emscriptenaudio: Reworked to use SDL_AudioStream.

Ryan C. Gordon 2017-01-05 21:31:02 -05:00
parent 3761b5f60b
commit f07a1a5ad5
3 changed files with 83 additions and 155 deletions

View File

@ -1243,6 +1243,21 @@ open_audio_device(const char *devname, int iscapture,
device->spec.userdata = device; 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. */ open_devices[id] = device; /* add it to our list of open devices. */
/* Start the audio thread if necessary */ /* 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; const size_t stacksize = is_internal_thread ? 64 * 1024 : 0;
char threadname[64]; 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); SDL_snprintf(threadname, sizeof (threadname), "SDLAudioDev%d", (int) device->id);
device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device); device->thread = SDL_CreateThreadInternal(iscapture ? SDL_CaptureAudio : SDL_RunAudio, threadname, stacksize, device);

View File

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

View File

@ -30,12 +30,7 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
Uint8 *mixbuf; int unused;
Uint32 mixlen;
Uint32 conv_in_len;
Uint32 write_off, read_off;
}; };
#endif /* _SDL_emscriptenaudio_h */ #endif /* _SDL_emscriptenaudio_h */