Executing 'realtime' javascript without hanging the browser
I'm trying to write a simple music-sequencer in Javascript.
Sounds will be played by SoundManager2
I quickly realised that setTimeout and setInterval are useless for this kind of timing. Their accuracy is simply not good enough.
So what I am trying instead is this:
Create a queue of sound-events (a 2d array [ note, time ] )
Process the queue in a while loop
In pseudo code it could look like this:
// The queue of time/note values (millisecs)
var q = [[0, C], [250, D], [500, E]]
var begin = (new Date).getTime()
while(q.length > 0 ){
var now = (new Date).getTime()
var eventT开发者_StackOverflowime = q[0][0] + begin
if( now >= eventTime){
playNote(q[0][1]) // Play the note
q.shift() // Now that the note has been played, discard it.
}
}
Through debugging I've found that this method seems precise enough (the playNote call is made at the exact time it's supposed to).
However while the 'sequence' is playing, all other Javascript (including the bit that actually MAKES the sound) is suspended which is hardly a surprise.
This means that I have silence for as long time it takes to run the sequence and then ALL of the calls to playNote are executed.
I've tried isolating the while-loop in it's own function and then call it through setTimeout, in the hopes that this would create a background thread executing it's own thing (namely playing the sequence) without halting all other JS execution. That doesn't work
I've also tried calling the playNote function using setTimeout, in the hopes that while indeed the UI is frozen at least the sequence is playing correctly, but that doesn't work either.
I've also tried a combination, but as you probably already guessed, that doesn't work either.
So my question is:
How can I have a queue of events processed with precise timing, while NOT shutting down all other code-execution during the run
I don't know if there is a solution for older browers, but web workers are intented for doing parrallel execution in JavaScript.
They are supported in recent versions of Chrome and Firefox, I don't know about other browsers.
If you replace your while
loop with a recursive function (it doesn't have to be self-executing) the browser wont freeze.
(function foo (q) {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
return foo(q); // Pass q into the next round
} else {
return; // End the "loop"
}
}(q));
Edit: The problem with recursive calls + timer accuracy should be taken care of by setInterval
:
var timer = setInterval(function foo () {
var now = (new Date).getTime(),
eventTime = q[0][0] + begin;
if (q.length > 0) {
if (now >= eventTime) {
playNote(q[0][1]); // Play the note
q.shift(); // Discard played note
}
} else {
clearInterval(timer); // End the "loop"
}
}, 10); // This is in ms (10 is just a random number)
精彩评论