mirror of
https://github.com/Ryujinx/SDL.git
synced 2025-03-22 16:55:07 +00:00
emscriptenaudio: Deal with blocked audio devices better.
Now, if the AudioContext starts in a "suspended" state, because the browser blocked it from playing by default, we we run the audio "thread" in a timer and throw away the generated audio. Once the AudioContext is allowed to resume, we clear this timer. The end result is that the app will continue to drain its audio queue instead of consuming more memory over time (and, if it relies on an audio callback to make progress, continue to run!), with the effect that the page is merely silent but otherwise functioning as intended. Once the user interacts with the page and the browser permits the the AudioContext to run for real, audio should still be in sync, instead of just starting to play audio that might now be at least several seconds behind. (cherry picked from commit fd75a4ca05bdbd7b0fecf781e59c77a07d264b16)
This commit is contained in:
parent
0b9d8e679a
commit
ae7f54f514
|
@ -183,6 +183,12 @@ static void EMSCRIPTENAUDIO_CloseDevice(_THIS)
|
|||
SDL2.audio.scriptProcessorNode.disconnect();
|
||||
SDL2.audio.scriptProcessorNode = undefined;
|
||||
}
|
||||
if (SDL2.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL2.audio.silenceTimer);
|
||||
}
|
||||
if (SDL2.audio.silenceBuffer !== undefined) {
|
||||
SDL2.audio.silenceBuffer = undefined
|
||||
}
|
||||
SDL2.audio = undefined;
|
||||
}
|
||||
if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) {
|
||||
|
@ -227,7 +233,9 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
|
|||
SDL2.audioContext = new webkitAudioContext();
|
||||
}
|
||||
if (SDL2.audioContext) {
|
||||
autoResumeAudioContext(SDL2.audioContext);
|
||||
if ((typeof navigator.userActivation) === 'undefined') { // Firefox doesn't have this (as of August 2023), use autoResumeAudioContext instead.
|
||||
autoResumeAudioContext(SDL2.audioContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL2.audioContext === undefined ? -1 : 0;
|
||||
|
@ -298,6 +306,7 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
|
|||
if (SDL2.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL2.capture.silenceTimer);
|
||||
SDL2.capture.silenceTimer = undefined;
|
||||
SDL2.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream);
|
||||
SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1);
|
||||
|
@ -339,10 +348,37 @@ static int EMSCRIPTENAUDIO_OpenDevice(_THIS, const char *devname)
|
|||
SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0);
|
||||
SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
|
||||
if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; }
|
||||
// if we're actually running the node, we don't need the fake callback anymore, so kill it.
|
||||
if (SDL2.audio.silenceTimer !== undefined) {
|
||||
clearInterval(SDL2.audio.silenceTimer);
|
||||
SDL2.audio.silenceTimer = undefined;
|
||||
SDL2.audio.silenceBuffer = undefined;
|
||||
}
|
||||
SDL2.audio.currentOutputBuffer = e['outputBuffer'];
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);
|
||||
|
||||
if (SDL2.audioContext.state === 'suspended') { // uhoh, autoplay is blocked.
|
||||
SDL2.audio.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate);
|
||||
SDL2.audio.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
if ((typeof navigator.userActivation) !== 'undefined') { // Almost everything modern except Firefox (as of August 2023)
|
||||
if (navigator.userActivation.hasBeenActive) {
|
||||
SDL2.audioContext.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// the buffer that gets filled here just gets ignored, so the app can make progress
|
||||
// and/or avoid flooding audio queues until we can actually play audio.
|
||||
SDL2.audio.currentOutputBuffer = SDL2.audio.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
SDL2.audio.currentOutputBuffer = undefined;
|
||||
};
|
||||
|
||||
SDL2.audio.silenceTimer = setInterval(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000);
|
||||
}
|
||||
}, this->spec.channels, this->spec.samples, HandleAudioProcess, this);
|
||||
}
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
|
Loading…
Reference in a new issue