SDL  2.0
SDL_emscriptenaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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, void *handle, 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  int result;
161 
162  while ((!valid_format) && (test_format)) {
163  switch (test_format) {
164  case AUDIO_F32: /* web audio only supports floats */
165  this->spec.format = test_format;
166 
167  valid_format = SDL_TRUE;
168  break;
169  }
170  test_format = SDL_NextAudioFormat();
171  }
172 
173  if (!valid_format) {
174  /* Didn't find a compatible format :( */
175  return SDL_SetError("No compatible audio format!");
176  }
177 
178  /* Initialize all variables that we clean on shutdown */
179  this->hidden = (struct SDL_PrivateAudioData *)
180  SDL_malloc((sizeof *this->hidden));
181  if (this->hidden == NULL) {
182  return SDL_OutOfMemory();
183  }
184  SDL_memset(this->hidden, 0, (sizeof *this->hidden));
185 
186  /* based on parts of library_sdl.js */
187 
188  /* create context (TODO: this puts stuff in the global namespace...)*/
189  result = EM_ASM_INT_V({
190  if(typeof(SDL2) === 'undefined')
191  SDL2 = {};
192 
193  if(typeof(SDL2.audio) === 'undefined')
194  SDL2.audio = {};
195 
196  if (!SDL2.audioContext) {
197  if (typeof(AudioContext) !== 'undefined') {
198  SDL2.audioContext = new AudioContext();
199  } else if (typeof(webkitAudioContext) !== 'undefined') {
200  SDL2.audioContext = new webkitAudioContext();
201  } else {
202  return -1;
203  }
204  }
205  return 0;
206  });
207  if (result < 0) {
208  return SDL_SetError("Web Audio API is not available!");
209  }
210 
211  /* limit to native freq */
212  int sampleRate = EM_ASM_INT_V({
213  return SDL2.audioContext['sampleRate'];
214  });
215 
216  if(this->spec.freq != sampleRate) {
217  for (i = this->spec.samples; i > 0; i--) {
218  f = (float)i / (float)sampleRate * (float)this->spec.freq;
219  if (SDL_floor(f) == f) {
220  this->hidden->conv_in_len = SDL_floor(f);
221  break;
222  }
223  }
224 
225  this->spec.freq = sampleRate;
226  }
227 
229 
230  /* setup a ScriptProcessorNode */
231  EM_ASM_ARGS({
232  SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
233  SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
234  SDL2.audio.currentOutputBuffer = e['outputBuffer'];
235  Runtime.dynCall('vi', $2, [$3]);
236  };
237  SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
238  }, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
239  return 0;
240 }
241 
242 static int
243 Emscripten_Init(SDL_AudioDriverImpl * impl)
244 {
245  /* Set the function pointers */
246  impl->OpenDevice = Emscripten_OpenDevice;
247  impl->CloseDevice = Emscripten_CloseDevice;
248 
249  /* only one output */
250  impl->OnlyHasDefaultOutputDevice = 1;
251 
252  /* no threads here */
253  impl->SkipMixerLock = 1;
254  impl->ProvidesOwnCallbackThread = 1;
255 
256  /* check availability */
257  int available = EM_ASM_INT_V({
258  if (typeof(AudioContext) !== 'undefined') {
259  return 1;
260  } else if (typeof(webkitAudioContext) !== 'undefined') {
261  return 1;
262  }
263  return 0;
264  });
265 
266  if (!available) {
267  SDL_SetError("No audio context available");
268  }
269 
270  return available;
271 }
272 
274  "emscripten", "SDL emscripten audio driver", Emscripten_Init, 0
275 };
276 
277 #endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
278 
279 /* vi: set ts=4 sw=4 expandtab: */
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1398
GLuint64EXT * result
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 Uint32 * e
Uint16 samples
Definition: SDL_audio.h:174
GLfloat f
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define SDL_floor
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1410
SDL_bool
Definition: SDL_stdinc.h:126
SDL_AudioSpec spec
Definition: loopwave.c:35
#define SDL_memcpy
#define SDL_ConvertAudio
Uint8 channels
Definition: SDL_audio.h:172
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:139
void SDL_free(void *mem)
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
const GLubyte * c
int paused
Definition: testoverlay2.c:149
#define SDL_memmove
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1419
SDL_AudioCallback callback
Definition: SDL_audio.h:177
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLenum GLsizei const GLuint GLboolean enabled
Uint32 size
Definition: SDL_audio.h:176
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:42
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:72
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_SetError
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
SDL_AudioFormat format
Definition: SDL_audio.h:171
void * userdata
Definition: SDL_audio.h:178
#define SDL_malloc
#define AUDIO_F32
Definition: SDL_audio.h:114
AudioBootStrap EmscriptenAudio_bootstrap
#define SDL_memset
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:42