summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Justin Kerk <dopefishjustin@gmail.com>2016-02-04 04:35:51 +0000
committer Justin Kerk <dopefishjustin@gmail.com>2016-02-04 04:36:17 +0000
commit039a07bb4d3c32ac66a55637d334056aa5661b6f (patch)
treeebc97e98aae5c51ab03d20e6910df86e1d89f39b
parent26426914cc9ede54b7542674400a96eb4a0e90a8 (diff)
Substantial improvements to Web Audio sound backend for Emscripten port. [Grant Galitz]
-rw-r--r--scripts/src/main.lua2
-rw-r--r--src/osd/modules/sound/js_sound.cpp6
-rw-r--r--src/osd/modules/sound/js_sound.js213
3 files changed, 217 insertions, 4 deletions
diff --git a/scripts/src/main.lua b/scripts/src/main.lua
index 9623a9d5236..2397a489e00 100644
--- a/scripts/src/main.lua
+++ b/scripts/src/main.lua
@@ -88,7 +88,7 @@ end
targetextension ".bc"
if os.getenv("EMSCRIPTEN") then
postbuildcommands {
- os.getenv("EMSCRIPTEN") .. "/emcc -O3 -s DISABLE_EXCEPTION_CATCHING=2 -s USE_SDL=2 --memory-init-file 0 -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_MEMORY=268435456 -s EXCEPTION_CATCHING_WHITELIST='[\"__ZN15running_machine17start_all_devicesEv\"]' -s EXPORTED_FUNCTIONS=\"['_main', '_malloc', '__Z14js_get_machinev', '__Z9js_get_uiv', '__Z12js_get_soundv', '__ZN10ui_manager12set_show_fpsEb', '__ZNK10ui_manager8show_fpsEv', '__ZN13sound_manager4muteEbh', '_SDL_PauseAudio']\" $(TARGET) -o " .. _MAKE.esc(MAME_DIR) .. _OPTIONS["target"] .. _OPTIONS["subtarget"] .. ".js --post-js " .. _MAKE.esc(MAME_DIR) .. "src/osd/sdl/emscripten_post.js",
+ os.getenv("EMSCRIPTEN") .. "/emcc -O3 -s DISABLE_EXCEPTION_CATCHING=2 -s USE_SDL=2 --memory-init-file 0 -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_MEMORY=268435456 -s EXCEPTION_CATCHING_WHITELIST='[\"__ZN15running_machine17start_all_devicesEv\"]' -s EXPORTED_FUNCTIONS=\"['_main', '_malloc', '__Z14js_get_machinev', '__Z9js_get_uiv', '__Z12js_get_soundv', '__ZN10ui_manager12set_show_fpsEb', '__ZNK10ui_manager8show_fpsEv', '__ZN13sound_manager4muteEbh', '_SDL_PauseAudio']\" $(TARGET) -o " .. _MAKE.esc(MAME_DIR) .. _OPTIONS["target"] .. _OPTIONS["subtarget"] .. ".js --pre-js " .. _MAKE.esc(MAME_DIR) .. "src/osd/modules/sound/js_sound.js --post-js " .. _MAKE.esc(MAME_DIR) .. "src/osd/sdl/emscripten_post.js",
}
end
diff --git a/src/osd/modules/sound/js_sound.cpp b/src/osd/modules/sound/js_sound.cpp
index bd95c79dd2e..581de205d7b 100644
--- a/src/osd/modules/sound/js_sound.cpp
+++ b/src/osd/modules/sound/js_sound.cpp
@@ -6,7 +6,7 @@
Shim for native JavaScript sound interface implementations (Emscripten only).
-*******************************************************************c********/
+****************************************************************************/
#include "sound_module.h"
#include "modules/osdmodule.h"
@@ -34,14 +34,14 @@ public:
{
EM_ASM_ARGS({
// Forward audio stream update on to JS backend implementation.
- jsmess_update_audio_stream($0, $1);
+ jsmame_update_audio_stream($0, $1);
}, (unsigned int)buffer, samples_this_frame);
}
virtual void set_mastervolume(int attenuation)
{
EM_ASM_ARGS({
// Forward volume update on to JS backend implementation.
- jsmess_set_mastervolume($0);
+ jsmame_set_mastervolume($0);
}, attenuation);
}
diff --git a/src/osd/modules/sound/js_sound.js b/src/osd/modules/sound/js_sound.js
new file mode 100644
index 00000000000..393bcd66e93
--- /dev/null
+++ b/src/osd/modules/sound/js_sound.js
@@ -0,0 +1,213 @@
+// license:BSD-3-Clause
+// copyright-holders:Grant Galitz, Katelyn Gadd
+/***************************************************************************
+
+ JSMAME web audio backend v0.3
+
+ Original by katelyn gadd - kg at luminance dot org ; @antumbral on twitter
+ Substantial changes by taisel
+
+***************************************************************************/
+
+var jsmame_web_audio = (function () {
+
+var context = null;
+var gain_node = null;
+var eventNode = null;
+var sampleScale = 32766;
+var inputBuffer = new Float32Array(44100);
+var bufferSize = 44100;
+var start = 0;
+var rear = 0;
+var watchDogDateLast = null;
+var watchDogTimerEvent = null;
+
+function lazy_init () {
+ //Make
+ if (context) {
+ //Return if already created:
+ return;
+ }
+ if (typeof AudioContext != "undefined") {
+ //Standard context creation:
+ context = new AudioContext();
+ }
+ else if (typeof webkitAudioContext != "undefined") {
+ //Older webkit context creation:
+ context = new webkitAudioContext();
+ }
+ else {
+ //API not found!
+ return;
+ }
+ //Generate a volume control node:
+ gain_node = context.createGain();
+ //Set initial volume to 1:
+ gain_node.gain.value = 1.0;
+ //Connect volume node to output:
+ gain_node.connect(context.destination);
+ //Initialize the streaming event:
+ init_event();
+};
+
+function init_event() {
+ //Generate a streaming node point:
+ if (typeof context.createScriptProcessor == "function") {
+ //Current standard compliant way:
+ eventNode = context.createScriptProcessor(4096, 0, 2);
+ }
+ else {
+ //Deprecated way:
+ eventNode = context.createJavaScriptNode(4096, 0, 2);
+ }
+ //Make our tick function the audio callback function:
+ eventNode.onaudioprocess = tick;
+ //Connect stream to volume control node:
+ eventNode.connect(gain_node);
+ //WORKAROUND FOR FIREFOX BUG:
+ initializeWatchDogForFirefoxBug();
+};
+
+function initializeWatchDogForFirefoxBug() {
+ //TODO: decide if we want to user agent sniff firefox here,
+ //since Google Chrome doesn't need this:
+ watchDogDateLast = (new Date()).getTime();
+ if (watchDogTimerEvent === null) {
+ watchDogTimerEvent = setInterval(function () {
+ var timeDiff = (new Date()).getTime() - watchDogDateLast;
+ if (timeDiff > 500) {
+ disconnect_old_event();
+ init_event();
+ }
+ }, 500);
+ }
+};
+
+function disconnect_old_event() {
+ //Disconnect from audio graph:
+ eventNode.disconnect();
+ //IIRC there was a firefox bug that did not GC this event when nulling the node itself:
+ eventNode.onaudioprocess = null;
+ //Null the glitched/unused node:
+ eventNode = null;
+};
+
+function set_mastervolume (
+ // even though it's 'attenuation' the value is negative, so...
+ attenuation_in_decibels
+) {
+ lazy_init();
+ if (!context) return;
+
+ // http://stackoverflow.com/questions/22604500/web-audio-api-working-with-decibels
+ // seemingly incorrect/broken. figures. welcome to Web Audio
+ // var gain_web_audio = 1.0 - Math.pow(10, 10 / attenuation_in_decibels);
+
+ // HACK: Max attenuation in JSMESS appears to be 32.
+ // Hit ' then left/right arrow to test.
+ // FIXME: This is linear instead of log10 scale.
+ var gain_web_audio = 1.0 + (+attenuation_in_decibels / +32);
+ if (gain_web_audio < +0)
+ gain_web_audio = +0;
+ else if (gain_web_audio > +1)
+ gain_web_audio = +1;
+
+ gain_node.gain.value = gain_web_audio;
+};
+
+function update_audio_stream (
+ pBuffer, // pointer into emscripten heap. int16 samples
+ samples_this_frame // int. number of samples at pBuffer address.
+) {
+ lazy_init();
+ if (!context) return;
+
+ for (
+ var i = 0,
+ l = samples_this_frame | 0;
+ i < l;
+ i++
+ ) {
+ var offset =
+ // divide by sizeof(INT16) since pBuffer is offset
+ // in bytes
+ ((pBuffer / 2) | 0) +
+ ((i * 2) | 0);
+
+ var left_sample = HEAP16[offset];
+ var right_sample = HEAP16[(offset + 1) | 0];
+
+ // normalize from signed int16 to signed float
+ var left_sample_float = left_sample / sampleScale;
+ var right_sample_float = right_sample / sampleScale;
+
+ inputBuffer[rear++] = left_sample_float;
+ inputBuffer[rear++] = right_sample_float;
+ if (rear == bufferSize) {
+ rear = 0;
+ }
+ if (start == rear) {
+ start += 2;
+ if (start == bufferSize) {
+ start = 0;
+ }
+ }
+ }
+};
+function tick (event) {
+ //Find all output channels:
+ for (var bufferCount = 0, buffers = []; bufferCount < 2; ++bufferCount) {
+ buffers[bufferCount] = event.outputBuffer.getChannelData(bufferCount);
+ }
+ //Copy samples from the input buffer to the Web Audio API:
+ for (var index = 0; index < 4096 && start != rear; ++index) {
+ buffers[0][index] = inputBuffer[start++];
+ buffers[1][index] = inputBuffer[start++];
+ if (start == bufferSize) {
+ start = 0;
+ }
+ }
+ //Pad with silence if we're underrunning:
+ while (index < 4096) {
+ buffers[0][index] = 0;
+ buffers[1][index++] = 0;
+ }
+ //Deep inside the bowels of vendors bugs,
+ //we're using watchdog for a firefox bug,
+ //where the user agent decides to stop firing events
+ //if the user agent lags out due to system load.
+ //Don't even ask....
+ watchDogDateLast = (new Date()).getTime();
+}
+
+function get_context() {
+ return context;
+};
+
+function sample_count() {
+ //TODO get someone to call this from the emulator,
+ //so the emulator can do proper audio buffering by
+ //knowing how many samples are left:
+ if (!context) {
+ //Use impossible value as an error code:
+ return -1;
+ }
+ var count = rear - start;
+ if (start > rear) {
+ count += bufferSize;
+ }
+ return count;
+}
+
+return {
+ set_mastervolume: set_mastervolume,
+ update_audio_stream: update_audio_stream,
+ get_context: get_context,
+ sample_count: sample_count
+};
+
+})();
+
+window.jsmame_set_mastervolume = jsmame_web_audio.set_mastervolume;
+window.jsmame_update_audio_stream = jsmame_web_audio.update_audio_stream;
+window.jsmame_sample_count = jsmame_web_audio.sample_count;