W3C home > Mailing lists > Public > public-audio@w3.org > July to September 2012

Re: Reflections on writing a sequencer

From: Adam Goode <agoode@google.com>
Date: Tue, 24 Jul 2012 15:03:58 -0400
Message-ID: <CAOf41N=c40fz05K7NA09w8rHtiVHp2OM9Q-himXa2W-sqo_uAQ@mail.gmail.com>
To: Joseph Berkovitz <joe@noteflight.com>
Cc: Audio Working Group <public-audio@w3.org>
Aha, thanks for the tips. I didn't find anything in the w3c bugzilla
regarding callbackAtTime, but that sounds like it's perfect.

I'll try Joseph's technique for now.


Adam



On Tue, Jul 24, 2012 at 12:40 PM, Joseph Berkovitz <joe@noteflight.com>wrote:

> HI Adam,
>
> I think one general way to structure sequencer playback is as follows --
> I've used this approach with WebAudio successfully in the past:
>
> 1. Just before starting playback, take note of the AudioContext's
> currentTime property.  Add a small time offset to it, say 100 ms.  The
> result will be your performance start time, corresponding to time offset
> zero in your sequencer data.  (The time offset provides a short window in
> which to schedule the first events in the sequence).
>
> 2. Create a scheduler function that will run periodically, which examines
> the AudioContext's currentTime and subtracts the previously captured
> startTime. That gives you a "current performance time" at the moment the
> callback occurs, expressed in terms of your sequencer data.  Then create
> AudioNodes representing all sequencer events that occur within an arbitrary
> time window after this current performance time (say, several seconds) and
> schedule them with noteOn/noteOff.
>
> 3. Call the function immediately, and also use setInterval() or
> setTimeout() to schedule callbacks to the above function on some reasonable
> basis, say every 100-200 ms. The exact interval is not important and can be
> tuned for best performance.
>
> This approach is relatively insensitive to callback timing and in general
> allows audio to be scheduled an arbitrary interval in advance of its being
> played.
>
> ...Joe
>
>
> On Jul 24, 2012, at 11:40 AM, Adam Goode wrote:
>
> 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
>
>
>
> ... .  .    .       Joe
>
> *Joe Berkovitz*
> President
>
> *Noteflight LLC*
> 84 Hamilton St, Cambridge, MA 02139
> phone: +1 978 314 6271
> www.noteflight.com
>
>
Received on Tuesday, 24 July 2012 19:04:26 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Tuesday, 24 July 2012 19:04:27 GMT