src/audio/emscripten/SDL_emscriptenaudio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Thu, 25 Dec 2014 22:03:07 +0100
changeset 9291 02b47b8164da
parent 9278 8900afb78a19
child 9344 83f4ab8fb0b8
permissions -rw-r--r--
Fixed compile warnings about unused variables.

Found by buildbot.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_AUDIO_DRIVER_EMSCRIPTEN
    24 
    25 #include "SDL_audio.h"
    26 #include "SDL_log.h"
    27 #include "../SDL_audio_c.h"
    28 #include "SDL_emscriptenaudio.h"
    29 
    30 #include <emscripten/emscripten.h>
    31 
    32 static int
    33 copyData(_THIS)
    34 {
    35     int byte_len;
    36 
    37     if (this->hidden->write_off + this->convert.len_cvt > this->hidden->mixlen) {
    38         if (this->hidden->write_off > this->hidden->read_off) {
    39             SDL_memmove(this->hidden->mixbuf,
    40                         this->hidden->mixbuf + this->hidden->read_off,
    41                         this->hidden->mixlen - this->hidden->read_off);
    42             this->hidden->write_off = this->hidden->write_off - this->hidden->read_off;
    43         } else {
    44             this->hidden->write_off = 0;
    45         }
    46         this->hidden->read_off = 0;
    47     }
    48 
    49     SDL_memcpy(this->hidden->mixbuf + this->hidden->write_off,
    50                this->convert.buf,
    51                this->convert.len_cvt);
    52     this->hidden->write_off += this->convert.len_cvt;
    53     byte_len = this->hidden->write_off - this->hidden->read_off;
    54 
    55     return byte_len;
    56 }
    57 
    58 static void
    59 HandleAudioProcess(_THIS)
    60 {
    61     Uint8 *buf = NULL;
    62     int byte_len = 0;
    63     int bytes = SDL_AUDIO_BITSIZE(this->spec.format) / 8;
    64     int bytes_in = SDL_AUDIO_BITSIZE(this->convert.src_format) / 8;
    65 
    66     /* Only do soemthing if audio is enabled */
    67     if (!this->enabled)
    68         return;
    69 
    70     if (this->paused)
    71         return;
    72 
    73     if (this->convert.needed) {
    74         if (this->hidden->conv_in_len != 0) {
    75             this->convert.len = this->hidden->conv_in_len * bytes_in * this->spec.channels;
    76         }
    77 
    78         (*this->spec.callback) (this->spec.userdata,
    79                                  this->convert.buf,
    80                                  this->convert.len);
    81         SDL_ConvertAudio(&this->convert);
    82         buf = this->convert.buf;
    83         byte_len = this->convert.len_cvt;
    84 
    85         /* size mismatch*/
    86         if (byte_len != this->spec.size) {
    87             if (!this->hidden->mixbuf) {
    88                 this->hidden->mixlen = this->spec.size > byte_len ? this->spec.size * 2 : byte_len * 2;
    89                 this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
    90             }
    91 
    92             /* copy existing data */
    93             byte_len = copyData(this);
    94 
    95             /* read more data*/
    96             while (byte_len < this->spec.size) {
    97                 (*this->spec.callback) (this->spec.userdata,
    98                                          this->convert.buf,
    99                                          this->convert.len);
   100                 SDL_ConvertAudio(&this->convert);
   101                 byte_len = copyData(this);
   102             }
   103 
   104             byte_len = this->spec.size;
   105             buf = this->hidden->mixbuf + this->hidden->read_off;
   106             this->hidden->read_off += byte_len;
   107         }
   108 
   109     } else {
   110         if (!this->hidden->mixbuf) {
   111             this->hidden->mixlen = this->spec.size;
   112             this->hidden->mixbuf = SDL_malloc(this->hidden->mixlen);
   113         }
   114         (*this->spec.callback) (this->spec.userdata,
   115                                  this->hidden->mixbuf,
   116                                  this->hidden->mixlen);
   117         buf = this->hidden->mixbuf;
   118         byte_len = this->hidden->mixlen;
   119     }
   120 
   121     if (buf) {
   122         EM_ASM_ARGS({
   123             var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels'];
   124             for (var c = 0; c < numChannels; ++c) {
   125                 var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c);
   126                 if (channelData.length != $1) {
   127                     throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
   128                 }
   129 
   130                 for (var j = 0; j < $1; ++j) {
   131                     channelData[j] = getValue($0 + (j*numChannels + c)*4, 'float');
   132                 }
   133             }
   134         }, buf, byte_len / bytes / this->spec.channels);
   135     }
   136 }
   137 
   138 static void
   139 Emscripten_CloseDevice(_THIS)
   140 {
   141     if (this->hidden != NULL) {
   142         if (this->hidden->mixbuf != NULL) {
   143             /* Clean up the audio buffer */
   144             SDL_free(this->hidden->mixbuf);
   145             this->hidden->mixbuf = NULL;
   146         }
   147 
   148         SDL_free(this->hidden);
   149         this->hidden = NULL;
   150     }
   151 }
   152 
   153 static int
   154 Emscripten_OpenDevice(_THIS, const char *devname, int iscapture)
   155 {
   156     SDL_bool valid_format = SDL_FALSE;
   157     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   158     int i;
   159     float f;
   160 
   161     while ((!valid_format) && (test_format)) {
   162         switch (test_format) {
   163         case AUDIO_F32: /* web audio only supports floats */
   164             this->spec.format = test_format;
   165 
   166             valid_format = SDL_TRUE;
   167             break;
   168         }
   169         test_format = SDL_NextAudioFormat();
   170     }
   171 
   172     if (!valid_format) {
   173         /* Didn't find a compatible format :( */
   174         return SDL_SetError("No compatible audio format!");
   175     }
   176 
   177     /* Initialize all variables that we clean on shutdown */
   178     this->hidden = (struct SDL_PrivateAudioData *)
   179         SDL_malloc((sizeof *this->hidden));
   180     if (this->hidden == NULL) {
   181         return SDL_OutOfMemory();
   182     }
   183     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   184 
   185     /* based on parts of library_sdl.js */
   186 
   187     /* create context (TODO: this puts stuff in the global namespace...)*/
   188     EM_ASM({
   189         if(typeof(SDL2) === 'undefined')
   190             SDL2 = {};
   191 
   192         if(typeof(SDL2.audio) === 'undefined')
   193             SDL2.audio = {};
   194 
   195         if (!SDL2.audioContext) {
   196             if (typeof(AudioContext) !== 'undefined') {
   197                 SDL2.audioContext = new AudioContext();
   198             } else if (typeof(webkitAudioContext) !== 'undefined') {
   199                 SDL2.audioContext = new webkitAudioContext();
   200             } else {
   201                 throw 'Web Audio API is not available!';
   202             }
   203         }
   204     });
   205 
   206     /* limit to native freq */
   207     int sampleRate = EM_ASM_INT_V({
   208         return SDL2.audioContext['sampleRate'];
   209     });
   210 
   211     if(this->spec.freq != sampleRate) {
   212         for (i = this->spec.samples; i > 0; i--) {
   213             f = (float)i / (float)sampleRate * (float)this->spec.freq;
   214             if (SDL_floor(f) == f) {
   215                 this->hidden->conv_in_len = SDL_floor(f);
   216                 break;
   217             }
   218         }
   219 
   220         this->spec.freq = sampleRate;
   221     }
   222 
   223     SDL_CalculateAudioSpec(&this->spec);
   224 
   225     /* setup a ScriptProcessorNode */
   226     EM_ASM_ARGS({
   227         SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
   228         SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
   229             SDL2.audio.currentOutputBuffer = e['outputBuffer'];
   230             Runtime.dynCall('vi', $2, [$3]);
   231         };
   232         SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
   233     }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
   234     return 0;
   235 }
   236 
   237 static int
   238 Emscripten_Init(SDL_AudioDriverImpl * impl)
   239 {
   240     /* Set the function pointers */
   241     impl->OpenDevice = Emscripten_OpenDevice;
   242     impl->CloseDevice = Emscripten_CloseDevice;
   243 
   244     /* only one output */
   245     impl->OnlyHasDefaultOutputDevice = 1;
   246 
   247     /* no threads here */
   248     impl->SkipMixerLock = 1;
   249     impl->ProvidesOwnCallbackThread = 1;
   250 
   251     /* check availability */
   252     int available = EM_ASM_INT_V({
   253         if (typeof(AudioContext) !== 'undefined') {
   254             return 1;
   255         } else if (typeof(webkitAudioContext) !== 'undefined') {
   256             return 1;
   257         }
   258         return 0;
   259     });
   260 
   261     return available;
   262 }
   263 
   264 AudioBootStrap EmscriptenAudio_bootstrap = {
   265     "emscripten", "SDL emscripten audio driver", Emscripten_Init, 0
   266 };
   267 
   268 #endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
   269 
   270 /* vi: set ts=4 sw=4 expandtab: */