I would like to know when the audio tag starts downloading so that I can throw up an overlay. Then remove it when it stops, but this does not work in chrome
audio.oncanplay = () => {
loading = false;
};
audio.onloadstart = () => {
loading = true;
};
loading will be true as soon as the page loads.
Is there an event for started downloading, and finished downloading? I need both not just the finished.
I figured it out. You want to use the waiting event. As seen in my fiddle.
let audio = document.getElementById('ad')
let message = document.getElementById('mes')
audio.oncanplay = () => {
message.append ( "done\n")
};
audio.onwaiting = () => {
message.append ( "load\n")
};
message.append ("start\n")
<audio id='ad' controls src='https://html.com/wp-content/uploads/flamingos.mp3' preload="none"></audio>
<h1 id="mes"></h1>
The waiting will fire whenever the audio tag is loading, and canplay will fire whenever the audio tag is again capable of playing.
https://jsfiddle.net/2pdn1k5f/1/
Related
I have a problem with Javascript:
I want to stop the audio in a Js program when the game comes to an end. For this I define a variable "endGame" and I make it false, so that when I call it true in an event, the audio stops. But the problem is that the audio does not stop when this variable is set to true.
Here's the piece of code that I need some help with:
let endGame = false
main.addEventListener("click", (e) => {
themeSong.play()
if(endGame == true) {
themeSong.pause()
}
EventListeners will not be triggered when the variable change. You should do it manually.
let endGame = false;
main.addEventListener("click", (e) => {
themeSong.play();
});
// The function which updates the endGame variable and stop the audio
function fnEndGame() {
endGame = true;
themeSong.pause();
}
// You should call fnEndGame() to stop the game, DO NOT update the variable directly.
By using a custom event, and then throwing that event whenever you want, you can pause your music execution the moment you want to.
So instead of using an "endgame" variable, I suggest you throw a custom event named "supergame_endgame" and listen to that event, and halt all relevant audio's in that event listener.
See my comments in the code below for more explanation.
let audio = document.querySelector('audio');
let start = document.getElementById('play');
let stop = document.getElementById('stop');
/** simple starter of the sound **/
start.addEventListener('click', () => {
audio.play();
});
/** Event handler for own custom event, named `stop_my_audio_play`
* This will actually stop audio play, regardless from where the event `stop_my_audio_play` is
* cast from
*/
window.addEventListener('stop_my_audio_play', (e) => {
audio.pause();
});
/** Throw an event `stop_my_audio_play` with the attribute bubbles set to true
* So it can be caught at window level
*/
stop.addEventListener('click', () => {
stop.dispatchEvent(new Event('stop_my_audio_play', {bubbles:true}))
});
audio {
display:none;
}
<audio controls="controls" autobuffer="autobuffer" autoplay="autoplay" loop>
<source src="data:audio/wav;base64,UklGRhwMAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAZGF0Ya4LAACAgICAgICAgICAgICAgICAgICAgICAgICAf3hxeH+AfXZ1eHx6dnR5fYGFgoOKi42aloubq6GOjI2Op7ythXJ0eYF5aV1AOFFib32HmZSHhpCalIiYi4SRkZaLfnhxaWptb21qaWBea2BRYmZTVmFgWFNXVVVhaGdbYGhZbXh1gXZ1goeIlot1k6yxtKaOkaWhq7KonKCZoaCjoKWuqqmurK6ztrO7tbTAvru/vb68vbW6vLGqsLOfm5yal5KKhoyBeHt2dXBnbmljVlJWUEBBPDw9Mi4zKRwhIBYaGRQcHBURGB0XFxwhGxocJSstMjg6PTc6PUxVV1lWV2JqaXN0coCHhIyPjpOenqWppK6xu72yxMu9us7Pw83Wy9nY29ve6OPr6uvs6ezu6ejk6erm3uPj3dbT1sjBzdDFuMHAt7m1r7W6qaCupJOTkpWPgHqAd3JrbGlnY1peX1hTUk9PTFRKR0RFQkRBRUVEQkdBPjs9Pzo6NT04Njs+PTxAPzo/Ojk6PEA5PUJAQD04PkRCREZLUk1KT1BRUVdXU1VRV1tZV1xgXltcXF9hXl9eY2VmZmlna3J0b3F3eHyBfX+JgIWJiouTlZCTmpybnqSgnqyrqrO3srK2uL2/u7jAwMLFxsfEv8XLzcrIy83JzcrP0s3M0dTP0drY1dPR1dzc19za19XX2dnU1NjU0dXPzdHQy8rMysfGxMLBvLu3ta+sraeioJ2YlI+MioeFfX55cnJsaWVjXVlbVE5RTktHRUVAPDw3NC8uLyknKSIiJiUdHiEeGx4eHRwZHB8cHiAfHh8eHSEhISMoJyMnKisrLCszNy8yOTg9QEJFRUVITVFOTlJVWltaXmNfX2ZqZ21xb3R3eHqAhoeJkZKTlZmhpJ6kqKeur6yxtLW1trW4t6+us7axrbK2tLa6ury7u7u9u7vCwb+/vr7Ev7y9v8G8vby6vru4uLq+tri8ubi5t7W4uLW5uLKxs7G0tLGwt7Wvs7avr7O0tLW4trS4uLO1trW1trm1tLm0r7Kyr66wramsqaKlp52bmpeWl5KQkImEhIB8fXh3eHJrbW5mYGNcWFhUUE1LRENDQUI9ODcxLy8vMCsqLCgoKCgpKScoKCYoKygpKyssLi0sLi0uMDIwMTIuLzQ0Njg4Njc8ODlBQ0A/RUdGSU5RUVFUV1pdXWFjZGdpbG1vcXJ2eXh6fICAgIWIio2OkJGSlJWanJqbnZ2cn6Kkp6enq62srbCysrO1uLy4uL+/vL7CwMHAvb/Cvbq9vLm5uba2t7Sysq+urqyqqaalpqShoJ+enZuamZqXlZWTkpGSkpCNjpCMioqLioiHhoeGhYSGg4GDhoKDg4GBg4GBgoGBgoOChISChISChIWDg4WEgoSEgYODgYGCgYGAgICAgX99f398fX18e3p6e3t7enp7fHx4e3x6e3x7fHx9fX59fn1+fX19fH19fnx9fn19fX18fHx7fHx6fH18fXx8fHx7fH1+fXx+f319fn19fn1+gH9+f4B/fn+AgICAgH+AgICAgIGAgICAgH9+f4B+f35+fn58e3t8e3p5eXh4d3Z1dHRzcXBvb21sbmxqaWhlZmVjYmFfX2BfXV1cXFxaWVlaWVlYV1hYV1hYWVhZWFlaWllbXFpbXV5fX15fYWJhYmNiYWJhYWJjZGVmZ2hqbG1ub3Fxc3V3dnd6e3t8e3x+f3+AgICAgoGBgoKDhISFh4aHiYqKi4uMjYyOj4+QkZKUlZWXmJmbm52enqCioqSlpqeoqaqrrK2ur7CxsrGys7O0tbW2tba3t7i3uLe4t7a3t7i3tre2tba1tLSzsrKysbCvrq2sq6qop6alo6OioJ+dnJqZmJeWlJKSkI+OjoyLioiIh4WEg4GBgH9+fXt6eXh3d3V0c3JxcG9ubWxsamppaWhnZmVlZGRjYmNiYWBhYGBfYF9fXl5fXl1dXVxdXF1dXF1cXF1cXF1dXV5dXV5fXl9eX19gYGFgYWJhYmFiY2NiY2RjZGNkZWRlZGVmZmVmZmVmZ2dmZ2hnaGhnaGloZ2hpaWhpamlqaWpqa2pra2xtbGxtbm1ubm5vcG9wcXBxcnFycnN0c3N0dXV2d3d4eHh5ent6e3x9fn5/f4CAgIGCg4SEhYaGh4iIiYqLi4uMjY2Oj5CQkZGSk5OUlJWWlpeYl5iZmZqbm5ybnJ2cnZ6en56fn6ChoKChoqGio6KjpKOko6SjpKWkpaSkpKSlpKWkpaSlpKSlpKOkpKOko6KioaKhoaCfoJ+enp2dnJybmpmZmJeXlpWUk5STkZGQj4+OjYyLioqJh4eGhYSEgoKBgIB/fn59fHt7enl5eHd3dnZ1dHRzc3JycXBxcG9vbm5tbWxrbGxraWppaWhpaGdnZ2dmZ2ZlZmVmZWRlZGVkY2RjZGNkZGRkZGRkZGRkZGRjZGRkY2RjZGNkZWRlZGVmZWZmZ2ZnZ2doaWhpaWpra2xsbW5tbm9ub29wcXFycnNzdHV1dXZ2d3d4eXl6enp7fHx9fX5+f4CAgIGAgYGCgoOEhISFhoWGhoeIh4iJiImKiYqLiouLjI2MjI2OjY6Pj46PkI+QkZCRkJGQkZGSkZKRkpGSkZGRkZKRkpKRkpGSkZKRkpGSkZKRkpGSkZCRkZCRkI+Qj5CPkI+Pjo+OjY6Njo2MjYyLjIuMi4qLioqJiomJiImIh4iHh4aHhoaFhoWFhIWEg4SDg4KDgoKBgoGAgYCBgICAgICAf4CAf39+f35/fn1+fX59fHx9fH18e3x7fHt6e3p7ent6e3p5enl6enl6eXp5eXl4eXh5eHl4eXh5eHl4eXh5eHh3eHh4d3h4d3h3d3h4d3l4eHd4d3h3eHd4d3h3eHh4eXh5eHl4eHl4eXh5enl6eXp5enl6eXp5ent6ent6e3x7fHx9fH18fX19fn1+fX5/fn9+f4B/gH+Af4CAgICAgIGAgYCBgoGCgYKCgoKDgoOEg4OEg4SFhIWEhYSFhoWGhYaHhoeHhoeGh4iHiIiHiImIiImKiYqJiYqJiouKi4qLiouKi4qLiouKi4qLiouKi4qLi4qLiouKi4qLiomJiomIiYiJiImIh4iIh4iHhoeGhYWGhYaFhIWEg4OEg4KDgoOCgYKBgIGAgICAgH+Af39+f359fn18fX19fHx8e3t6e3p7enl6eXp5enl6enl5eXh5eHh5eHl4eXh5eHl4eHd5eHd3eHl4d3h3eHd4d3h3eHh4d3h4d3h3d3h5eHl4eXh5eHl5eXp5enl6eXp7ent6e3p7e3t7fHt8e3x8fHx9fH1+fX59fn9+f35/gH+AgICAgICAgYGAgYKBgoGCgoKDgoOEg4SEhIWFhIWFhoWGhYaGhoaHhoeGh4aHhoeIh4iHiIeHiIeIh4iHiIeIiIiHiIeIh4iHiIiHiIeIh4iHiIeIh4eIh4eIh4aHh4aHhoeGh4aHhoWGhYaFhoWFhIWEhYSFhIWEhISDhIOEg4OCg4OCg4KDgYKCgYKCgYCBgIGAgYCBgICAgICAgICAf4B/f4B/gH+Af35/fn9+f35/fn1+fn19fn1+fX59fn19fX19fH18fXx9fH18fXx9fH18fXx8fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x8e3x7fHt8e3x7fHx8fXx9fH18fX5+fX59fn9+f35+f35/gH+Af4B/gICAgICAgICAgICAgYCBgIGAgIGAgYGBgoGCgYKBgoGCgYKBgoGCgoKDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KCgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGBgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCAgICBgIGAgYCBgIGAgYCBgIGAgYCBgExJU1RCAAAASU5GT0lDUkQMAAAAMjAwOC0wOS0yMQAASUVORwMAAAAgAAABSVNGVBYAAABTb255IFNvdW5kIEZvcmdlIDguMAAA" />
</audio>
<button id="play">Play</button>
<button id="stop">Stop</button>
I've searched other questions and it seems like this is a problematic area. Unfortunately no one else seemed to have the same issue as me.
I'm essentially trying to trigger an mp3 when a button is clicked. I'm having no problem with desktop browsers but when I try on android I get nothing. I added an alert() just to check that the onclick function was firing and to my surprise after dismissing it the audio plays.
I'm aware that user interaction is required in order to trigger audio but I thought an onclick event would suffice. Is there another gotcha that I am missing?
I've included two snippets to display the difference.
This version works on desktop but not android
let btn = document.querySelector('button')
let audio = new Audio('https://archive.org/download/BirdCall/chrysococcyx-basalis.mp3')
const handleClick = (file) => {
if (file.paused) {
file.play();
} else {
file.currentTime = 0;
}
}
btn.onclick = () => handleClick(audio)
<h1>Working On Desktop</h1>
<button>Bird Call</button>
This version works on android
let btn = document.querySelector('button')
let audio = new Audio('https://archive.org/download/BirdCall/chrysococcyx-basalis.mp3')
const handleClick = (file) => {
alert('Audio will play.')
if (file.paused) {
file.play();
} else {
file.currentTime = 0;
}
}
btn.onclick = () => handleClick(audio)
<h1>Working On Android Chrome</h1>
<button>Bird Call</button>
Any input is greatly appreciated.
That's a strange problem. I tried it on an old android phone and couldn't reproduce the issue (the audio plays without any need for an alert box). I can't see any obvious explanation, but perhaps chrome is waiting for user input before loading the audio file, your device is failing to start loading it quickly enough, then it's hitting a timeout. The alert box might just introduce enough delay. It's a long shot, but you could try adding an extra interaction for the user to load the audio first, like this:
let load = document.querySelector("#load");
let play = document.querySelector("#play");
// different audio file used in this snippet, because archive.org is
// blocked by my ISP ... damn internet censorship
let audio = new Audio("https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3");
play.onclick = () => { audio.play(); }
load.onclick = () => { audio.load(); }
audio.oncanplay = () => {
load.disabled = true;
play.disabled = false;
};
<button id="play" disabled>Play</button>
<button id="load">Load</button>
I have two media elements:
<audio id="aud"></audio>
<video id="vid"></video>
I've attached media urls to both of them.
Then I load and play them by doing audio.load() and video.load() followed by audio.play() and video.play() in listener
audio.load();
video.load();
video.addEventListener("canplay", () => {
video.play();
});
audio.addEventListener("canplay", () => {
audio.play();
});
Both start loading, audio loads first and it takes around 1-2 seconds to video to load.
When they actually start playing depends on when "canplay" event is fired by each media elements. (which is fired after loading is done for at-least a couple of frames)
what I wanna do is, call audio.play() AND video.play() together ONLY when "canplay" event is fired for both of them. How can I achieve this?
note that I DO need to use both audio and video element, and i'm trying to achieve a close but not perfect synchronisation at start.
in brief: what i want to achieve is:
audio.load();
video.load();
// when video can "canplay" and when audio can "canplay" ONLY then
video.play();
audio.play();
Use a global flags:
let audCanPlay=false;
let vidCanPlay=false;
function audPlay() {
audCanPlay=true;
console.log("audCanPlay=true");
if(vidCanPlay) playBoth();
}
function vidPlay() {
vidCanPlay=true;
console.log("vidCanPlay=true");
if(audCanPlay) playBoth();
}
function playBoth() {
console.log("audio.play();");
console.log("video.play();");
}
/* commented for demonstration
audio.addEventListener("canplay", audPlay);
video.addEventListener("canplay", vidPlay);
audio.load();
video.load();
*/
<audio id="aud"></audio>
<button id="audBtn" onclick="audPlay()">Mimic Audio canplay</button>
<video id="vid"></video>
<button id="vidBtn" onclick="vidPlay()">Mimic Video canplay</button>
I think it's better to add the event listeners before calling the load methods.
I think it's better to do it this way, rather than waiting for one to finish/canplay and only then call the second = saves time/faster.
Promises resolved by canplay events and combined with Promise.all to wait for multiple events come to mind.
However, how complex it needs to be also depends on how general purpose it is to be. For the simple case of waiting for exactly one video and one audio to be ready to play, I would only use the one promise and count events as they arrive.
Note that to catch events on elements that retrieve files it is unsafe to set src or href attributes before adding event listeners.
Concept code (untested) to add listeners, set sources and return a promise when both are ready:
const loadAV = (video, vidSrc, audio, audioSrc, timeout) {
let resolve, reject;
const p = new Promise( (r,j) => { resolve = r, reject = j};
let count = 0;
let timer;
const listenCanPlay = () => {
if( ++count == 2) {
resolve();
unListen();
}
};
const unListen = () => {
video.removeEventListener("canplay", listenCanPlay);
audio.removeEventListener("canplay", listenCanPlay);
if( timeout) {
clearTimeout(timer);
}
};
if( timeout) {
timer = setTimeout( ()=> {
if( count < 2) {
unListen();
reject( new Error(`loadAV timed out after ${timeout} msec`)
}
}, timeout);
}
video.addEventListener("canplay", listenCanPlay);
audio.addEventListener("canplay", listenCanPlay);
video.src = vidSrc;
audio.src = audioSrc;
return p;
}
which would then be called like
loadAV( video, video_url, audio, audio_url, 15000)
.then(()=>{ video.play(); audio.play()})
.catch(err=> console.log(err));
I create websocket server in python to handle notification event. Now, i can receive notification, the problem is i can't play sound because new autoplay policy changed, if i play sound using javascript it give me domexception. Any suggestion please ?
As i know, playing sound is simple in html-javascript. like this example: https://stackoverflow.com/a/18628124/7514010
but it depend to your browsers and how you load and play, so issues is:
Some of browsers wait till user click something, then let you play it (Find a way for it)
In some case browsers never let you play till the address use SSL (means the HTTPS behind your url)
The loading be late so the playing be late / or even not start.
So i usually do this:
HTML
<audio id="notifysound" src="notify.mp3" autobuffer preload="auto" style="visibility:hidden;width:0px;height:0px;z-index:-1;"></audio>
JAVASCRIPT (Generally)
var theSound = document.getElementById("notifysound");
theSound.play();
And the most safe if i want sure it be played when i notify is :
JAVASCRIPT (In your case)
function notifyme(theTitle,theBody) {
theTitle=theTitle || 'Title';
theBody=theBody || "Hi. \nIt is notification!";
var theSound = document.getElementById("notifysound");
if ("Notification" in window && Notification) {
if (window.Notification.permission !== "granted") {
window.Notification.requestPermission().then((result) => {
if (result != 'denied') {
return notifyme(theTitle,theBody);
} else {
theSound.play();
}
});
} else {
theSound.play();
try {
var notification = new Notification(theTitle, {
icon: 'icon.png',
body: theBody
});
notification.onclick = function () {
window.focus();
};
}
catch(err) {
return;
}
}
} else {
theSound.play();
}
}
(and just hope it be played. because even possible to volume or some customization make it failed.)
to bypass new autoplay policy :
create a button that can play the sound, hide it and trigger the sound with :
var event = new Event('click');
playBtn.dispatchEvent(event);
EDIT
assuming you have :
let audioData = 'data:audio/wav;base64,..ᴅᴀᴛᴀ...'; // or the src path
you can use this function to trigger whenever you want without appending or create element to the DOM:
function playSound() {
let audioEl = document.createElement('audio');
audioEl.src = audioData;
let audioBtn = document.createElement('button');
audioBtn.addEventListener('click', () => audioEl.play(), false);
let event = new Event('click');
audioBtn.dispatchEvent(event);
}
usage :
just playSound()
EDIT 2
I re test my code and it does'nt work hum ... weird
I have used javascript Audio() before, but now I need to add some reverb effect in the audio and I am using reverb.js which uses the AudioContext api. I have the start property available, but no pause property? How do I pause or stop the audio??
Here is my code:
<script src="http://reverbjs.org/reverb.js"></script>
<script>
// 1) Setup your audio context (once) and extend with Reverb.js.
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
reverbjs.extend(audioContext);
// 2) Load the impulse response; upon load, connect it to the audio output.
var reverbUrl = "http://reverbjs.org/Library/SaintLawrenceChurchMolenbeekWersbeekBelgium.m4a";
var reverbNode = audioContext.createReverbFromUrl(reverbUrl, function() {
reverbNode.connect(audioContext.destination);
});
// 3) Load a test sound; upon load, connect it to the reverb node.
var sourceUrl = "./sample.mp3";
var sourceNode = audioContext.createSourceFromUrl(sourceUrl, function() {
sourceNode.connect(reverbNode);
});
</script>
Play
Stop
Also, I tried using stop(), and it works, but when I fire start() after clicking on stop, the start() doesn't work. Can you you help me out with a solution??
You can use the suspend() and resume() methods of AudioContext to pause and resume your audio: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend
One way to implement this with a single button for play/pause/resume, would be to add a function that controls the player state. For example:
let started = false;
function pauseOrResume() {
if (!started) {
sourceNode.start();
started = true;
document.getElementById("pauseButton").innerHTML = 'Pause';
} else if (audioContext.state === 'running') {
audioContext.suspend().then(function () {
document.getElementById("pauseButton").innerHTML = 'Resume';
});
} else if (audioContext.state === 'suspended') {
audioContext.resume().then(function () {
document.getElementById("pauseButton").innerHTML = 'Pause';
});
}
}
And replace your existing "Play" button with:
<a id="pauseButton" href="javascript:pauseOrResume()">Play</a>
This does the following:
If the audio hasn't yet been started, the link will say "Play".
If the user clicks "Play", the audio will start playing and the text of the link will change to "Pause".
If the user clicks "Pause" while the audio is playing, it will be paused, and the text of the link will change to "Resume".