summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c')
-rw-r--r--3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c b/3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c
new file mode 100644
index 00000000000..8378233ca22
--- /dev/null
+++ b/3rdparty/SDL2/src/audio/emscripten/SDL_emscriptenaudio.c
@@ -0,0 +1,279 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../../SDL_internal.h"
+
+#if SDL_AUDIO_DRIVER_EMSCRIPTEN
+
+#include "SDL_audio.h"
+#include "SDL_log.h"
+#include "../SDL_audio_c.h"
+#include "SDL_emscriptenaudio.h"
+
+#include <emscripten/emscripten.h>
+
+static int
+copyData(_THIS)
+{
+ 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;
+
+ return byte_len;
+}
+
+static void
+HandleAudioProcess(_THIS)
+{
+ Uint8 *buf = NULL;
+ int byte_len = 0;
+ int bytes = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
+ int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
+
+ /* Only do soemthing if audio is enabled */
+ if (!this->enabled)
+ return;
+
+ if (this->paused)
+ return;
+
+ if (this->convert.needed) {
+ 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);
+ }
+
+ /* 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);
+ }
+ (*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] = getValue($0 + (j*numChannels + c)*4, 'float');
+ }
+ }
+ }, buf, byte_len / bytes / this->spec.channels);
+ }
+}
+
+static void
+Emscripten_CloseDevice(_THIS)
+{
+ if (this->hidden != NULL) {
+ if (this->hidden->mixbuf != NULL) {
+ /* Clean up the audio buffer */
+ SDL_free(this->hidden->mixbuf);
+ this->hidden->mixbuf = NULL;
+ }
+
+ SDL_free(this->hidden);
+ this->hidden = NULL;
+ }
+}
+
+static int
+Emscripten_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
+{
+ SDL_bool valid_format = SDL_FALSE;
+ SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
+ int i;
+ float f;
+ int result;
+
+ while ((!valid_format) && (test_format)) {
+ switch (test_format) {
+ case AUDIO_F32: /* web audio only supports floats */
+ this->spec.format = test_format;
+
+ valid_format = SDL_TRUE;
+ break;
+ }
+ test_format = SDL_NextAudioFormat();
+ }
+
+ if (!valid_format) {
+ /* Didn't find a compatible format :( */
+ return SDL_SetError("No compatible audio format!");
+ }
+
+ /* Initialize all variables that we clean on shutdown */
+ this->hidden = (struct SDL_PrivateAudioData *)
+ SDL_malloc((sizeof *this->hidden));
+ if (this->hidden == NULL) {
+ return SDL_OutOfMemory();
+ }
+ SDL_memset(this->hidden, 0, (sizeof *this->hidden));
+
+ /* based on parts of library_sdl.js */
+
+ /* create context (TODO: this puts stuff in the global namespace...)*/
+ result = EM_ASM_INT_V({
+ if(typeof(SDL2) === 'undefined')
+ SDL2 = {};
+
+ if(typeof(SDL2.audio) === 'undefined')
+ SDL2.audio = {};
+
+ if (!SDL2.audioContext) {
+ if (typeof(AudioContext) !== 'undefined') {
+ SDL2.audioContext = new AudioContext();
+ } else if (typeof(webkitAudioContext) !== 'undefined') {
+ SDL2.audioContext = new webkitAudioContext();
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+ });
+ if (result < 0) {
+ return SDL_SetError("Web Audio API is not available!");
+ }
+
+ /* limit to native freq */
+ 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;
+ }
+
+ SDL_CalculateAudioSpec(&this->spec);
+
+ /* setup a ScriptProcessorNode */
+ EM_ASM_ARGS({
+ SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
+ SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
+ SDL2.audio.currentOutputBuffer = e['outputBuffer'];
+ Runtime.dynCall('vi', $2, [$3]);
+ };
+ SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
+ }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
+ return 0;
+}
+
+static int
+Emscripten_Init(SDL_AudioDriverImpl * impl)
+{
+ /* Set the function pointers */
+ impl->OpenDevice = Emscripten_OpenDevice;
+ impl->CloseDevice = Emscripten_CloseDevice;
+
+ /* only one output */
+ impl->OnlyHasDefaultOutputDevice = 1;
+
+ /* no threads here */
+ impl->SkipMixerLock = 1;
+ impl->ProvidesOwnCallbackThread = 1;
+
+ /* check availability */
+ int available = EM_ASM_INT_V({
+ if (typeof(AudioContext) !== 'undefined') {
+ return 1;
+ } else if (typeof(webkitAudioContext) !== 'undefined') {
+ return 1;
+ }
+ return 0;
+ });
+
+ if (!available) {
+ SDL_SetError("No audio context available");
+ }
+
+ return available;
+}
+
+AudioBootStrap EmscriptenAudio_bootstrap = {
+ "emscripten", "SDL emscripten audio driver", Emscripten_Init, 0
+};
+
+#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
+
+/* vi: set ts=4 sw=4 expandtab: */