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

Re: MIDI Tracks and Sequences

From: Chris Wilson <cwilso@google.com>
Date: Sat, 15 Sep 2012 08:49:23 -0700
Message-ID: <CAJK2wqXXveobDMNx+rcfCkvu=QPoftNggNtV1kk2YDtXRcrKJw@mail.gmail.com>
To: James Ingram <j.ingram@netcologne.de>
Cc: public-audio@w3.org, Jussi Kalliokoski <jussi.kalliokoski@gmail.com>, Joseph Berkovitz <joe@noteflight.com>
On Sat, Sep 15, 2012 at 5:12 AM, James Ingram <j.ingram@netcologne.de>wrote:

> It really, really doesn't need to be recursive.
> I don't think you can remove the recursion, and stay accurate at the same
> time. See below.

You can, in a conformant implementation.  That's the whole point.
 Recursion in this way would be very bad, for precisely the reasons you're
running into.

> You are right, of course, using setInterval was an artefact. I thought it
> might help keep the stack size down if I could explicitly clear something,
> but using setTimeout actually makes no difference (I just tried it).

Stack scope and timers here are unrelated; setTimeout vs. setInterval is
not relevant to your scope issue, your recursion is.  setInterval vs
setTimeout is just a programming convenience.

>  Additionally, you are only sending one message per tick; that's not
>> really the point.  You should be sending any messages that are between this
>> interval call and the next, to get them queued up;
>> ... more like this: https://gist.github.com/**3722988<https://gist.github.com/3722988>
>> .
> That's more or less what Joe suggested too.
> But if you send all messages within the INTERVAL as fast as possible (in
> the while{} loop) then you lose accuracy.

No, you don't.  This is a misunderstanding of what sendMIDIMessage is
supposed to do - it SCHEDULES the message, at the timestamp in the event.
 It is not supposed to send the message immediately, if the timestamp is in
the future.  (Caveat: I have no idea if MIDIBridge properly implements
timestamps or not - this may simply not work today.  Things like this is
why we need native implementations of the MIDI API.  In fact, based on
behavior in your example, I would say MIDIBridge does NOT support
timestamps.  Try https://gist.github.com/3728539 - this should make up for
it as best it can.)  This is the critical point to understand.

As far as I can see, the delay between messages inside the INTERVAL is
> being ignored, and everything gets scheduled on a grid of INTERVAL
> milliseconds (plus or minus a few nano-seconds). In your code that's every
> 200ms. That's also what I hear in test 6 [1], where I have simply replaced
> my sendMIDISequence() function by your code

Everything gets SCHEDULED on an interval of 200 ms or so; however, in a
conformant implementation, the note would be SENT at the appropriate time.
 I should not have left your logging code in my example, because it's
misleading; your logging code that lists a "deviation" is just capturing
when the note scheduling is taking place, not when the note should actually
be sent.  You don't have access to that information (because it's below the
API level); the best you could do is loop an actual MIDI cable back from an
OUT port to an IN port and watch the timestamps on the inputs (and that
will have some of its own latency).

> (remember, my recursive function was working with an accuracy of 1
> millisecond).

Because you were choking the setTimeout queue.  setTimeout(0) WORKS - but
it's not a good programming form, as it's burning processing power.

[Actually, that's not quite true: I have seen rare glitches in test 5
> (maybe once in 3 minutes) in which 2-3 notes deviate by more than 1
> millisecond. Obviously, setInterval sometimes takes a rest...]

Sure.  You might have relayouts happening, or other things the system has
to take into account.  That's precisely why the MIDI API has its own timing
system; setInterval/Timeout is unreliable.

> Interestingly, the scheduling in test 6 (your code) also decays over time,
> and the individual note-clusters spread out a bit.

I'm not sure what you mean by "decays over time".  All the "deviation"
values should be listed between 200 and 400, and they are.

I do note that the initial ones are off, because you need to call your
(anonymous) scheduling function exactly when the DOMHRT sequencer clock
starts (because you're missing an opportunity to schedule the first 200ms
of events).

> Looking at the log, there's also some drift in where setInterval is
> starting each INTERVAL. That's obviously what you meant by
>  and really, you need some lookahead past that, in case the timers are
>> skewed a bit (aka the setInterval callback is late).
Yes, that's expected.

>  I'm not at all happy with an indeterminacy of around 200ms, and I know
> that passing small delays to setInterval  is something one shouldn't expect
> to work.

That is correct.  But you have the underlying scheduling of the
sendMIDIMessage implementation to take care of the indeterminacy below
200ms; in fact, you could schedule the entire sequence at one go.  But then
you couldn't change tempo or cancel the output.

> So there's no way I can get to anything like 1 millisecond accuracy by
> using setInterval or setTimeout non-recursively.

Recursion is not relevant here - you can't get true 1ms accuracy using
setInterval or setTimeout in any case.  This is why we have timestamps
built in to the API, and that level of accuracy is left to the
implementation of the MIDI API.
Received on Saturday, 15 September 2012 15:49:53 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 6 January 2015 21:50:02 UTC