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...
Related
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
What I'm trying to do: Create a list of items which contains a link to play audio from a youtube video beside each item
What I'm currently doing: I'm able to do this for a single item using the below:
<div data-video="VIDEO_ID"
data-autoplay="0"
data-loop="1"
id="youtube-audio">
</div>
<script src="https://www.youtube.com/iframe_api"></script>
<script src="https://cdn.rawgit.com/labnol/files/master/yt.js"></script>
This creates a play button and works perfectly on a single item, however will not work with multiple items on the same page presumably since they all use the same ID. Creating a different ID causes the player to not function. Creating two different data-video's with the same ID will only allow the first one to play, the other will appear correctly but not operate when pressing play.
Looking for solution: Preferably how to use the existing script for multiple videos on the same page. Otherwise an alternative solution for playing audio only on youtube videos with a custom play button would be great.
Thanks!
If you want you can just copy paste your code like this
https://jsfiddle.net/8wkjqf3m/
and it works, I'm not sure if you were having a problem doing that or if your problem was elsewhere. It is of course very sloppy looking and the code should be reworked so that you don't have to hard code every function for every video.
I tried to do everything dynamically and failed. I'm not sure if it is possible to dynamically make a function that makes a "new YT.player" for every video id you have and also have the onPlayerReady and onPlayerStateChange functions dynamically made to go with it. I'm sure there is some way but I couldn't figure it out.
The idea though is to make multiple "youtube-audio" divs with different ids for however many youtube players you want to have and have matching multiple "youtube-player" divs for the iframe to function. You can generate that part with javascript if you want so that you don't have to pollute your code with a bunch of repetitive html.
You can make all of your ids dynamically too.
var array = ['put a video id here','put a video id here','put a video id here'];
array = array.map(function(element,index) {
var div = document.createElement('div');
div.setAttribute('id', 'youtube-audio-' + index);
return {'videoId': element, 'div': div };
}
You can just have an array holding the youtube video ids and then initialize all of the divs data-video attributes using
document.getElementById("youtube-audio-1").dataset.video = //youtube video id
I don't see the point in doing all of that dynamically though if there is no way to get around copy pasting x number of "new YT.player"s and "onPlayerReady"s etc...
Good Luck, let me know if I was in the right area or if that was not what you wanted
I have modified the second script so it works as you (or I) want.
To use it use classes instead of IDs. Like the following:
<div data-video="NQKC24th90U" data-autoplay="0" data-loop="1" class="youtube-audio"></div>
<div data-video="KK2smasHg6w" data-autoplay="0" data-loop="1" class="youtube-audio"></div>
Here is the script:
/*
YouTube Audio Embed
--------------------
Author: Amit Agarwal
Web: http://www.labnol.org/?p=26740
edited by Anton Chinaev
*/
function onYouTubeIframeAPIReady()
{
var o= function(e, t)
// This function switches the imgs, you may want to change it
{
var a=e?"IDzX9gL.png":"quyUPXN.png";
//IDzX9gL is the stopped img and quyUPXN the playing img
t.setAttribute("src","https://i.imgur.com/"+a)
// folder or web direction the img is in. it can be "./"+a
};
var counter = 0;
var bigE = document.querySelectorAll(".youtube-audio");
bigE.forEach(function(e)
{
var t=document.createElement("img");
t.setAttribute("id","youtube-icon-"+counter),
t.style.cssText="cursor:pointer;cursor:hand",
e.appendChild(t);
var a=document.createElement("div");
a.setAttribute("id","youtube-player-"+counter),
e.appendChild(a);
t.setAttribute("src","https://i.imgur.com/quyUPXN.png");
e.onclick=function()
{
r.getPlayerState()===YT.PlayerState.PLAYING||r.getPlayerState()===YT.PlayerState.BUFFERING?(r.pauseVideo(),
o(!1, t)):(r.playVideo(),
o(!0, t))
};
var r= new YT.Player("youtube-player-"+counter,
{
height:"0",
width:"0",
videoId:e.dataset.video,
playerVars:
{
autoplay:e.dataset.autoplay,loop:e.dataset.loop
},
events:
{
onReady:function(e)
{
r.setPlaybackQuality("small"),
o(r.getPlayerState()!==YT.PlayerState.CUED, t)
},
onStateChange:function(e)
{
e.data===YT.PlayerState.ENDED&&o(!1, t)
}
}
})
counter++;
});
}
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
I am trying to save the volume of a playing music file in a cookie so when the page is reloaded, the volume the user chose last is maintained, rather than turning up super loud or whatever.
Here's my test code for the eventlistener:
var myAudio = document.getElementById("audio1");
myAudio.addEventListener('change',alert("Audio Volume Changed"),true};
However, it does not respond when I change the volume. I've searched and despite it being something I think is pretty practical, there's no information on it.
You're looking for the "volumechange" event.
var audio = document.getElementById('sample');
audio.addEventListener('volumechange', function() {
console.log('changed.', arguments);
}, false);
I'm using the bubble phase(as opposed to capture) in this example.
http://jsfiddle.net/426E6/
http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#event-definitions
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