as a follow-up on this post: How to rapidly play multiple copies of a soundfile in javascript I created a small demo page to illustrate the core of my problem.
My goal is to rapidly play the same audiofile over and over again while a user holds down a button, without crashing the browser ;-) (second line in the fiddle)
My initial method uses clone node to create multiple audio objects in the DOM. This works actually fine in chrome and edge, but safari and firefox run into problems after a while. This leads to dis-synchronized audios, and audios that keep on starting after the user has released the button.
So Codebreaker007 proposed to use audio-context instead, which resulted in a couple of different problems. Chrome replies:
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
Chrome and firefox don't play the audio file. I then followed the google guide and got at least the error messages to be gone, but still no audible audio. Using the web audio plugin for chrome I could at one point see that the audio nodes are being created correctly. How can I make them audible? How can I get the Audio Context to start?
I think I'm quite close to the solution, so let's fix this together.
<!doctype html>
<html class="h-100" lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>Test</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
AudioContext:true;
var clickingBuffer = null;
var timer
var inStart = 0
var inStop = 0
var timer = null
var type = ""
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
// =========== Variant 1: Clone Node ========== //
var sound = new Audio("https://sounds4email.com/wav/hellobaby.mp3");
sound.preload = 'auto';
sound.load();
function triggersound(){
console.log("triggerSound")
var click=sound.cloneNode();
click.volume=1;
click.play();
}
// =========== Variant 2: AudioContext ========== //
function loadClickSound(url) {
console.log("loading sound")
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
if (!buffer) {
console.log('Error decoding file data: ' + url);
return;
}
clickingBuffer = buffer;
});
request.onerror = function() {
console.log('BufferLoader: XHR error');
};
console.og("sound buffered")
request.send();
};
}
function playSound(buffer, time, volume) {
console.log("playSound")
context.resume().then(() => {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
var gainNode = context.createGain(); // Create a gain node
source.connect(gainNode); // Connect the source to the gain node
gainNode.connect(context.destination); // Connect the gain node to the destination
gainNode.gain.value = volume; // Set the volume
source.start(time); // play the source at the deisred time 0=now
console.log('Playback resumed successfully');
});
}
// =========== RAPID FIRE ========== //
function stop() {
console.log("stop")
inStop = 1
}
// Initializing the spinning sequence. Blocking other user interaction
function start(tp) {
type = tp
console.log("active")
context.resume().then(() => {
console.log('Playback resumed successfully');
if (null === timer) {
timer = setInterval(timerCallback, 200)
inStart = 1
}
});
}
/**
* Timer callback
*/
function timerCallback() {
console.log(type + " " + inStart + " " + inStop)
if (inStart) {
if(type==="node"){
triggersound()
} else if(type==="context"){
playSound(clickingBuffer, 0, 1)
}
}
if (inStop) {
inStop = 0
clearTimeout(timer)
timer = null
console.log("stopped")
}
}
// =========== DOC READY ========== //
$( document ).ready(function() {
console.log( "ready!" );
// You call with in your document ready
loadClickSound("https://sounds4email.com/wav/hellobaby.mp3");
// Fix up prefixi
});
// =================================================================================//
</script>
</head>
<body class="d-flex flex-column align-content-center align-items-center justify-content-center w-100 h-100" >
<div class="row p-1 w-100">
<div class="col">
Click once:
</div>
<button id="clickNode" style="width: 100px; height: 100px;" class="col m-1" onclick="triggersound()">
Clone Node
</button>
<button id="clickContext" style="width: 100px; height: 100px;" class="col m-1" onclick="playSound(clickingBuffer, 0, 1)">
Audio Context
</button>
</div>
<div class="row p-1 w-100">
<div class="col">
Press and hold:
</div>
<button id="autoNode" style="width: 100px; height: 100px;" class="col m-1" onmousedown="start('node')" onmouseup="stop()">
Auto Clone Node
</button>
<button id="autoContext" style="width: 100px; height: 100px;" class="col m-1" onmousedown="start('context')" onmouseup="stop()">
Auto Audio Context
</button>
</div>
</body>
</html>