mirror of
https://github.com/libretro/RetroArch
synced 2025-04-11 00:44:20 +00:00
[EMSCRIPTEN] more audio fixes, revert to busywait method
This commit is contained in:
parent
04be8cbee2
commit
336e1eeb51
@ -338,8 +338,6 @@ static const bool rate_control = false;
|
|||||||
// Rate control delta. Defines how much rate_control is allowed to adjust input rate.
|
// Rate control delta. Defines how much rate_control is allowed to adjust input rate.
|
||||||
#if defined(__QNX__)
|
#if defined(__QNX__)
|
||||||
static const float rate_control_delta = 0.000;
|
static const float rate_control_delta = 0.000;
|
||||||
#elif defined(EMSCRIPTEN)
|
|
||||||
static const float rate_control_delta = 0.002;
|
|
||||||
#else
|
#else
|
||||||
static const float rate_control_delta = 0.005;
|
static const float rate_control_delta = 0.005;
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,8 +28,8 @@ static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
(void)device;
|
(void)device;
|
||||||
(void)rate;
|
(void)rate;
|
||||||
void *data = RWebAudioInit(latency);
|
void *data = RWebAudioInit(latency);
|
||||||
|
if (data)
|
||||||
g_settings.audio.out_rate = RWebAudioSampleRate();
|
g_settings.audio.out_rate = RWebAudioSampleRate();
|
||||||
RARCH_LOG("audio out rate: %u\n", g_settings.audio.out_rate);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,4 +26,3 @@ void RWebAudioSetNonblockState(bool state);
|
|||||||
void RWebAudioFree(void);
|
void RWebAudioFree(void);
|
||||||
size_t RWebAudioWriteAvail(void);
|
size_t RWebAudioWriteAvail(void);
|
||||||
size_t RWebAudioBufferSize(void);
|
size_t RWebAudioBufferSize(void);
|
||||||
int RWebAudioEnoughSpace(void);
|
|
||||||
|
@ -3,122 +3,135 @@
|
|||||||
var LibraryRWebAudio = {
|
var LibraryRWebAudio = {
|
||||||
$RA__deps: ['$Browser'],
|
$RA__deps: ['$Browser'],
|
||||||
$RA: {
|
$RA: {
|
||||||
SCRIPTNODE_BUFFER: 1024,
|
BUFFER_SIZE: 256,
|
||||||
|
|
||||||
context: null,
|
context: null,
|
||||||
leftBuffer: null,
|
buffers: [],
|
||||||
rightBuffer: null,
|
numBuffers: 0,
|
||||||
blank: null,
|
bufIndex: 0,
|
||||||
scriptNode: null,
|
bufOffset: 0,
|
||||||
bufferNode: null,
|
startTime: 0,
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
size: 0,
|
|
||||||
lastWrite: 0,
|
|
||||||
nonblock: false,
|
nonblock: false,
|
||||||
|
|
||||||
npot: function(n) {
|
setStartTime: function() {
|
||||||
n--;
|
if (RA.context.currentTime) {
|
||||||
n |= n >> 1;
|
RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000;
|
||||||
n |= n >> 2;
|
if (RA.startTime === 0) throw 'startTime is 0';
|
||||||
n |= n >> 4;
|
Module["resumeMainLoop"]();
|
||||||
n |= n >> 8;
|
} else window['setTimeout'](RA.setStartTime, 0);
|
||||||
n |= n >> 16;
|
|
||||||
n++;
|
|
||||||
return n;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
process: function(e) {
|
getCurrentPerfTime: function() {
|
||||||
var left = e.outputBuffer.getChannelData(0);
|
if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000;
|
||||||
var right = e.outputBuffer.getChannelData(1);
|
else throw 'getCurrentPerfTime() called before start time set';
|
||||||
var samples1 = RA.size;
|
},
|
||||||
var samples2 = 0;
|
|
||||||
samples1 = e.outputBuffer.length > samples1 ? samples1 : e.outputBuffer.length;
|
|
||||||
|
|
||||||
if (samples1 + RA.start > RA.leftBuffer.length) {
|
process: function(queueBuffers) {
|
||||||
samples2 = samples1 + RA.start - RA.leftBuffer.length;
|
var currentTime = RA.getCurrentPerfTime();
|
||||||
samples1 = samples1 - samples2;
|
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--;
|
||||||
}
|
}
|
||||||
|
|
||||||
var remaining = e.outputBuffer.length - (samples1 + samples2);
|
return count;
|
||||||
|
},
|
||||||
|
|
||||||
if (samples1) {
|
queueAudio: function() {
|
||||||
left.set(RA.leftBuffer.subarray(RA.start, RA.start + samples1), 0);
|
var index = RA.bufIndex;
|
||||||
right.set(RA.rightBuffer.subarray(RA.start, RA.start + samples1), 0);
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (samples2) {
|
RWebAudioInit: function(latency) {
|
||||||
left.set(RA.leftBuffer.subarray(0, samples2), samples1);
|
var ac = window['AudioContext'] || window['webkitAudioContext'];
|
||||||
right.set(RA.rightBuffer.subarray(0, samples2), samples1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (remaining) {
|
if (!ac) return 0;
|
||||||
left.set(RA.blank.subarray(0, remaining), samples1 + samples2);
|
|
||||||
right.set(RA.blank.subarray(0, remaining), samples1 + samples2);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
RA.start = (RA.start + samples1 + samples2) % RA.leftBuffer.length;
|
RA.context = new ac();
|
||||||
RA.size -= samples1 + samples2;
|
|
||||||
}
|
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() {
|
RWebAudioSampleRate: function() {
|
||||||
return RA.context.sampleRate;
|
return RA.context.sampleRate;
|
||||||
},
|
},
|
||||||
|
|
||||||
RWebAudioInit: function(latency) {
|
|
||||||
var ac = window['AudioContext'] || window['webkitAudioContext'];
|
|
||||||
var bufferSize;
|
|
||||||
|
|
||||||
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) {
|
RWebAudioWrite: function (buf, size) {
|
||||||
|
RA.process();
|
||||||
var samples = size / 8;
|
var samples = size / 8;
|
||||||
var free = RA.leftBuffer.length - RA.size;
|
var count = 0;
|
||||||
if (free < samples)
|
|
||||||
RA.start = (RA.start + free) % RA.leftBuffer.length;
|
|
||||||
|
|
||||||
for (var i = 0; i < samples; i++) {
|
while (samples) {
|
||||||
RA.leftBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8', 'float') }}};
|
var fill = RA.fillBuffer(buf, samples);
|
||||||
RA.rightBuffer[RA.end] = {{{ makeGetValue('buf', 'i * 8 + 4', 'float') }}};
|
samples -= fill;
|
||||||
RA.end = (RA.end + 1) % RA.leftBuffer.length;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RA.lastWrite = size;
|
return count * 8;
|
||||||
RA.size += samples;
|
|
||||||
return size;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
RWebAudioStop: function() {
|
RWebAudioStop: function() {
|
||||||
RA.scriptNode.onaudioprocess = null;
|
RA.bufIndex = 0;
|
||||||
|
RA.bufOffset = 0;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
RWebAudioStart: function() {
|
RWebAudioStart: function() {
|
||||||
RA.scriptNode.onaudioprocess = RA.process;
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -127,33 +140,18 @@ var LibraryRWebAudio = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
RWebAudioFree: function() {
|
RWebAudioFree: function() {
|
||||||
RA.scriptNode.onaudioprocess = null;
|
RA.bufIndex = 0;
|
||||||
RA.start = RA.end = RA.size = RA.lastWrite = 0;
|
RA.bufOffset = 0;
|
||||||
return;
|
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() {
|
RWebAudioBufferSize: function() {
|
||||||
return RA.leftBuffer.length / 2;
|
return RA.numBuffers * RA.BUFFER_SIZE + RA.BUFFER_SIZE;
|
||||||
},
|
},
|
||||||
|
|
||||||
RWebAudioEnoughSpace__deps: ['RWebAudioWriteAvail'],
|
RWebAudioWriteAvail: function() {
|
||||||
RWebAudioEnoughSpace: function() {
|
RA.process();
|
||||||
var guess = RA.lastWrite;
|
return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8;
|
||||||
var available = _RWebAudioWriteAvail();
|
|
||||||
if (RA.nonblock) return true;
|
|
||||||
if (!guess) return true;
|
|
||||||
return (guess < available) ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,11 +56,6 @@ static void endloop(void)
|
|||||||
|
|
||||||
static void mainloop(void)
|
static void mainloop(void)
|
||||||
{
|
{
|
||||||
if (!RWebAudioEnoughSpace())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_extern.system.shutdown)
|
if (g_extern.system.shutdown)
|
||||||
{
|
{
|
||||||
endloop();
|
endloop();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user