Reflections on writing a sequencer

Hi,

Yesterday I tried to write an extremely simple sequencer using webaudio. My
goal was to have a tone play periodically, at a user-selectable low
frequency interval.

The main problem I ran into was the difficulties in scheduling events
synchronized with the a-rate clock.

If I want to play a tone twice per second, I want to call this code in a
loop, indefinitely:

var startTime = ....
var o = c.createOscillator();
o.connect(c.destination);
o.noteOn(startTime);
o.noteOff(startTime + 0.1);

I can't just put it in a loop, I need to schedule this in a callback, when
necessary to fill the event queue. But what callback to use? setInterval is
not appropriate, since the setInterval clock will skew quickly from
c.currentTime. And busy looping with setInterval(0) will consume a lot of
CPU and gets throttled when switching tabs (try putting the drum machine
demo in a background tab and see).

My solution was this:

var controlOscillator = c.createOscillator();
controlOscillator.frequency.value = 2;
var js = c.createJavaScriptNode(256, 1, 0);
controlOscillator.connect(js);

js.onaudioprocess = function(e) {
  ... detect positive zero crossing from control oscillator ...
  if (zeroCross) {
    var o = c.createOscillator();
    o.connect(c.destination);
    var startTime = ... zero crossing offset + playbackTime ...
    o.noteOn(startTime);
    o.noteOff(startTime + 0.1);
  }
};


This does work (except for missing playbackTime
https://bugs.webkit.org/show_bug.cgi?id=61524, needing to connect the
javascript node to destination, and another bug on chrome
http://crbug.com/138646), but is awkward. There is also the question of
having a disconnected graph: I am sending control data, not audio data, so
I don't want to connect it to destination.

I essentially want to have a callback for getting new control data, to keep
the event pipeline filled without overflowing any noteOn buffer or falling
behind. Is the javascript node appropriate for this? I feel like there
could be something more explicit, like a setInterval off of the audio
context.



Adam

Received on Tuesday, 24 July 2012 15:41:31 UTC