Mac and Linux SDL2 binary snapshots
Edward Rudd
2018-08-19 561f0d614098a95527367cc3f911e476f35643d6
source/src/audio/emscripten/SDL_emscriptenaudio.c
@@ -1,6 +1,6 @@
/*
  Simple DirectMedia Layer
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  Copyright (C) 1997-2018 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
@@ -26,173 +26,119 @@
#include "SDL_log.h"
#include "../SDL_audio_c.h"
#include "SDL_emscriptenaudio.h"
#include "SDL_assert.h"
#include <emscripten/emscripten.h>
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->callbackspec.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->callbackspec.userdata, this->work_buffer, stream_len);
    } else {  /* streaming/converting */
        int got;
        while (SDL_AudioStreamAvailable(this->stream) < ((int) this->spec.size)) {
            callback(this->callbackspec.userdata, this->work_buffer, stream_len);
            if (SDL_AudioStreamPut(this->stream, this->work_buffer, stream_len) == -1) {
                SDL_AudioStreamClear(this->stream);
                SDL_AtomicSet(&this->enabled, 0);
                break;
            }
            /* 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->work_buffer, this->spec.size);
        SDL_assert((got < 0) || (got == this->spec.size));
        if (got != this->spec.size) {
            SDL_memset(this->work_buffer, 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->work_buffer, this->spec.size);
}
static void
HandleCaptureProcess(_THIS)
{
    Uint8 *buf;
    int buflen;
    SDL_AudioCallback callback = this->callbackspec.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->work_buffer, (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->callbackspec.userdata, this->work_buffer, stream_len);
    } else {  /* streaming/converting */
        if (SDL_AudioStreamPut(this->stream, this->work_buffer, this->spec.size) == -1) {
            SDL_AtomicSet(&this->enabled, 0);
        }
        while (SDL_AudioStreamAvailable(this->stream) >= stream_len) {
            const int got = SDL_AudioStreamGet(this->stream, this->work_buffer, stream_len);
            SDL_assert((got < 0) || (got == stream_len));
            if (got != stream_len) {
                SDL_memset(this->work_buffer, this->callbackspec.silence, stream_len);
            }
            callback(this->callbackspec.userdata, this->work_buffer, stream_len);  /* Send it to the app. */
        }
    }
    /* Send it to the app. */
    (*this->spec.callback) (this->spec.userdata, buf, buflen);
}
static void
@@ -236,8 +182,9 @@
        }
    }, 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 +192,6 @@
{
    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 +238,17 @@
    }
    /* 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);
@@ -395,6 +328,9 @@
static int
EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl * impl)
{
    int available;
    int capture_available;
    /* Set the function pointers */
    impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
    impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
@@ -406,7 +342,7 @@
    impl->ProvidesOwnCallbackThread = 1;
    /* check availability */
    const int available = EM_ASM_INT_V({
    available = EM_ASM_INT_V({
        if (typeof(AudioContext) !== 'undefined') {
            return 1;
        } else if (typeof(webkitAudioContext) !== 'undefined') {
@@ -419,7 +355,7 @@
        SDL_SetError("No audio context available");
    }
    const int capture_available = available && EM_ASM_INT_V({
    capture_available = available && EM_ASM_INT_V({
        if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
            return 1;
        } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {