// do nothing if the context was closed to avoid errors... can't check for the suspended state because of apple's crappy interrupted implementation
// Can only resume after a media playback (input) event has occurred
if(hasMediaPlaybackEventOccurred){
varp=context.resume();
if(p)p.then(noop,noop).catch(noop);
}
}
}else{
// Want to be suspended, so try suspending
if(context.state==="running"){
varp=context.suspend();
if(p)p.then(noop,noop).catch(noop);
}
}
}
/**
*Handlescontextstatechangeevents.
*@paramevtTheevent.
*/
functioncontext_statechange(evt){
// Check if the event was already handled since we're listening for it both ways
if(!evt||!evt.unmute_handled){
// Mark handled
evt.unmute_handled=true;
// The context may have auto changed to some undesired state, so immediately check again if we want to change it
updateContextState();
}
}
addEventListeners(context,["statechange"],context_statechange,true,true);// NOTE: IIRC some devices don't support the onstatechange event callback, so handle it both ways
if(!context.onstatechange)context.onstatechange=context_statechange;// NOTE: IIRC older androids don't support the statechange event via addeventlistener, so handle it both ways
//#endregion
//#region HTML Audio Channel State
/** The html audio element that forces web audio playback onto the media channel instead of the ringer channel. */
// We want to be playing back on the media channel, but can only do so from an allowed input event
if(isMediaPlaybackEvent){
// Create a new channel tag if necessary
if(!channelTag){
vartmp=document.createElement("div");
tmp.innerHTML="<audio x-webkit-airplay='deny'></audio>";// Airplay like controls on other devices, prevents casting of the tag, doesn't work on modern iOS
channelTag=tmp.children.item(0);
channelTag.controls=false;
channelTag.disableRemotePlayback=true;// Airplay like controls on other devices, prevents casting of the tag, doesn't work on modern iOS
channelTag.preload="auto";
channelTag.src=silence;
channelTag.loop=true;
channelTag.load();
}
// Play the channel tag
if(channelTag.paused){
varp=channelTag.play();
if(p)p.then(noop,destroyChannelTag).catch(destroyChannelTag);// If playback fails the tag is pretty much trash and needs to be recreated on next media playback event
}
}
}else{
// We don't want to be allowing playback at all at the moment, so destroy the channel tag to halt playback and hide those silly iOS media controls
destroyChannelTag();
}
}
}
/**
*Completeunloads/destroysthechanneltag.
*/
functiondestroyChannelTag(){
if(channelTag){
// Change src to nothing and trigger a load, this is required to actually hide / clear the iOS playback controls
channelTag.src="about:blank";
channelTag.load();
channelTag=null;
}
}
//#endregion
//#region Input
/** The event types that can trigger media playback. */
varmediaPlaybackEvents=[
"click",
"contextmenu",
"auxclick",
"dblclick",
"mousedown",
"mouseup",
"touchend",
"keydown",
"keyup",
];
/** Tracks if a media playback event has occurred */
varhasMediaPlaybackEventOccurred=false;
/**
*Handleseventsthatcanbeginmediaplayback.
*/
functionwin_mediaPlaybackEvent(){
hasMediaPlaybackEventOccurred=true;
// This is an opportunity to resume the html audio channel control
updateChannelState(true);
// This is an opportunity to resume the context if paused