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

Re: MIDI Tracks and Sequences

From: Jussi Kalliokoski <jussi.kalliokoski@gmail.com>
Date: Sat, 15 Sep 2012 21:46:12 +0300
Message-ID: <CAJhzemVSAFp4AmJkfrCWu2YbqaOO6K+tOs8Ds9akgp-ZmTKhCA@mail.gmail.com>
To: Chris Wilson <cwilso@google.com>
Cc: James Ingram <j.ingram@netcologne.de>, public-audio@w3.org, Joseph Berkovitz <joe@noteflight.com>
On Sat, Sep 15, 2012 at 6:49 PM, Chris Wilson <cwilso@google.com> wrote:

> 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.
>

Theoretically the timestamps may decay over time, depending on
implementation of DOMHRT, but unless the user keeps the tab open for years,
it's unlikely that it will be noticeable (accuracy going outside 1ms
boundaries).


> 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).
>

I believe this can be counteracted by keeping a reference to for when the
first scheduling happens, rather than comparing to DOMHRT start point.


> 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.
>

Really the problem you (James) are having is due to the implementation
(MIDIBridge?) rather than the API, the shims for this API are likely to
rely on unreliable timing measures such as timeouts to implement the
timestamps, whereas an actual implementation will be able to do the
scheduling natively. Accurate timing/scheduling was my primary concern when
designing this API.

Cheers,
Jussi
Received on Saturday, 15 September 2012 18:46:40 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Saturday, 15 September 2012 18:46:41 GMT