I have a simple program where I run this script:
function PlayAudio(Location){
var audio = new Audio(Location);
audio.Play()
}
in an onclick HTML attribute. I have a fancy loading picture that I know how to make appear and have successfully made it work. I would like to show this picture while the audio is loading, and then make it go away after the audio file is done loading and ready to play. Right now, there is a considerable delay between clicking the element and hearing the sound.
My trouble is knowing when the audio is done loading and ready to play. I figure the best way to know when it's complete loading is to preload the file in-script. I don't know how to do that.
And that's my question:
How do you preload files inside a script?
Or, is there a way to know when an audio file is finally playing?
Note: I cannot use an <audio> element, unless there is some way to nest a <div> inside the <audio> so that the sound in the <audio> is triggered by clicking anywhere in the content of the <div>
Sorry for my slightly confusing descriptions!
Thanks,
Lucas N
You can use canplaythrough event
Sent when the ready state changes to CAN_PLAY_THROUGH, indicating that
the entire media can be played without interruption, assuming the
download rate remains at least at the current level.
function PlayAudio (Location){
var audio = new Audio;
audio.oncanplaythrough = function(event) {
// do stuff, e.g.; set loading `img` `style` `display` to `none`
this.play();
}
audio.src = Location;
}
You change its preload attr by
. audio.preload = "auto";
It will load the whole audio file on start
Related
I have an mp3 from an external URL via <audio></audio> tag.
As on mobile it takes a few seconds to download before being ready to play, I've added a spinner which reveals the player only once the mp3 is ready to play - so nobody is going to click on play if there's no sound yet.
Everything works, except an ENORMOUS NETWORK PAYLOAD, as the page finish to load only when the first bytes of the mp3 are dowloaded and therefore is ready to play (the mp3 comes in ranges of data - partial content code 206).
So, to avoid the ENORMOUS NETWORK PAYLOAD, I'm thinking to lazy load the mp3, or maybe the whole player, using intersection observer, as the mp3 audio tag is below the fold. But then the mp3 is not downloaded at all.
I've placed the intersection observer after the audio tag is revealed (after the loading spinner is gone), but the mp3 is not downloaded. I've tried everything everywhere, but no success.
I just started to learn some javascript, and I'm not even sure if using the intersectin observer to load the page first, and then the mp3, is a good idea in this case. And anyway I can't make it work, so any help or suggestion is much appreciated. Thank you.
Here's the code:
<div class="player">
<div class="hide animated" id="player"> {# spinner #}
<audio data-src="https://some.com/track.mp3"></audio>
</div> {# spinner #}
<div class="circle-spin" id="loaderSpinner" role="status"></div> {# spinner #}
</div>
--
// SPINNER
window.addEventListener("load", function(){
document.getElementById("loaderSpinner").style.display = "none";
document.getElementById("player").classList.remove('hide');
});
// On the document's DOMContentLoaded event, this script should queries the DOM for audio tag -- so, I guess
document.addEventListener("DOMContentLoaded", function() {
const mp3 = document.querySelector('audio');
const io = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
entry.target.src = entry.target.getAttribute('data-src');
io.unobserve(entry.target);
})
});
io.observe(mp3);
});
// And here's some customization for audio player controls which starts with load sound via <audio tag
const audioElement = document.querySelector("audio");
const audioCtx = new AudioContext();
const track = audioCtx.createMediaElementSource(audioElement);
// follows Player controls and attributes...
// and finally an event listener that waits for the page to load etc etc
window.addEventListener("load", () => {
// Set times after page load
setTimes();
// etc etc etc...
I think the problem comes from the fact that your observer will never trigger because your audio has no source.
The audio tag is not even added to the DOM if there is no valid src from the tests I quickly ran.
Also, what you are trying to achieve is already baked in the audio tag. You can use the preload attribute to tell the browser if/how it should preload the audio stream.
Values can be:
auto The author thinks that the browser should load the entire audio file when the page loads
metadata The author thinks that the browser should load only metadata when the page loads
none The author thinks that the browser should NOT load the audio file when the page loads
So, for you, I think you'd like none or metadata.
Parts of the answer come from W3CSchools
Okay, I'm losing my mind here.
I'm trying to code a very simple player just for myself -- something crude but functional.
<button onclick="javascript:PlayAudio();">Play</button>
<script>
var audio = new Audio();
audio.src = "file.mp3"; // this file is in the same directory as the html page
var PlayAudio = function()
{
audio.play();
};
</script>
Should work, right? I know it's not the BEST way to do it, but here's the thing: I've rewritten this code a couple hundred times and nothing seems to be working. There aren't even any error codes/exceptions/whatever that I can find. The browser says it's loaded the file just fine. What's even weirder is when I check the paused member in the audio object, no matter how many times I call the play() method, it still returns true.
When I load the page just as a file in my browser, lo and behold, it plays! Just fine! But if I were to change the onclick event to audio.play();, it doesn't work anymore. I want to run this on a server though.
I promise you there is no additional code. No JQuery, no weird server plugins (not even PHP!). Just Apache, Windows, nothing else.
And I know the browser can play the audio because when I copy audio.src and go to the address, it'll play just fine. Even the protocol is fine; the HTTP:/// address is not trying to load the File:/// address and vice versa. (I need the audio to play as a DOM so I can randomize the audio file later on; I'm just trying to get my browser to play one stinkin' file in the first place.)
I know I can do this in HTML with some JavaScript, but I know this can work in pure javascript too (ignoring the <button>) because I've done this before a LONG time ago. So what changed?
I've also tried to load the definitions using window.onload, but that doesn't work neither.
So... what the heck? I'm am stupid or something? I can accept that; I just need to know.
I think it's because of the path to the mp3 file. Also, separate your HTML from JavaScript code like so:
HTML
<button id="btn">Play</button>
JavaScript
const btnSound = document.querySelector('#btn');
btnSound.addEventListener('click', () => {
const sound = new Audio('./file.mp3') // assuming it's in the directory
sound.play();
});
If you're trying to create a dynamic audio element in pure Javascript...
Create a div on your document to act as container for the dynamic tag
in JS, create the audio element then add to page (via adding it to Div container)
Then you can try a code setup like this...
<div id="container">
<button onclick="PlayAudio('file.mp3')">Play</button>
<div>
<script>
//#create new audio tag
var myAudioElement = document.createElement( "audio");
myAudioElement.setAttribute("controls", "true" );
//myAudioElement.setAttribute("id", "myAudioTag"); //# if you'll need access "by ID"
//# add element to page DOM
document.getElementById('container').appendChild( myAudioElement );
function PlayAudio ( inputURL) //# input is a String like "file.mp3"
{
myAudioElement.setAttribute("src", inputURL);
myAudioElement.play();
}
</script>
Note: To run the playAudio() function without clicking a button just call:
PlayAudio ( "someOtherFile.mp3" );
PS: Below is an example of a "better" approach. Better here means less headaches (more intuitive) but it uses the HTML that you want to avoid. Notice no .src is specified because you can still use JS to update such property by code.
<!DOCTYPE html>
<html>
<body>
<h1>Test Audio Playback </h1>
<audio id="myAudioTag" controls> <source src=" " type="audio/mpeg"> </audio>
<br>
<button onclick="PlayAudio();"> Play </button>
</body>
<script>
var audio = document.getElementById("myAudioTag");
audio.src = "file.mp3"; // this file is in the same directory as the html page
function PlayAudio() { audio.play(); }
//# call this function whenever track must be changed
//# example use: changeAudio( "https://example.com/files/song2.mp3" );
function changeAudio( inputURL) //is a String of some other mp3 file
{
audio.src = inputURL;
audio.play();
}
</script>
</html>
Write your code like this
<button onclick="PlayAudio();">Play</button>
<script>
var PlayAudio = function()
{
var audio = new Audio("file.mp3")
audio.play();
};
</script>
If it still does not work then also try to write onClick
I am trying to view a video stream from an IP camera in a web page, when the stream can be played I want it to start automatically. Trying to do that with a timer, try to play and if that fails, try again.
The timer (timeout) doesn't seem to do that, however if I execute the script using a button, it does. What am I missing?
see the code below.
thanks,
Ron
PS: I commented out the setTimeout functions, to make the button work.
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
function playVid() {
var videoElem = document.getElementById("IPcamerastream");
var playPromise = videoElem.play();
// In browsers that don’t yet support this functionality playPromise won’t be defined.
if (playPromise !== undefined) {
playPromise.then(function() {
// Automatic playback started!
videoElem.controls = true;
}).catch(function(error) {
// Automatic playback failed.
// setTimeout(playVid, 1000);
});
}
}
//setTimeout(playVid, 1000);
</script>
<button onclick="playVid()" type="button">Play Video</button><BR>
<video id="IPcamerastream" src="http://192.168.2.8:8080" width="960" height="540"></video>
</body>
</html>
Look into the features of the video html5 tag:(https://www.w3schools.com/tags/tag_video.asp)
one of the optional attributes is autoplay (Specifies that the video will start playing as soon as it is ready) so there is no need to set timeout.
<video id="IPcamerastream" src="http://192.168.2.8:8080" width="960" height="540" autoplay></video>
Move your script below the video element, and you should not need the timeout at all, because the element will already be initialized when the script is executed. So document.getElementById should resolve the element right away.
Using timers will introduce race conditions. If anything, you should add a listener to the DOMContentLoaded event.
Welcome Ron,
This is a well formatted question, but target browser info could also assist in helping you resolve your issue. Please consider adding it in future!
As for your problem, you tell us that you wish the video to autoplay, I'm assuming on page load?
I've also removed the duplicate source paste.
In this case, you only call playVid() from within the promise erroring out. The initial call is bound to the button click event.
In short, only clicking the button will initiate the playVid() function.
You need to add an event listener for DOM readiness and call playVid() within that. Otherwise, it's only being called when you click your button!
document.addEventListener('DOMContentLoaded', (event) => {
//the event occurred
playVid();
});
You can also use the autoplay HTML option in the video tag.
lang-html
<video {...} autoplay />
I had an almost similar problem ، when I received the video stream in a webrtc project, the video would not be displayed without clicking a button.
if you want to play automatically received stream video, you should add "muted" in the video tag.
I have an extension for chrome I am working on, I have an HTML popup that has buttons that play audio. I don't think my approach is the most elegant and I am having trouble exploring ways to shrink this down. I know this is very inefficient. I want to be able to tell which button was clicked on the HTML page, then using that ID play the audio file with that same ID. The way I have it now is using the EventListener in javascript to use multiple functions to stop or play. Is there a way to put all of this in one function through javascript or jquery to fix this chaos? Since I am doing this on a chrome extension I cannot use Javascript in the HTML document.
Currently I have my HTML like this;
<script type="text/javascript" src="js/audio.js"></script>
Stop Audio
Audio 1
Audio 2
Audio 3
My JS file like this to create the variables for my audio files.
var aud1 = new Audio();
var aud2 = new Audio();
var aud3 = new Audio();
Along with this function to start playback when a button is selected with the coresponding name.
function aud1play() {
aud1.src = "mp3/aud1.mp3";
aud1.play();
}
document.getElementById('aud1').addEventListener('click', aud1play);
//you get the trend
To stop my audio I have the following function:
function audstop() {
aud1.pause;
aud2.pause;
aud3.pause;
document.getElementById('stopbutton').addEventListener('click', audstop);
var audios= Array.prototype.map.call(document.getElementsByClassName("myButton"),function(el){
var audio=new Audio();
audio.preload="none";//=> not preloaded
audio.src="mp3/"+el.id+".mp3";
el.onclick=audio.play.bind(audio);
return audio;
});
Simply iterate over your buttons, create an audio element with the right src for it, and bind the onclick listener to its play function. Then map the audio elements.
document.getElementById('stopbutton').addEventListener('click', function(){
audios.forEach(audio=>audio.pause());
});
To stop, simply iterate over the audio elements and call the stop function on each...
I'm getting different results i Firefox and Chrome when using <audio> and <video> with preload="none" and then trying to play from Javascript.
Let's say i was using preload="auto" or preload="metadata" :
audio.src = "filename";
audio.play();
That seems to work fine in both Firefox and Chrome but i want to use preload="none" and then Chrome dossent play.
So i'm trying this code with preload="none" :
audio.src = url;
audio.load();
audio.addEventListener('canplay', function(e) {
audio.play(); // For some reason this dossent work in Firefox
}, false);
audio.play(); // Added this so Firefox would play
I don't know if that's the correct way to do it.
I'm using :
Firefox 20.0.1
Chrome 25.0.1364.172 m
I made a demo : http://netkoder.dk/test/test0217.html
Edit :
In the 2nd audio player (on the demo page) it seems that when using preload="none" you have to use load().
But is it correct to just use play() right after load() or is the correct way to use an event to wait for the file to load before playing it ?
In the 3rd audio player it seems Firefox 20.0.1 dossent support the canplay event correctly when used with addEventListener() because it dossent trigger after load(), it triggers after play() and also triggers when scrubbing though the sound which dossent seem to be the way the canplay should work.
Using .oncanplay does work.
So the following code seems to work :
function afspil2(url) {
afspiller2.src = url;
afspiller2.load(); // use load() when <audio> has preload="none"
afspiller2.play();
}
function afspil3(url) {
afspiller3.src = url;
afspiller3.load(); // use load() when <audio> has preload="none"
//afspiller3.addEventListener('canplay', function() { // For some reason this dossent work correctly in Firefox 20.0.1, its triggers after load() and when scrubbing
// afspiller3.play();
//}, false);
afspiller3.oncanplay = afspiller3.play(); // Works in Firefox 20.0.1
}
I updated the demo to include the changes : http://netkoder.dk/test/test0217.html
My way of adding addEventListener inside the afspil3() function dossent seem good because the first time the function is called the code inside addEventListener is called 1 time. The second time the function is called the code inside addEventListener is called 2 time and then 3 times and so on.
It's because your audio tags are missing the required src attribute, or <source> tags. When I added them in your demo, all 3 players immediately began working in both Chrome and FF.
Also, I recently discovered that src cannot be an empty string and subsequently changed with JS. If there's a reason you can't set the src in the HTML, your best alternative, IMO, is to create the audio elements with Javascript:
var audio = new Audio();
audio.src = url;
audio.controls = true;
audio.preload = false;
// and so on
Edit: Ok. It seems that in Chrome, when the HTML is preload="none" it is necessary to call load() before playing when the src is changed. Your second audio doesn't preload, so your function needs to be this:
function afspil2(url) {
afspiller2.src = url;
afspiller2.load(); // add load call
afspiller2.play();
}
Then, it seems that in Firefox, it is necessary to set preload="auto"; when attaching an event to the canplay event, like in the 3rd function.
function afspil3(url) {
afspiller3.src = url;
afspiller3.preload = "auto";
afspiller3.load();
afspiller3.addEventListener('canplay', function(e) {
this.play(); // For some reason this dossent work in Firefox
}, false);
}
That just seems very strange, but I tested it multiple times, and each time it would play if preload="auto" is called, but would not play if it isn't called. Note that it wasn't necessary for the 2nd player, which was also preload="none" in the HTML tag.
Finally, Chrome has some odd behaviors if there are multiple <audio> elements on the page. For all three players, reloading the page and clicking "the big electron" link would play correctly.
Reloading and then clicking "Yoda" on the 2nd or 3rd player won't do anything, but it WILL play for the first player. But, if the top player is played first by any means - play button or either link - then the other two "Yoda" links will suddenly work.
Also, if you click a 2nd or 3rd "Yoda" link first after reload, and then click the top player, the previously clicked "Yoda" (that didn't previously play) will begin to play on its own after the top player stops.
Suffice it to say they have some bugs to work out.
The correct way in my opinion would mean using an existing solution, like http://mediaelementjs.com/
If your really interested in the details on the best way to play audio and video with js then look at the source:
https://github.com/johndyer/mediaelement/tree/master/src/js
https://github.com/johndyer/mediaelement/blob/master/src/js/me-mediaelements.js