From 336e1eeb51ffda3ae3999f01f26ed57074795ba6 Mon Sep 17 00:00:00 2001 From: ToadKing Date: Mon, 2 Sep 2013 21:29:40 -0400 Subject: [PATCH] [EMSCRIPTEN] more audio fixes, revert to busywait method --- config.def.h | 2 - emscripten/RWebAudio.c | 4 +- emscripten/RWebAudio.h | 1 - emscripten/library_rwebaudio.js | 284 ++++++++++++++++---------------- frontend/frontend_emscripten.c | 5 - 5 files changed, 143 insertions(+), 153 deletions(-) diff --git a/config.def.h b/config.def.h index 3294cd73b2..32d263a623 100644 --- a/config.def.h +++ b/config.def.h @@ -338,8 +338,6 @@ static const bool rate_control = false; // Rate control delta. Defines how much rate_control is allowed to adjust input rate. #if defined(__QNX__) static const float rate_control_delta = 0.000; -#elif defined(EMSCRIPTEN) -static const float rate_control_delta = 0.002; #else static const float rate_control_delta = 0.005; #endif diff --git a/emscripten/RWebAudio.c b/emscripten/RWebAudio.c index b384c828a8..1040da32d7 100644 --- a/emscripten/RWebAudio.c +++ b/emscripten/RWebAudio.c @@ -28,8 +28,8 @@ static void *ra_init(const char *device, unsigned rate, unsigned latency) (void)device; (void)rate; void *data = RWebAudioInit(latency); - g_settings.audio.out_rate = RWebAudioSampleRate(); - RARCH_LOG("audio out rate: %u\n", g_settings.audio.out_rate); + if (data) + g_settings.audio.out_rate = RWebAudioSampleRate(); return data; } diff --git a/emscripten/RWebAudio.h b/emscripten/RWebAudio.h index fd928cd3d6..6911753923 100644 --- a/emscripten/RWebAudio.h +++ b/emscripten/RWebAudio.h @@ -26,4 +26,3 @@ void RWebAudioSetNonblockState(bool state); void RWebAudioFree(void); size_t RWebAudioWriteAvail(void); size_t RWebAudioBufferSize(void); -int RWebAudioEnoughSpace(void); diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js index d2197ceccb..73448afbf9 100644 --- a/emscripten/library_rwebaudio.js +++ b/emscripten/library_rwebaudio.js @@ -1,160 +1,158 @@ //"use strict"; var LibraryRWebAudio = { - $RA__deps: ['$Browser'], - $RA: { - SCRIPTNODE_BUFFER: 1024, + $RA__deps: ['$Browser'], + $RA: { + BUFFER_SIZE: 256, - context: null, - leftBuffer: null, - rightBuffer: null, - blank: null, - scriptNode: null, - bufferNode: null, - start: 0, - end: 0, - size: 0, - lastWrite: 0, - nonblock: false, - - npot: function(n) { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; - }, + context: null, + buffers: [], + numBuffers: 0, + bufIndex: 0, + bufOffset: 0, + startTime: 0, + nonblock: false, - process: function(e) { - var left = e.outputBuffer.getChannelData(0); - var right = e.outputBuffer.getChannelData(1); - var samples1 = RA.size; - var samples2 = 0; - samples1 = e.outputBuffer.length > samples1 ? samples1 : e.outputBuffer.length; + setStartTime: function() { + if (RA.context.currentTime) { + RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000; + if (RA.startTime === 0) throw 'startTime is 0'; + Module["resumeMainLoop"](); + } else window['setTimeout'](RA.setStartTime, 0); + }, - if (samples1 + RA.start > RA.leftBuffer.length) { - samples2 = samples1 + RA.start - RA.leftBuffer.length; - samples1 = samples1 - samples2; + getCurrentPerfTime: function() { + if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; + else throw 'getCurrentPerfTime() called before start time set'; + }, + + process: function(queueBuffers) { + var currentTime = RA.getCurrentPerfTime(); + for (var i = 0; i < RA.bufIndex; i++) { + if (RA.buffers[i].endTime < currentTime) { + var buf = RA.buffers.splice(i, 1); + RA.buffers[RA.numBuffers - 1] = buf[0]; + i--; + RA.bufIndex--; + } + } + }, + + fillBuffer: function(buf, samples) { + var count = 0; + var leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0); + var rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1); + while (samples && RA.bufOffset !== RA.BUFFER_SIZE) { + leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}}; + rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}}; + RA.bufOffset++; + count++; + samples--; + } + + return count; + }, + + queueAudio: function() { + var index = RA.bufIndex; + + var startTime; + if (RA.bufIndex) startTime = RA.buffers[RA.bufIndex - 1].endTime; + else startTime = RA.context.currentTime; + RA.buffers[index].endTime = startTime + RA.buffers[index].duration; + + var bufferSource = RA.context.createBufferSource(); + bufferSource.buffer = RA.buffers[index]; + bufferSource.connect(RA.context.destination); + bufferSource.start(startTime); + + RA.bufIndex++; + RA.bufOffset = 0; + }, + + block: function() { + do { + RA.process(); + } while (RA.bufIndex === RA.numBuffers - 1); + } + }, + + RWebAudioInit: function(latency) { + var ac = window['AudioContext'] || window['webkitAudioContext']; + + if (!ac) return 0; + + RA.context = new ac(); + + RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0; + if (RA.numBuffers < 2) RA.numBuffers = 2; + + for (var i = 0; i < RA.numBuffers; i++) RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate); + + RA.nonblock = false; + RA.startTime = 0; + // chrome hack to get currentTime running + RA.context.createGain(); + window['setTimeout'](RA.setStartTime, 0); + Module["pauseMainLoop"](); + return 1; + }, + + RWebAudioSampleRate: function() { + return RA.context.sampleRate; + }, + + RWebAudioWrite: function (buf, size) { + RA.process(); + var samples = size / 8; + var count = 0; + + while (samples) { + var fill = RA.fillBuffer(buf, samples); + samples -= fill; + count += fill; + buf += fill * 8; + + if (RA.bufOffset === RA.BUFFER_SIZE) { + if (RA.bufIndex === RA.numBuffers - 1) { + if (RA.nonblock) break; + else RA.block(); + } + RA.queueAudio(); + } } - var remaining = e.outputBuffer.length - (samples1 + samples2); + return count * 8; + }, - if (samples1) { - left.set(RA.leftBuffer.subarray(RA.start, RA.start + samples1), 0); - right.set(RA.rightBuffer.subarray(RA.start, RA.start + samples1), 0); - } + RWebAudioStop: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return true; + }, - if (samples2) { - left.set(RA.leftBuffer.subarray(0, samples2), samples1); - right.set(RA.rightBuffer.subarray(0, samples2), samples1); - } + RWebAudioStart: function() { + return true; + }, - /*if (remaining) { - left.set(RA.blank.subarray(0, remaining), samples1 + samples2); - right.set(RA.blank.subarray(0, remaining), samples1 + samples2); - }*/ + RWebAudioSetNonblockState: function(state) { + RA.nonblock = state; + }, - RA.start = (RA.start + samples1 + samples2) % RA.leftBuffer.length; - RA.size -= samples1 + samples2; - } - }, - - RWebAudioSampleRate: function() { - return RA.context.sampleRate; - }, + RWebAudioFree: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return; + }, - RWebAudioInit: function(latency) { - var ac = window['AudioContext'] || window['webkitAudioContext']; - var bufferSize; + RWebAudioBufferSize: function() { + return RA.numBuffers * RA.BUFFER_SIZE + RA.BUFFER_SIZE; + }, - if (!ac) return 0; - - RA.context = new ac(); - // account for script processor overhead - latency -= 32; - // because we have to guess on how many samples the core will send when - // returning early, we double the buffer size to account for times when it - // sends more than we expect it to without losing samples - bufferSize = RA.npot(RA.context.sampleRate * latency / 1000) * 2; - RA.leftBuffer = new Float32Array(bufferSize); - RA.rightBuffer = new Float32Array(bufferSize); - RA.blank = new Float32Array(RA.SCRIPTNODE_BUFFER); - RA.bufferNode = RA.context.createBufferSource(); - RA.bufferNode.buffer = RA.context.createBuffer(2, RA.SCRIPTNODE_BUFFER, RA.context.sampleRate); - RA.bufferNode.loop = true; - RA.scriptNode = RA.context.createScriptProcessor(RA.SCRIPTNODE_BUFFER, 2, 2); - RA.scriptNode.onaudioprocess = RA.process; - RA.bufferNode.connect(RA.scriptNode); - RA.scriptNode.connect(RA.context.destination); - RA.bufferNode.start(0); - RA.start = RA.end = RA.size = 0; - RA.nonblock = false; - return 1; - }, - - RWebAudioWrite: function (buf, size) { - var samples = size / 8; - var free = RA.leftBuffer.length - RA.size; - if (free < samples) - RA.start = (RA.start + free) % RA.leftBuffer.length; - - for (var i = 0; i < samples; i++) { - RA.leftBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8', 'float') }}}; - RA.rightBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8 + 4', 'float') }}}; - RA.end = (RA.end + 1) % RA.leftBuffer.length; - } - - RA.lastWrite = size; - RA.size += samples; - return size; - }, - - RWebAudioStop: function() { - RA.scriptNode.onaudioprocess = null; - return true; - }, - - RWebAudioStart: function() { - RA.scriptNode.onaudioprocess = RA.process; - return true; - }, - - RWebAudioSetNonblockState: function(state) { - RA.nonblock = state; - }, - - RWebAudioFree: function() { - RA.scriptNode.onaudioprocess = null; - RA.start = RA.end = RA.size = RA.lastWrite = 0; - return; - }, - - RWebAudioWriteAvail: function() { - var free = (RA.leftBuffer.length / 2) - RA.size; - // 4 byte samples, 2 channels - free *= 8; - - if (free < 0) - return 0; - else - return free; - }, - - RWebAudioBufferSize: function() { - return RA.leftBuffer.length / 2; - }, - - RWebAudioEnoughSpace__deps: ['RWebAudioWriteAvail'], - RWebAudioEnoughSpace: function() { - var guess = RA.lastWrite; - var available = _RWebAudioWriteAvail(); - if (RA.nonblock) return true; - if (!guess) return true; - return (guess < available) ? 1 : 0; - } + RWebAudioWriteAvail: function() { + RA.process(); + return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8; + } }; autoAddDeps(LibraryRWebAudio, '$RA'); diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c index 452c7f41ea..1941e49984 100644 --- a/frontend/frontend_emscripten.c +++ b/frontend/frontend_emscripten.c @@ -56,11 +56,6 @@ static void endloop(void) static void mainloop(void) { - if (!RWebAudioEnoughSpace()) - { - return; - } - if (g_extern.system.shutdown) { endloop();