summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/sound/js_sound.js
diff options
context:
space:
mode:
author ImJezze <jezze@gmx.net>2016-02-21 11:48:45 +0100
committer ImJezze <jezze@gmx.net>2016-02-21 11:48:45 +0100
commitcc24a339d8c0517259084b5c178d784626ba965c (patch)
tree9868e9687b5802ae0a3733712a3bbeb3bc75c953 /src/osd/modules/sound/js_sound.js
parentb5daabda5495dea5c50e17961ecfed2ea8619d76 (diff)
Merge remote-tracking branch 'refs/remotes/mamedev/master'
Second attempt
Diffstat (limited to 'src/osd/modules/sound/js_sound.js')
-rw-r--r--src/osd/modules/sound/js_sound.js213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/osd/modules/sound/js_sound.js b/src/osd/modules/sound/js_sound.js
new file mode 100644
index 00000000000..b06cbb44365
--- /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;