I'm creating my own HTML5 audio player capable of handling playlists. I have created a custom myPlaylist object with includes all play(), pause(), stop() and other needed functionality. This is all working correctly, but moreover, I need to be aware about when an audio file has ended in order to automatically start playing the next one.
Here's the relevant parts of the code I'm using:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
// Here I want to call to my next() function
});
}
I haven't been able to figure out how to do it. I've tried already several combinations, like $(this).next(), which seems the most reasonable and actually displays the alert, but then does nothing ¿?, also this.next(), which also displays the alert but then shows an error since this refers to the HTML5 audio element, which does not have a next() function.
I've also tried another approach, using
audio.onended = function(){
alert("Song is finished!");
$(this).next();
};
But those do not even trigger the alert. Also audio.ended does not work.
So, I'm basically clueless right now, does anyone have any idea what am I doing wrong? Thanks in advance.
Oh, and I've tested all this in the latest versions of Google Chrome and Safari in Mac OS X.
EDIT Following the advice given in HTML5 audio playlist - how to play a second audio file after the first has ended?, I've also tried the following code
player.addEventListener("ended", function() {
alert("Song is finished!");
$(this).next();
});
And
player.addEventListener("ended", next);
None of them work either, although the first one shows the alert properly.
EDIT 2 Using the search I came across this question, which might also have something to do with my problem, so in order to get rid of any possible troubles with the reference to this, I added a new variable referring to the object itself, so now I'm basically working with:
function myPlaylist(){
var player = document.createElement('audio');
var audio = $(player).get(0);
var me = $(this);
this.next = function next(){
// Picks next song in the playlist and plays it
...
};
$(audio).bind('ended', function() {
alert("Song is finished!");
me.next();
});
}
But then I get an error saying that the Object does not have a method next().
I don't know what else can I try... Any extra information will be highly appreciated, thank you!
there's an HTML5 playlist example handling the ended event here, if that helps?
in your event handler you reference this, but in this context this refers to the DOM element that caught the event, i.e. your audio element.. try this instead:
function myPlaylist(){
var self = this;
var player = document.createElement('audio');
this.next = function (){
// Picks next song in the playlist and plays it
...
};
player.addEventListener("ended", function() {
alert("Song is finished!");
self.next();
});
}
see the MDN for more info on the this keyword
Related
Currently working on a page containing a video that has to be paused at certain points (like chapters). So I made a function that will stop the video when it hits the next "time marker" which looks like this:
function vidPause(nextMarker){
var timeMarker = nextMarker;
if(videoPlayer.currentTime >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', vidPause());
}
};
And I'm trying to fire it this way:
videoPlayer.addEventListener('timeupdate', vidPause(nextMarker));
But it only seems to fire when the video is loaded. Nothing happens when the video is playing (tested by using a console.log(videoPlayer.currentTime); inside the vidPause function).
Note: I need the function to be called that way so that I can remove the event listener when it hits the time marker, that way it won't stop when the user wants to play the video from that point on.
Thanks in advance for any suggestions!
The function is being called once in the addEventListener line, but that's not actually passing it as a callback.
Try this:
function videoUpdate(e) {
vidPause(nextMarker, videoPlayer.currentTime;); // Now call your function
}
function vidPause(nextMarker, timeStamp){
var timeMarker = nextMarker;
if (timeStamp >= timeMarker) {
videoPlayer.pause();
videoPlayer.removeEventListener('timeupdate', videoUpdate);
}
};
videoPlayer.addEventListener('timeupdate', videoUpdate); // Note no brackets, as it's passing a ref to the function rather than calling it
I don't know what the scope of nextMarker is, but you should be able to start console logging and find out.
I know this should be straight forward if I just use the onended event, like this:
<video src="video.ogv" id="myVideo">
</video>
<script type='text/javascript'>
document.getElementById('myVideo').addEventListener('ended',myHandler,false);
function myHandler(e) {
// What you want to do after the event
}
</script>
however, the ended event is firing the evn after I switch tracks. I am using the videojs player.
In this particular case, I'm looking play Clip B, and the moment it finishes, switch to Clip C.
Here is what my code looks like:
// clipA is currently playing......
// User hits a button which calls switchVideo();
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.ended(function () {
console.log("clipB ended");
video.src(clipC);
video.play();
})
};
The moment I call this function, I can see that it jumps to clipB for a moment, then the onended event is fired, and then the video feed jumps to clipC. How can I ignore the first onended event for clipA, and instead only listen for clipB?
UPDATE: Here is what the final correct answer looks like:
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.one('ended', function () {
console.log("clibB ended");
video.src(clipC);
video.play();
})
};
UPDATE 2: I discovered that the above code will only work once. (It is called 'one' afterall. In my particular case, I needed to be able to do this several times.
I made one small change, and that was to use video.on('ended', myfunction) instead of video.one('ended', myFunction). Now I can call it as many times as needed.
I came to this conclusion after reading this Stack Overflow response.
"addEvent" and "removeEvent" were replaced by "on" and "off" per the videojs API. - #Lyn Headley
Final solution:
// video = videojs('video-player');
var switchVideo = function (clipB, clipC) {
video.src(clipB);
video.play();
// When clipB has ended...
video.on('ended', function () {
console.log("clibB ended");
video.src(clipC);
video.play();
})
};
For readers: asker is using a library called videojs, which envelops the native JS-<video> interface. Of which, it provides event functions: on, off, one.
The original code created a new event listener for every time the switchVideo was called, causing the ended callback to be called multiple times.
The solution is to use one('ended') instead of ended [equivalent to on('ended')]:
video.one('ended', function () {
whatever;
});
I'm using the SoundCloud Javascript SDK. Recently, the 2.0 SDK was announced, and there no longer seems to be a 'whileplaying' event. In the following code, the 'whileplaying' handler never gets executed.
SC.stream(sound_id, function(sound) {
sound.play({
whileplaying: function() { console.log('whileplaying'); }
})
);
Am I doing something wrong? If this was taken out of the SDK, is there a workaround?
By looking into the non-obfuscated JS files (the API itself and the soundmanager file), you can find the events you are looking for.
For example:
sound._player.on("stateChange", function(state) {
console.log(state);
});
Or:
sound._player.on("positionChange", function(position) {
console.log(position);
});
I have quickly testes both for HTML5 and Flash tracks.
I'm putting together a Soundcloud audio player for my site and this proved to be a headache: Soundcloud haven't released any docs for the js sdk v2 yet, and it doesn't use soundManager anymore so the streaming audio object is completely different.
Until the docs are available to clarify the situation, you could get by with setInterval or the requestAnimationFrame polyfill to call the audio object's methods (https://developers.soundcloud.com/docs/api/javascript-sdk-2), having stored it in a global variable so you can access it from other functions:
Using setInterval
var updater, scTrack;
function updatePosition(){
// Do stuff, update timers and transport position
var loadedPosition = scTrack.getLoadedPosition(),
currentPosition = scTrack.getCurrentPosition();
console.log('Current: '+currentPosition+', Loaded: '+loadedPosition);
}
SC.stream(sound_id, function(sound){
scTrack = sound;
scTrack.play();
updater = setInterval( updatePosition, 100);
});
And then with whatever handler you have for a pause or stop button:
scTrack.pause();
clearInterval(updater);
Using requestAnimationFrame
( Polyfill available here: https://gist.github.com/paulirish/1579671 )
var updater, scTrack;
function updatePosition(){
updater = requestAnimationFrame(updatePosition);
// Do stuff, update timers and transport position
var loadedPosition = scTrack.getLoadedPosition(),
currentPosition = scTrack.getCurrentPosition();
console.log('Current: '+currentPosition+', Loaded: '+loadedPosition);
}
SC.stream(sound_id, function(sound){
scTrack = sound;
scTrack.play();
updatePosition();
});
For the pause / stop handler:
scTrack.pause();
cancelAnimationFrame(updater);
// This hasn't always cleared the animation request for me so I also set the loop variable to undefined:
updater = undefined;
I'm sure there's a better way of doing it, but it works for now - hopefully the forthcoming docs will clear it up.
Code I am creating is a function which with the function it should .play() it... here is the code
function playSound() {
document.getElementById('newMessage').play();
}
var sound = document.createElement('audio');
sound.setAttribute("src","http://www.soundjay.com/button/beep-2.wav");
sound.id="newMessage";
sound.setAttribute('autoplay','false');
document.body.appendChild(sound);
Though everytime in console trying to do playSound(); it says playSound is undefined. So then I try doing document.getElementById('newMessage').play(); and it doesn't play either, nor does $('#newMessage').play(); which comes with an error of object [object Object] has no method play.
Any suggestions as this is the first time trying to dynamically create the audio file and use a function to play it. I've looked at a few other SO topics and well they don't seem to be leading me in the right direction. Thanks
My guess is you're defining the playSound method after the page has already loaded, maybe in some onload method. If this is the case, try attaching the method to the window object:
window.playSound = function() {
document.getElementById('newMessage').play();
}
This is make the function available even if the function is defined after the page loads. Also you shouldn't set autoplay to false. It defaults to false, and if you want to set it to true you set autoplay="autoplay".
JSFiddle
I'm using the jQuery audio player 'jPlayer', I have a loop which creates a number of jPlayer unique instances then assigns an on click 'jPlayer play' to another part of the document for each instance.. The problem is jPlayer uses an internal ready: function() {} to assign the audio path and load the file.. I need to wait for this function to finish before continuing the loop.
The pseudocode is something like this:
for loop{
create jPlayer div;
instantiate jPlayer and assign audio file in the ready(); // need to wait before continuing
assign an on click event to play file;
}
I'm convinced it's about using queues but I wouldn't know how to implement it? Any help would be most appreciated!
You might try something like this:
function initPlayer(playerQueue){
if(playerQueue.length > 0) {
var player = playerQueue.pop(); // get our options hash for this iteration
var container = $('<div id="'+player.container+'"></div>').appendTo('body');
$(container).jPlayer({
ready: function(){
var _e = this.element;
_e.jPlayer('setFile', player.file);
var timer = setInterval(function(){
if(_e.jPlayer('getData', 'diag.loadPercent') == 100){
$(player.control).click(function(){
// assign your handler
});
clearInterval(timer); // clear the interval
initPlayer(playerQueue); // invoke the next player init
}
},500);
},
/* ... other options ... */
});
}
}
initPlayer([
{container: 'audio-1', file: 'uri/for/file', control: '#button-1'},
{container: 'audio-2', file: 'uri/for/file', control: '#button-2'},
{container: 'audio-3', file: 'uri/for/file', control: '#button-3'},
{container: 'audio-4', file: 'uri/for/file', control: '#button-4'}
]);
Basically we get rid of the explicit loop and instead use the ready function itself to advance the build iteration via the initPlayer function. By usin an array we can use pop() which will get us that last element of the array while also removing it from the array. We wait to invoke initPlayer until after we get a load percent of 100 by polling the load percent of the player every 500 ms.
This is only a suggestion and may need a lot of work... ive never used jPlayer and am flying blind from the documentation so dont expect it to work out of the box :-).