I'm trying to preload audio files for a game. I'm using Jplayer. I have an overlay that gets removed once all audio has been preloaded. In Firefox, sometimes canplaythrough gets fired, sometimes not. I have five audio files, sometimes I get three canplaythrough events, sometimes four, not often do I get all of them. If I log which ones work, the event isn't always fired, or not, on the same audio files.
I've tried to break the code as much as possible. Here I am adding a new Jplayer instance for each audio file, I still get the same problem.
{
for(var i = 0; i < _timeOutAudioFilesToLoad; i++){
c = i + 1;
var elid = "timeOutAudio"+c;
var elt = '<div id="'+elid+'" class="audioPlayer audio-player" data-audio-file="/themes/foo/sounds/time'+c+'.mp3"></div>';
$("#jPlayers").append(elt);
$("#"+elid).jPlayer( {
swfPath: "/themes/foo/js/libs/jPlayer250/Jplayer.swf"
});
}
}
{
$(".audioPlayer").each(function(){
var audioFile = $(this).attr("data-audio-file");
$(this).bind($.jPlayer.event.canplaythrough, function(event) {
_loadWatcher();
});
$(this).jPlayer("setMedia", {mp3: audioFile});
})
}
The _loadWatcher() function gets called by some, but not others.
Am I doing something wrong? Or is this a bug with a workaround? I've tried the Jplayer Google Group, but for some reason, they're taking days and days to mod questions.
Thanks
My problem was that I was binding the event before instantiating jPlayer on the element. I didn't realise that jplayer was actually waiting for a custom event - not the standard HTML5 event - so I thought binding anywhere would be OK. You live and learn. So switching the order of the last two lines in the for loop fixed the problem.
Related
I'm using the Howler js library to set a player in an app running through Electron. First everything seemed to work well, but after a few weeks using the app, a bug occurs repeatedly, but not constantly : the pause() function doesn't work. Here's some piece of code :
Initialization :
var is_paused = true;
var currentTrack = "track1";
var tracks = {"track1" : new Howl({urls: ['path/to/track1.mp3']}),
"track2" : new Howl({urls: ['path/to/track2.mp3']}),
"track3" : new Howl({urls: ['path/to/track3.mp3']})
};
Then I have a few buttons for play/resume, pause, stop, and play a specific track :
$('#playS').click(function(){
if (is_paused){
tracks[currentTrack].play();
is_paused = false;
}
});
$('#pauseS').click(function(){
tracks[currentTrack].pause();
is_paused = true;
});
$('.trackBtn').click(function(){
tracks[currentTrack].stop();
currentTrack = $(this).attr('id');
tracks[currentTrack].play();
is_paused = false;
});
The problem is that sometimes (generally after 40-45 min of a track playing), the pause() function just do nothing, which is really annoying cause I need to pause the track and play another 30 sec file and then resume the current track. I checked the console while the bug occurs, it says absolutely nothing. I have no idea where the bug comes from, there's not a lot of information about how works the library. I really need some help here, thank's in advance.
EDIT : one more thing, when pause() doesn't work, if I click play() the track plays from the begining, and I have control on this second instance of the track. It's like the first instance has reached end, but still playing.
Without knowing what version of Howler you're using, or what other code might be messing things up, there is one thing I think might help: You don't need to track the paused state. The "playing" method takes care of that. I've made it work using something like this:
// If it's paused, it's not playing, so...
paused = !myHowlInstance.playing();
Another thing I noticed is that you have currentTrack = $(this).attr('id'); in your (I think it's a) stop button. Unfortunately I don't know JQuery well enough to know if there's anything wrong with that (I'm more of a Dojo fan myself). But it looks like currentTrack may be set to some value not in your list (which would break tracks[currentTrack]). You might want to go into the console and type tracks["track1"], currentTrack etc. to see their values. You can even do tracks[currentTrack].play(); and see what happens. I wasn't sure if you knew you could do that (it was a huge help to me when I found out about it).
And as far as the "un-pausing" starting from the beginning, I'm currently struggling with it myself; at this time there's no clear way to do this (no resume(), pause(false) etc.), and I've seen a few more questions on the subject on Google and SO, so you're not alone. I've experimented with the seek method, but with no luck. I'll post a comment if/when I reach a breakthrough on that. :)
EDIT: I figured out the play-from-beginning thing. It does involve "seek", and also the whole "instance ID" concept (which I never really understood the importance of from the documentation).
Here's an example from a project I'm working on (also a game); it doesn't involve JQuery (sorry), but it should give you the gist of how to fix the problem.
var myBgMusic = new Howl(...);
var myMusicID = myBgMusic.play(); // The docs say play() returns an ID, and I'll be passing that to "seek" later.
var paused = false;
var saveSeek;
function TogglePause() {
if (paused) {
myBgMusic.play(myMusicID);
myBgMusic.seek(saveSeek, myMusicID);
} else {
myBgMusic.pause();
saveSeek = myBgMusic.seek(myMusicID);
}
};
I want to play a single audio file (mp3) and my only problem is media length.
It works just fine on Android 5.0.1, but on 4.4.2/4.4.4 it doesn't work!
With native implementation I get a value but it's incorrect and if I use the Media plugin API (from Phonegap) the media.duration is undefined and media.getDuration() returns -1.
I'm trying to get duration only after loadedmetadata event is fired, so this could not be the problem.
The native implementation is done through js with new Audio(), no DOM element involved.
The file is stored on sdcard, and src looks like file:///storage/sdcard/audio.mp3. Everything else regarding html5 audio api works, but duration.
Are there any solutions to fix this?
Thanks to #tawpie's answer I figured out a workaround for this issue I'm having.
That setInterval made me thing about my custom seekbar been updated (correctly) while the audio is playing and in calculating the width of it I was using audio duration value and from that results that the duration is working after media file play method is fired.
The problem is that loadedmetadata event doesn't return the correct duration value (in some browsers like android webView), but after audio played for at least 1s the duration is updated and you can use it.
So you can forget about loadedmetadata event and jump straight to canplay event and from there you can make something like this:
var myAudio = new Audio();
myAudio.src = 'file://mnt/sdcard/audio.mp3';
myAudio.load();
myAudio.correctDuration = null;
myAudio.addEventListener('canplay', function(){
myAudio.play();
myAudio.muted = true;
setTimeout(function(){
myAudio.pause();
myAudio.currentTime = 0;
myAudio.muted = false;
myAudio.correctDuration = myAudio.duration;
},1000);
});
...of course, you can use volume = 0.0/1.0 instead of mute.
Another method would be to create a helper function (in my case - a AngularJS service) which takes your src value and uses the code above and returns the correctDuration. This one is preferred if you have listeners to audio timeUpdate which changes the DOM.
The Media plugin works exactly the same way - if the audio haven't played for at least 1s you cannot use getDuration() method or duration property inside a interval/timeout wrapper to get the correct duration.
I think the video element behaves similarly. I'll test it these days.
Hope this workaround helps!
Try Media.node.duration. That works on windows... For what it's worth, as long as getDuration is called in an interval, I don't have any problems on Android 4.4. But I'm using just the media plugin new Media(src, onSuccess, onError, playbackStatus) and not the HTML5 player.
Hardcoded values. It's a pain, but you can do this if the files are local.
I ran into an issue where chrome was reporting different duration values than other browsers, and this is where we landed. I know it's not really a solution, but it works.
OR... you can use some external process to generate a json of duration times, and reference those values at runtime.
For the sake of reference:
audio.addEventListener('durationchange', function(e) {
console.log(e.target.duration); //FIRST 0, THEN REAL DURATION
});
worked for me.
Credit: this stackowerflow question
I'm getting the audio/video duration of a file without appending it to the screen. "Using the same code", when I try to get the video duration on both sides it works as expected. But when using audio files it says that the duration is 0 on Android, but it works on a desktop computer.
// Only working on Desktop
var audio = new Audio(url);
// Hide audio player
// player.appendChild(audio);
audio.addEventListener('loadedmetadata', function() {
alert(audio.duration);
});
And the below code is working:
// Working on Desktop and Android
var video = document.createElement('video');
video.src = url;
// Hide video
// player.appendChild(video);
video.addEventListener('loadedmetadata', function() {
alert(video.duration);
});
There is a different approach you can try but, if duration doesn't work with your device (which IMO is a bug) then it's likely this doesn't either; worth a shot though:
audio.seekable.end(audio.seekable.length-1);
or even
audio.buffered.end(audio.buffered.length-1);
though the latter is dependent on content being loaded which in this case probably then won't help.
EDIT: Using the durationchange event is much easier. First the 0 is being output, but as soon as the file is loaded (that's where loadedmetadata fails I guess) the updated and real duration will be output.
audio.addEventListener('durationchange', function(e) {
console.log(e.target.duration); //FIRST 0, THEN REAL DURATION
});
OLD WAY (ABOVE IS MUCH FASTER)
Looks like this "bug" (if this is actually a real bug) is still around. Chrome (40) for Android still outputs 0 as the audio files duration. Researching the web didn't get me a solution but I found out the bug also occurs on iOS. I figured I should post my fix here for you guys.
While audio.duration outputs 0, logging audio outputs the object and you can see that the duration is displayed just right there. All this is happening in the loadedmetadata event.
audio.addEventListener('loadedmetadata', function(e) {
console.log(e.target.duration); //0
});
If you log audio.duration in the timeupdate event though, the real duration is being output. To only output it once you could do something like:
var fix = true;
audio.addEventListener('timeupdate', function(e) {
if(fix === true) {
console.log(e.target.duration); //REAL DURATION
fix = false;
}
console.log(e.target.currentTime); //UPDATED TIME POSITION
});
I'm not sure why all this is happening. But let's be happy it's nothing serious.
sounds["foo"]= new Audio("foo.ogg");
function playSound(id){
var snd = this.sounds[id];
if(snd.currentTime>snd.duration*0.99999){
snd.pause();
snd.currentTime=0;
//snd.seekable.start(); //doesnt work
//snd.load(); does the trick but with the cost re-downloading the clip every single time
snd.play();
}
if(snd.currentTime==0)
snd.play();
}
For some reason playSound('foo'); works on the first time, but fails after that on Chrome (works just fine on Firefox). Adding snd.load() seemed to fix this, but now it downloads the clip from the server every time the clip is played, which is a lot in my use case.
EDIT: Oh, and snd.currentTime seems to get stuck at the end, so snd.currentTime=0 does nothing.
The following script works just fine for me on OSX, Chrome Version 32.0.1700.102 and Firefox 27.0.
If you do not want to loop the file immediately, your script definitely is the right approach.
I couldn't reproduce your issues. I associated the method with an onclick action on a link and used a 5s .ogg file for testing. Clicking the link and re-playing the audio file worked for me all the time.
The only possible issue I noticed is your second if clause. If the first if is matched, the time is set to 0 and the file will be played. Even if it is very unlikely in my opinion, it could be that the second if is also matched and snd.play() is invoked a second time. I therefore would use else if.
Can you give it a try? Even though I suppose it does not solve your problem :/
<script type="text/javascript">
var sounds = [];
sounds["foo"]= new Audio("foo.ogg");
/* First solution, creating a loop */
function playSoundLoop(id){
var snd = this.sounds[id];
snd.play();
snd.addEventListener('ended', function(){
this.currentTime = 0;
this.play();
}, false);
}
/* Use else if instead of if */
function playSound(id){
var snd = this.sounds[id];
if(snd.currentTime>snd.duration*0.99999){
snd.pause();
snd.currentTime=0;
snd.play();
} else if(snd.currentTime==0) {
snd.play();
}
}
</script>
I'm trying to control html5 video with javascript. What I want is that when the user clicks on a button, the video will jump to another frame and keep playing from there. With my current code the playback always stops after the seek.
function jumpToTime(){
var video = $("video")[0];
video.currentTime = 160;
document.getElementbyId("videoclip").play(); //not working
};
//the following code is probably not really related to the question:
var endtimeoffset = 2000;
video.addEventListener("timeupdate", function() {
if (video.currentTime >= endtimeoffset) {
video.pause();
}
}, false);
I ran into a similar problem, and found a solution by pausing the video, then setting the currentTime, then playing the video. To update your code:
function jumpToTime(){
var video = $("video")[0];
video.pause();
video.currentTime = 160;
video.play();
};
Some things I would try:
in the jumpToTime() function, you have two different references to supposedly the same video (one obtained through jQuery and the other by getElementById()). Are you sure these reference the same video? To be safe, I would just call play() on the 'video' reference that you set the currentTime on.
This is probably a copy and paste issue since the console would complain if this was in the actual code, but you did mispell getElementById() (Need to capitalize the B).
For debugging purposes, I would comment out the 'timeupdate' event code, to make sure this isn't the issue and that this code isn't pausing the video after you update the timehead or call play. It probably isn't, since you are setting the current time to be much less than the offset you are comparing it with. It would, however, be an easy test to eliminate this as a possible cause of the issue.
function jumpToTime(){
document.getElementById("videoclip").currentTime = 160;
document.getElementById("videoclip").play(); //not working
};
getElementbyId --> getElementById -- b --> B
get direct object by id...