Replaying an Audio element in chrome - javascript

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>

Related

Howler js pause/resume trouble

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);
}
};

Javascript:: audio.currentTime is updated properly only after alert

I think that I found a bug on JavaScript Audio API.
My code is working perfectly like that :
var audio=new Audio();
var lastPause;
var myPlayer=document.querySelector('#player>.button.hand');
var playerStatus=("textContent" in document)?"textContent":"innerText";
myPlayer.addEventListener
(
'click',
function()
{
if(audio.paused)
{
audio.src="Nadav Guedj - Golden Boy (Eurovision 2015 - Israel - Karaoke).mp3";
myPlayer.innerHTML="►";
if(lastPause)
{
alert(lastPause); //If I remove this line it's not working when lastPause is defined!
audio.currentTime=lastPause;
}
audio.play();
}
else
{
myPlayer.innerHTML="||";
lastPause=audio.currentTime;
audio.pause();
}
}
);
BUT
If I remove or remark the alert line alert(lastPause); the audio stuck when lastPause is defined or iow... The second time the user press play (after pause).
I am assuming this question is related to this question my answer there is applicable for this also,
repeated setting audio.src is your problem. you have two ways of fixing this:
get rid of audio.src line, bunch of other redundant code could also be removed with it.
the reason it works when you use alert is, when alert is triggered, the next few lines are not executed till you dismiss it, in these precious few seconds/ milliseconds, the audio element whose source has been changed has enough time to load the new metadata, only then can the current time can be set. so you can modify the code to trigger setting of audio on metadata loading:
...
myPlayer.innerHTML="►";
audio.play();
audio.onloadedmetadata = function(){
if(lastPause){
audio.currentTime = lastPause;
}
};
fiddle demo

Get audio duration on Chrome for Android

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.

canplaythrough firing intermitently in firefox

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.

setTimeout not working on safari mobile

I have a function that shows a menu when clicking on it, and I want it to disappear after 5 seconds. This is my javascript - it works properly on desktop browser but it doesn't disappear on the mobile ones.
$(function() {
$('#prod_btn').click(function() {
$(this).addClass('selected').next('ul').css('display', 'block');
setTimeout(hideMenu, 5000);
});
});
function hideMenu() {
$('#prod_btn').removeClass('selected').next('ul').css('display', 'none');
}
Where is the problem?
Thanks
I've just had the same problem. My code is running great in any browser on my Mac, but on iOs devices it doesn't work.
I use ".bind(this)" on my timeout function and that is what is causing the problem for me.
When I extend the function object with ".bind" in my script it works like a charm.
My code is something like this:
searchTimeout = setTimeout(function() {
...
}.bind(this),250);
For this to work on iOs devices I (like mentioned above) just added this:
Function.prototype.bind = function(parent) {
var f = this;
var args = [];
for (var a = 1; a < arguments.length; a++) {
args[args.length] = arguments[a];
}
var temp = function() {
return f.apply(parent, args);
}
return(temp);
}
I don't see any .bind on your setTimeout, but for others with the same problem this may be the issue. That's why I'm posting :-)
I moved your example to a jsbin, and it's working on my iphone 4.
Please test it out going here from your devices: http://jsbin.com/asihac/5
You can see the code here http://jsbin.com/asihac/5/edit
The example is using jQuery - latest and I only added the required css class.
this doesn't apply to your code, but a common problem with long-running scripts failing on iOS devices is that MobileSafari kills a javascript thread after 10 seconds have elapsed. you should be able to use setTimeout and/or setInterval to work around this, or you can avoid it by making a shortcut to it and thereby running it as an app. see https://discussions.apple.com/thread/2298038, particularly the comments by Dane Harrigan.
Keep in mind also that any setTimeout function is actually likely fire while DOM elements are rendering if the delay is set to a value too short. While that might seem obvious, it can be easily confused with no method firing whatsoever. A good way to test is to have an alert prompt run.
window.onLoad(alert("hey!"));
Then check to see if your function fires after.

Categories