I am learning how to use the Web Audio API and I am experiencing a problem that I am not sure how to resolve. I am not sure if it is because I am a beginner at this, or there is something going on that I don't understand.
Basically I am creating a function that plays a short mp3. It should http GET to load MP3 into the audio buffer the first time the audio is played. (Subsequently requests for the same audio don't need to re-fetch the mp3)
The code below works completely correctly in Chrome. However in Safari playAudio('filename')
will not play audio the very first time that it is invoked. Every single time after that it works fine. Any expert advise from someone more experienced than me would be very much appreciated.
<script>
var context;
function init() {
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
}
catch(e) {
alert("Your browser doesn't support Web Audio API");
}
}
window.addEventListener('load', init);
function loadAudio(w) {
var audioURL='https://biblicaltext.com/audio/' + w + '.mp3';
var request = new XMLHttpRequest();
request.open("GET", audioURL, true);
request.responseType = 'arraybuffer';
request.onload = function() {
//console.log("update", request.readyState, request.status)
if (request.readyState === 4) {
if (request.status === 200) {
//take the audio from http request and decode it in an audio buffer
context.decodeAudioData(request.response, function(buffer) {
if(buffer) {
console.log(w, buffer);
playAudioBuffer(w, buffer);
} else {
console.log("audio buffer decoding failed")
}
}, function(ec) {
console.log("http request buffer decoding failed")
});
} else {
console.log("get", audioURL, "failed", request.status)
}
}
}
request.send();
}
var audioBuffer;
var currentWord;
function playAudio(w) {
if (typeof audioBuffer === 'undefined' || audioBuffer == null || currentWord != w) {
audioBuffer = null
currentWord = ""
console.log("reqest load of different word", w)
loadAudio(w)
} else {
playAudioBuffer(w, audioBuffer)
}
}
function playAudioBuffer(w, buffer) {
audioBuffer = buffer
currentWord = w
var source = context.createBufferSource();
source.buffer = audioBuffer;
source.connect(context.destination);
source.start();
console.log('playing', currentWord);
}
</script>
<a href="javascript:playAudio('ἦν')">Play ἦν</a>
<br>
<a href="javascript:playAudio('ὥστε')">Play ὥστε</a>
<br>
<a href="javascript:playAudio('οὐρανός')">Play οὐρανός</a>
<br>
<a href="javascript:playAudio('υἱός')">Play υἱός</a>
<br>
<a href="javascript:playAudio('εἷς')">Play εἷς</a>
<br>
Am I hitting some kind of weird situation where Safari is blocking sound because the clicking of the link is disconnected from the loading/playing of the audio by a fraction of a second?
It turns out the way to 'unlock' audio is to play a silent/hidden sound when the user first interacts with the page to make sure future requests for audio will function correctly.
https://paulbakaus.com/tutorials/html5/web-audio-on-ios/
Here is what I used:
window.addEventListener('touchstart', function() {
// create empty buffer
var buffer = myContext.createBuffer(1, 1, 22050);
var source = myContext.createBufferSource();
source.buffer = buffer;
// connect to output (your speakers)
source.connect(myContext.destination);
// play the file
source.noteOn(0);
}, false);
I realize I am probably not the first person to have asked this on SO, however the search keywords (safari play first time vs second time) don't turn up anything on SO, so I am inclined to answer and leave this up. If the community things deleting my noob question would be better then I'm happy to do that.