From f07a1a5ad53f338b0a5a661a56657df1642a32c3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 5 Jan 2017 21:31:02 -0500 Subject: [PATCH] emscriptenaudio: Reworked to use SDL_AudioStream. --- src/audio/SDL_audio.c | 29 +-- src/audio/emscripten/SDL_emscriptenaudio.c | 202 +++++++-------------- src/audio/emscripten/SDL_emscriptenaudio.h | 7 +- 3 files changed, 83 insertions(+), 155 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 8f71b6eaf..94ee3816e 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1243,6 +1243,21 @@ open_audio_device(const char *devname, int iscapture, device->spec.userdata = device; } + /* !!! FIXME: rename this from fake_stream */ + /* Allocate a scratch audio buffer */ + device->fake_stream_len = build_stream ? device->callbackspec.size : 0; + if (device->spec.size > device->fake_stream_len) { + device->fake_stream_len = device->spec.size; + } + SDL_assert(device->fake_stream_len > 0); + + device->fake_stream = (Uint8 *) SDL_malloc(device->fake_stream_len); + if (device->fake_stream == NULL) { + close_audio_device(device); + SDL_OutOfMemory(); + return 0; + } + open_devices[id] = device; /* add it to our list of open devices. */ /* Start the audio thread if necessary */ @@ -1253,20 +1268,6 @@ open_audio_device(const char *devname, int iscapture, const size_t stacksize = is_internal_thread ? 64 * 1024 : 0; char threadname[64]; - /* Allocate a fake audio buffer; only used by our internal threads. */ - device->fake_stream_len = build_stream ? device->callbackspec.size : 0; - if (device->spec.size > device->fake_stream_len) { - device->fake_stream_len = device->spec.size; - } - SDL_assert(device->fake_stream_len > 0); - - device->fake_stream = (Uint8 *) SDL_malloc(device->fake_stream_len); - if (device->fake_stream == NULL) { - close_audio_device(device); - SDL_OutOfMemory(); - return 0; - } - SDL_snprintf(threadname, sizeof (threadname), "SDLAudioDev%d", (int) device->id); device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device); diff --git a/src/audio/emscripten/SDL_emscriptenaudio.c b/src/audio/emscripten/SDL_emscriptenaudio.c index d3f75a5a4..771ccf64f 100644 --- a/src/audio/emscripten/SDL_emscriptenaudio.c +++ b/src/audio/emscripten/SDL_emscriptenaudio.c @@ -26,175 +26,120 @@ #include "SDL_log.h" #include "../SDL_audio_c.h" #include "SDL_emscriptenaudio.h" +#include "SDL_assert.h" #include -static int -copyData(_THIS) +static void +FeedAudioDevice(_THIS, const void *buf, const int buflen) { - int byte_len; + 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!'; + } - 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; + for (var j = 0; j < $1; ++j) { + channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; /* !!! FIXME: why are these shifts here? */ + } } - 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; - - return byte_len; + }, 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); + } + + 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. */ + } } - - /* Send it to the app. */ - (*this->spec.callback) (this->spec.userdata, buf, buflen); } - 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); diff --git a/src/audio/emscripten/SDL_emscriptenaudio.h b/src/audio/emscripten/SDL_emscriptenaudio.h index 603f16308..c059c6753 100644 --- a/src/audio/emscripten/SDL_emscriptenaudio.h +++ b/src/audio/emscripten/SDL_emscriptenaudio.h @@ -30,12 +30,7 @@ struct SDL_PrivateAudioData { - Uint8 *mixbuf; - Uint32 mixlen; - - Uint32 conv_in_len; - - Uint32 write_off, read_off; + int unused; }; #endif /* _SDL_emscriptenaudio_h */