diff --git a/src/osd/modules/sound/js_sound.js b/src/osd/modules/sound/js_sound.js index 7f00d703..f3bcd166 100644 --- a/src/osd/modules/sound/js_sound.js +++ b/src/osd/modules/sound/js_sound.js @@ -17,6 +17,16 @@ var eventNode = null; var sampleScale = 32766; var inputBuffer = new Float32Array(44100); var bufferSize = 44100; +//Everything waiting in the queue is delay between the game and the speakers. +//MAME's emulated output is 48 kHz, the default of -samplerate. +var SAMPLE_RATE = 48000; +var BLOCK = 2048; //frames in each block handed to the browser +var QUEUE_START = 3 * BLOCK; //values, two to a frame: play when this much has arrived +var QUEUE_LIMIT = 5 * BLOCK; //over this, drop the oldest +var QUEUE_KEEP = 3 * BLOCK; //values left after a drop +var playing = false; +var lastLeft = 0; +var lastRight = 0; var start = 0; var rear = 0; var watchDogDateLast = null; @@ -30,11 +40,12 @@ function lazy_init () { } if (typeof AudioContext != "undefined") { //Standard context creation: - context = new AudioContext(); + //The page starts this on the Start tap. Phones keep a later context silent. + context = window.vectrexAudio || new AudioContext({ sampleRate: SAMPLE_RATE, latencyHint: "interactive" }); } else if (typeof webkitAudioContext != "undefined") { //Older webkit context creation: - context = new webkitAudioContext(); + context = new webkitAudioContext({ sampleRate: SAMPLE_RATE }); } else { //API not found! @@ -54,11 +65,11 @@ function init_event() { //Generate a streaming node point: if (typeof context.createScriptProcessor == "function") { //Current standard compliant way: - eventNode = context.createScriptProcessor(4096, 0, 2); + eventNode = context.createScriptProcessor(BLOCK, 0, 2); } else { //Deprecated way: - eventNode = context.createJavaScriptNode(4096, 0, 2); + eventNode = context.createJavaScriptNode(BLOCK, 0, 2); } //Make our tick function the audio callback function: eventNode.onaudioprocess = tick; @@ -136,6 +147,14 @@ function stream_sink_update ( } } } + + var queued = rear - start; + if (queued < 0) { + queued += bufferSize; + } + if (queued > QUEUE_LIMIT) { + start = (rear - QUEUE_KEEP + bufferSize) % bufferSize; + } }; function tick (event) { @@ -143,19 +162,32 @@ function tick (event) { for (var bufferCount = 0, buffers = []; bufferCount < 2; ++bufferCount) { buffers[bufferCount] = event.outputBuffer.getChannelData(bufferCount); } + //After running dry, wait for a margin, so a late browser frame does not run dry again: + var queued = rear - start; + if (queued < 0) { + queued += bufferSize; + } + if (queued < 2 * BLOCK) { + playing = false; + } + else if (queued >= QUEUE_START) { + playing = true; + } //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; + var index = 0; + if (playing) { + for (; index < BLOCK && start != rear; ++index) { + lastLeft = buffers[0][index] = inputBuffer[start++]; + lastRight = buffers[1][index] = inputBuffer[start++]; + if (start == bufferSize) { + start = 0; + } } } //Pad with latest if we're underrunning: - var idx = (index == 0 ? bufferSize : index) - 1; - while (index < 4096) { - buffers[0][index] = buffers[0][idx]; - buffers[1][index++] = buffers[1][idx]; + while (index < BLOCK) { + buffers[0][index] = lastLeft; + buffers[1][index++] = lastRight; } //Deep inside the bowels of vendors bugs, //we're using watchdog for a firefox bug,