Reputation: 473
I've created a javascript audio controller that runs perfectly in browsers on my Windows computer (for testing reasons), and on my android mobile phone. It is capable of
To achieve this last point, I have to listen to the audio.onended event, in order wait with the start of the following sound until the previous sound has finished playing.
The javascript audio controller is part of a mobile application, thus it has to run on mobile/smartphone browsers. When I run the same app it in iPhone Safari, a few sounds are played, but then a certain sound is not played. On order to troubleshoot this, I have added an onerror event listener, but neither the onended handler, nor the
async playNwait(audio) { // plays sound and waits until it has finished
return new Promise ((resolve) => {
audio.onerror = (event) => {
log('Play Error: ' + audio.error.code);
log('Play Error: ' + audio.error.message);
log('Play Error: ' + event.currentTarget.error.code);
log('Play Error: ' + event.currentTarget.error.message);
reject('Audio failed');
};
audio.onended = (event) => {
log(' playNwait ended');
resolve('Audio ended');
};
log(' playNwait started');
log(audio.src);
audio.addEventListener("error", function(e) {
log("Playback error: " + e.currentTarget.error.code);
var prop, props='\nEvent properties are: ';
var o = e.currentTarget.error;
for (prop in o) {
props += prop+'='+o[prop]+' ';
}
log(props);
reject('Audio failed');
});
audio.play();
});
}
The function log calls a console.log function with the same parameter, and also sends the value via an API to a server, where it is logged in a database table, on order to analyse the log entries for mobile browsers.
I know I have two error handlers that have the same purpose. None of them fires.
For easy comparison, here are the relevant log entries generated by Firefox on Andriod and Safari on iOS:
"Playing 4: 60_sechzig" 2021-05-28 23:40:28
" playNwait started" 2021-05-28 23:40:28
"data:audio\/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPA..." 2021-05-28 23:40:28
" playNwait ended" 2021-05-28 23:40:29
"Played 4" 2021-05-28 23:40:29
"Playing 4: 60_sechzig" 2021-05-28 22:21:13
" playNwait started" 2021-05-28 22:21:13
"data:audio\/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAAPA..." 2021-05-28 22:21:13
... and nothing more - neither the onended nor one of the error handler fires. Moreover, further sounds are not played, as the app wrongly assumes, that the previous sound has not finished playing. (It doesn't start playing.)
4 is just the number of the sound in the "play list".
How can I get the onerror event handler working? How can I further troubleshoot this?
Any help is highly appreciated.
Upvotes: 1
Views: 2524
Reputation: 473
Problem identified: The audio play function failed on iOS Safari due to a NotAllowedError: "The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission."
Here is my updated function that reports the error (and adds some debugging):
async playNwait(audio) { // plays sound an wait until it has finished
return new Promise (async (resolve, reject) => {
audio.onerror = (event) => {
log('Play Error: ' + audio.error.code);
log('Play Error: ' + audio.error.message);
log('Play Error: ' + event.currentTarget.error.code);
log('Play Error: ' + event.currentTarget.error.message);
reject('Audio failed');
};
audio.abort = (event) => {
log(' playNwait abort');
};
audio.loadstart = (event) => {
log(' playNwait loadstart');
};
audio.onplay = (event) => {
log(' playNwait playing');
};
audio.onended = (event) => {
log(' playNwait ended');
resolve('Audio ended');
};
log(' playNwait started');
log(audio.src);
audio.addEventListener("error", function(e) {
log("Playback error: " + e.currentTarget.error.code);
var prop, props='\nEvent properties are: ';
var o = e.currentTarget.error;
for (prop in o) {
props += prop+'='+o[prop]+' ';
}
log(props);
reject('Audio failed');
});
try {
await audio.play();
} catch (err) {
error ('Media play error: ' + err.name + ' - ' + err.message);
}
});
}
I know that iOS Safari blocks playing audio when it was not initialized by user interactio. I know I had a successful solution for that in the past, that is, to load the audio files on user interaction.
However, this is a different problem, and this question here is addressed and solved: The error handler never fires because the play method already fails.
Upvotes: 5