|
@@ -26,175 +26,120 @@
|
|
|
#include "SDL_log.h"
|
|
|
#include "../SDL_audio_c.h"
|
|
|
#include "SDL_emscriptenaudio.h"
|
|
|
+#include "SDL_assert.h"
|
|
|
|
|
|
#include <emscripten/emscripten.h>
|
|
|
|
|
|
-static int
|
|
|
-copyData(_THIS)
|
|
|
+static void
|
|
|
+FeedAudioDevice(_THIS, const void *buf, const int buflen)
|
|
|
{
|
|
|
- int byte_len;
|
|
|
-
|
|
|
- if (this->hidden->write_off + this->convert.len_cvt > this->hidden->mixlen) {
|
|
|
- if (this->hidden->write_off > this->hidden->read_off) {
|
|
|
- SDL_memmove(this->hidden->mixbuf,
|
|
|
- this->hidden->mixbuf + this->hidden->read_off,
|
|
|
- this->hidden->mixlen - this->hidden->read_off);
|
|
|
- this->hidden->write_off = this->hidden->write_off - this->hidden->read_off;
|
|
|
- } else {
|
|
|
- this->hidden->write_off = 0;
|
|
|
- }
|
|
|
- this->hidden->read_off = 0;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_memcpy(this->hidden->mixbuf + this->hidden->write_off,
|
|
|
- this->convert.buf,
|
|
|
- this->convert.len_cvt);
|
|
|
- this->hidden->write_off += this->convert.len_cvt;
|
|
|
- byte_len = this->hidden->write_off - this->hidden->read_off;
|
|
|
+ const int framelen = (SDL_AUDIO_BITSIZE(this->spec.format) / 8) * this->spec.channels;
|
|
|
+ EM_ASM_ARGS({
|
|
|
+ var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels'];
|
|
|
+ for (var c = 0; c < numChannels; ++c) {
|
|
|
+ var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c);
|
|
|
+ if (channelData.length != $1) {
|
|
|
+ throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
|
|
+ }
|
|
|
|
|
|
- return byte_len;
|
|
|
+ for (var j = 0; j < $1; ++j) {
|
|
|
+ channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; /* !!! FIXME: why are these shifts here? */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, buf, buflen / framelen);
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
HandleAudioProcess(_THIS)
|
|
|
{
|
|
|
- Uint8 *buf = NULL;
|
|
|
- int byte_len = 0;
|
|
|
- int bytes = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
|
|
|
+ SDL_AudioCallback callback = this->spec.callback;
|
|
|
+ const int stream_len = this->callbackspec.size;
|
|
|
|
|
|
/* Only do something if audio is enabled */
|
|
|
if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
|
|
|
+ if (this->stream) {
|
|
|
+ SDL_AudioStreamClear(this->stream);
|
|
|
+ }
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (this->convert.needed) {
|
|
|
- const int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
|
|
|
-
|
|
|
- if (this->hidden->conv_in_len != 0) {
|
|
|
- this->convert.len = this->hidden->conv_in_len * bytes_in * this->spec.channels;
|
|
|
- }
|
|
|
-
|
|
|
- (*this->spec.callback) (this->spec.userdata,
|
|
|
- this->convert.buf,
|
|
|
- this->convert.len);
|
|
|
- SDL_ConvertAudio(&this->convert);
|
|
|
- buf = this->convert.buf;
|
|
|
- byte_len = this->convert.len_cvt;
|
|
|
-
|
|
|
- /* size mismatch*/
|
|
|
- if (byte_len != this->spec.size) {
|
|
|
- if (!this->hidden->mixbuf) {
|
|
|
- this->hidden->mixlen = this->spec.size > byte_len ? this->spec.size * 2 : byte_len * 2;
|
|
|
- this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
|
|
|
+ if (this->stream == NULL) { /* no conversion necessary. */
|
|
|
+ SDL_assert(this->spec.size == stream_len);
|
|
|
+ callback(this->spec.userdata, this->fake_stream, stream_len);
|
|
|
+ } else { /* streaming/converting */
|
|
|
+ int got;
|
|
|
+ while (SDL_AudioStreamAvailable(this->stream) < ((int) this->spec.size)) {
|
|
|
+ callback(this->spec.userdata, this->fake_stream, stream_len);
|
|
|
+ if (SDL_AudioStreamPut(this->stream, this->fake_stream, stream_len) == -1) {
|
|
|
+ SDL_AudioStreamClear(this->stream);
|
|
|
+ SDL_AtomicSet(&this->enabled, 0);
|
|
|
}
|
|
|
-
|
|
|
- /* copy existing data */
|
|
|
- byte_len = copyData(this);
|
|
|
-
|
|
|
- /* read more data*/
|
|
|
- while (byte_len < this->spec.size) {
|
|
|
- (*this->spec.callback) (this->spec.userdata,
|
|
|
- this->convert.buf,
|
|
|
- this->convert.len);
|
|
|
- SDL_ConvertAudio(&this->convert);
|
|
|
- byte_len = copyData(this);
|
|
|
- }
|
|
|
-
|
|
|
- byte_len = this->spec.size;
|
|
|
- buf = this->hidden->mixbuf + this->hidden->read_off;
|
|
|
- this->hidden->read_off += byte_len;
|
|
|
}
|
|
|
|
|
|
- } else {
|
|
|
- if (!this->hidden->mixbuf) {
|
|
|
- this->hidden->mixlen = this->spec.size;
|
|
|
- this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
|
|
|
+ got = SDL_AudioStreamGet(this->stream, this->spec.size, this->fake_stream, this->spec.size);
|
|
|
+ SDL_assert((got < 0) || (got == this->spec.size));
|
|
|
+ if (got != this->spec.size) {
|
|
|
+ SDL_memset(this->fake_stream, this->spec.silence, this->spec.size);
|
|
|
}
|
|
|
- (*this->spec.callback) (this->spec.userdata,
|
|
|
- this->hidden->mixbuf,
|
|
|
- this->hidden->mixlen);
|
|
|
- buf = this->hidden->mixbuf;
|
|
|
- byte_len = this->hidden->mixlen;
|
|
|
}
|
|
|
|
|
|
- if (buf) {
|
|
|
- EM_ASM_ARGS({
|
|
|
- var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels'];
|
|
|
- for (var c = 0; c < numChannels; ++c) {
|
|
|
- var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c);
|
|
|
- if (channelData.length != $1) {
|
|
|
- throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
|
|
- }
|
|
|
-
|
|
|
- for (var j = 0; j < $1; ++j) {
|
|
|
- channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2];
|
|
|
- }
|
|
|
- }
|
|
|
- }, buf, byte_len / bytes / this->spec.channels);
|
|
|
- }
|
|
|
+ FeedAudioDevice(this, this->fake_stream, this->spec.size);
|
|
|
}
|
|
|
|
|
|
static void
|
|
|
HandleCaptureProcess(_THIS)
|
|
|
{
|
|
|
- Uint8 *buf;
|
|
|
- int buflen;
|
|
|
+ SDL_AudioCallback callback = this->spec.callback;
|
|
|
+ const int stream_len = this->callbackspec.size;
|
|
|
|
|
|
/* Only do something if audio is enabled */
|
|
|
if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
|
|
|
+ SDL_AudioStreamClear(this->stream);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (this->convert.needed) {
|
|
|
- buf = this->convert.buf;
|
|
|
- buflen = this->convert.len_cvt;
|
|
|
- } else {
|
|
|
- if (!this->hidden->mixbuf) {
|
|
|
- this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->spec.size);
|
|
|
- if (!this->hidden->mixbuf) {
|
|
|
- return; /* oh well. */
|
|
|
- }
|
|
|
- }
|
|
|
- buf = this->hidden->mixbuf;
|
|
|
- buflen = this->spec.size;
|
|
|
- }
|
|
|
-
|
|
|
EM_ASM_ARGS({
|
|
|
var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels;
|
|
|
- if (numChannels == 1) { /* fastpath this a little for the common (mono) case. */
|
|
|
- var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(0);
|
|
|
+ for (var c = 0; c < numChannels; ++c) {
|
|
|
+ var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c);
|
|
|
if (channelData.length != $1) {
|
|
|
throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
|
|
}
|
|
|
- for (var j = 0; j < $1; ++j) {
|
|
|
- setValue($0 + (j * 4), channelData[j], 'float');
|
|
|
- }
|
|
|
- } else {
|
|
|
- for (var c = 0; c < numChannels; ++c) {
|
|
|
- var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c);
|
|
|
- if (channelData.length != $1) {
|
|
|
- throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
|
|
- }
|
|
|
|
|
|
+ if (numChannels == 1) { /* fastpath this a little for the common (mono) case. */
|
|
|
+ for (var j = 0; j < $1; ++j) {
|
|
|
+ setValue($0 + (j * 4), channelData[j], 'float');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
for (var j = 0; j < $1; ++j) {
|
|
|
setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }, buf, (this->spec.size / sizeof (float)) / this->spec.channels);
|
|
|
+ }, this->fake_stream, (this->spec.size / sizeof (float)) / this->spec.channels);
|
|
|
|
|
|
/* okay, we've got an interleaved float32 array in C now. */
|
|
|
|
|
|
- if (this->convert.needed) {
|
|
|
- SDL_ConvertAudio(&this->convert);
|
|
|
- }
|
|
|
+ if (this->stream == NULL) { /* no conversion necessary. */
|
|
|
+ SDL_assert(this->spec.size == stream_len);
|
|
|
+ callback(this->spec.userdata, this->fake_stream, stream_len);
|
|
|
+ } else { /* streaming/converting */
|
|
|
+ if (SDL_AudioStreamPut(this->stream, this->fake_stream, this->spec.size) == -1) {
|
|
|
+ SDL_AtomicSet(&this->enabled, 0);
|
|
|
+ }
|
|
|
|
|
|
- /* Send it to the app. */
|
|
|
- (*this->spec.callback) (this->spec.userdata, buf, buflen);
|
|
|
+ while (SDL_AudioStreamAvailable(this->stream) >= stream_len) {
|
|
|
+ const int got = SDL_AudioStreamGet(this->stream, stream_len, this->fake_stream, stream_len);
|
|
|
+ SDL_assert((got < 0) || (got == stream_len));
|
|
|
+ if (got != stream_len) {
|
|
|
+ SDL_memset(this->fake_stream, this->callbackspec.silence, stream_len);
|
|
|
+ }
|
|
|
+ callback(this->spec.userdata, this->fake_stream, stream_len); /* Send it to the app. */
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
static void
|
|
|
EMSCRIPTENAUDIO_CloseDevice(_THIS)
|
|
|
{
|
|
@@ -236,8 +181,9 @@ EMSCRIPTENAUDIO_CloseDevice(_THIS)
|
|
|
}
|
|
|
}, this->iscapture);
|
|
|
|
|
|
- SDL_free(this->hidden->mixbuf);
|
|
|
+#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL2 namespace? --ryan. */
|
|
|
SDL_free(this->hidden);
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -245,8 +191,6 @@ EMSCRIPTENAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscaptu
|
|
|
{
|
|
|
SDL_bool valid_format = SDL_FALSE;
|
|
|
SDL_AudioFormat test_format;
|
|
|
- int i;
|
|
|
- float f;
|
|
|
int result;
|
|
|
|
|
|
/* based on parts of library_sdl.js */
|
|
@@ -293,29 +237,17 @@ EMSCRIPTENAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscaptu
|
|
|
}
|
|
|
|
|
|
/* Initialize all variables that we clean on shutdown */
|
|
|
+#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL2 namespace? --ryan. */
|
|
|
this->hidden = (struct SDL_PrivateAudioData *)
|
|
|
SDL_malloc((sizeof *this->hidden));
|
|
|
if (this->hidden == NULL) {
|
|
|
return SDL_OutOfMemory();
|
|
|
}
|
|
|
SDL_zerop(this->hidden);
|
|
|
+#endif
|
|
|
|
|
|
/* limit to native freq */
|
|
|
- const int sampleRate = EM_ASM_INT_V({
|
|
|
- return SDL2.audioContext.sampleRate;
|
|
|
- });
|
|
|
-
|
|
|
- if(this->spec.freq != sampleRate) {
|
|
|
- for (i = this->spec.samples; i > 0; i--) {
|
|
|
- f = (float)i / (float)sampleRate * (float)this->spec.freq;
|
|
|
- if (SDL_floor(f) == f) {
|
|
|
- this->hidden->conv_in_len = SDL_floor(f);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- this->spec.freq = sampleRate;
|
|
|
- }
|
|
|
+ this->spec.freq = EM_ASM_INT_V({ return SDL2.audioContext.sampleRate; });
|
|
|
|
|
|
SDL_CalculateAudioSpec(&this->spec);
|
|
|
|