Setting the granularity of the HTML5 audio event 'timeupdate'
I'm sorry, but that's the way it works. From the html5 specs:
Every 15 to 250ms, or whenever the MediaController's media controller position changes, whichever happens least often, the user agent must queue a task to fire a simple event named timeupdate at the MediaController.
Also,
The event thus is not to be fired faster than about 66Hz or slower than 4Hz (assuming the event handlers don't take longer than 250ms to run). User agents are encouraged to vary the frequency of the event based on the system load and the average cost of processing the event each time, so that the UI updates are not any more frequent than the user agent can comfortably handle while decoding the video.
If you read through the specification, you can get the idea that timeupdate
event is something of a "best effort" kind of event. It will fire when it can and always as long as it does not to affect performance too much.
You could filter the events discarding some from time to time to smooth the arrival times, but I'm afraid it's not possible to do the opposite.
Html5 Audio timeupdate precision
So I researched my way through this problem and came out with the following findings:
1) HTML Audio is no dice for such a scenario. Its a very limited API and doesn't offer fine grain audio control. Web Audio API is the way to go for this.
2) Web Audio API is not the easiest thing to read/understand and is perhaps weakly implemented across various browsers. Its better to find a nice wrapper around it that fallsback to web audio or flash when Web Audio is not available in a browser.
3) Some wrappers exist like howler.js
, sound.js
and SoundManager2
. Off the three SoundManager2 seems the best fit at first thought.
Demo 4G at http://www.schillmania.com/projects/soundmanager2/demo/api/ effectively solves a very similar problem as in the scenario in the question.
Update: SoundManager2 actually doesn't support the web audio api. From the technotes:
SM2 does not presently use the Webkit Audio API. It may be
experimentally or more formally added at some point in the future,
when the API is more universally-supported.
HTML5: How to get currentTime and duration of Audio Tag in milliseconds
You can use:
<audio id="track" controls>
<source src="your.mp3" type="audio/mpeg">
<source src="your.ogg" type="audio/ogg">
</audio>
<script type="text/javascript">
var audio = document.getElementById('track');
audio.addEventListener('timeupdate',function(){
var currentTimeMs = audio.currentTime*1000;
console.log(currentTimeMs);
},false);
</script>
You can read here for more information on the precision of the timeupdate event. It is dependent on the browser implementation so keep in mind you will get different results from a browser/device to another.
You should use addEventListener method rather than the ontimeupdate property - it is more maintainable.
Also if you need browser coverage it is good to use both ogg and mp3 audio files as sources.
timeupdate Event Function Firing Twice After Rebind
Looks like the problem is this chunk:
$('.goal, .choices').fadeOut(function() {
$('#audio-try_again').off('timeupdate');
setTimeout(generate_objects, 200);
});
The selector $('.goal, .choices')
will return a jQuery collection containing two elements, which means the callback function is invoked two times (once per matched element).
A quick and dirty way to overcome this, for your specific application, would be:
$('.goal, .choices').fadeOut(function() {
if ( $(this).is('.goal') ) return;
$('#audio-try_again').off('timeupdate');
setTimeout(generate_objects, 200);
});
Which will, essentially, only execute the stuff inside that callback once.
HTML5 Video run event once if currentTime is larger than X
To achieve this you can use a flag to keep track of the state of the event occurring. By default the flag will be false. It will only be set to true when the currentTime
of the video is less than 30 seconds from the end.
You can now use this flag in your if condition so that it will only ever fire once, like this:
var $video = $('video');
var $p = $('p');
var eventFired = false;
$video.get(0).volume = 0.2; // just for testing
function func() {
if (!eventFired && this.currentTime > this.duration - 30) {
eventFired = true;
$p.show();
}
}
$video.on('timeupdate', func);
p { display: none; }
video {
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>You are less than 30 seconds from the end!</p>
<video autobuffer controls autoplay>
<source id="mp4" src="http://grochtdreis.de/fuer-jsfiddle/video/sintel_trailer-480.mp4" type="video/mp4">
</video>
Is setTimeout function accurate enough to sync with audio timing?
"Accurate enough" is subjective, but in general, no. You don't know when the audio will finish downloading and start playing, or if it will play through without buffering or being paused by the user.
Instead you could use the timeupdate
event and currentTime
property, to properly sync events to the timeline.
var exampleElement = document.getElementById('example');
var lastTime = 0;
exampleElement.addEventListener('timeupdate', function(e) {
var nowTime = this.currentTime;
//Check if just passed the 1.4 second time mark.
if (nowTime > 1.4 && lastTime < 1.4) {
//Add a message to the debug element.
var logElement = document.getElementById('log');
logElement.textContent += 'Do something at 1.4 seconds\n';
}
lastTime = nowTime;
});
<audio id="example" controls="controls" autoplay="autoplay">
<source src="http://media.w3.org/2010/07/bunny/04-Death_Becomes_Fur.mp4" type='audio/mp4'>
<source src="http://media.w3.org/2010/07/bunny/04-Death_Becomes_Fur.oga" type='audio/ogg; codecs=vorbis'>
</audio>
<pre id="log"></pre><!--debug element-->
Related Topics
Play Infinitely Looping Video On-Load in HTML5
How to Set the HTML Options for Collection_Select in Rails
What Characters Must Be Escaped in an Http Query String
Flexbox on Ie11: Image Stretched for No Reason
Internet Explorer 9 Drag and Drop (Dnd)
How to Save the Content in Uiwebview for Faster Loading on Next Launch
How to Align Footer (Div) to the Bottom of the Page
Best JSON-Ld Practices: Using Multiple <Script> Elements
Selectable <Optgroup> in HTML <Select> Tag
CSS Text Underlining Too Long When Letter-Spacing Is Applied
Centering Floating Divs Within Another Div
How to Margin the Body of the Page (Html)