W3C home > Mailing lists > Public > public-audio@w3.org > October to December 2012

Re: [Web MIDI API] send() timestamp

From: Chris Wilson <cwilso@google.com>
Date: Fri, 14 Dec 2012 11:12:55 -1000
Message-ID: <CAJK2wqUtxDdj60tYRU02iEXLz+qhhxeWf+PG8c4TfG6zdVzSEQ@mail.gmail.com>
To: Marcos Caceres <marcos@marcosc.com>
Cc: Jussi Kalliokoski <jussi.kalliokoski@gmail.com>, "public-audio@w3.org" <public-audio@w3.org>
I don't actually know of any systems that do this (future-scheduling of
MIDI input), personally, but that's not the point of timestamps in my
opinion.

The point of the timestamps on input is based on the fact that the
underlying system (device driver, e.g.) will get the MIDI input likely on a
much higher precision time clock than it will be able to pass the message
into a JavaScript callback (on the main thread, which may get blocked by
layout, etc.).  The higher precision of that timestamp allows, e.g. a
higher-precision recording of the timing of input messages in a sequence;
for example, if you're recording the MIDI messages from a live piano
controller, you will want the subtleties of that timing recorded - you may
have some lag when passing through to send(), but when playing back that
recorded sequence later, your timestamps will be more precise.

Likewise, the timestamps on send need to be on a consistent time clock, to
keep a rock-solid "sequence timing" going - that's why we use performance
timing rather than time.now() - and using offsets would require finding the
"current time" when send() is called frequently.  You could make it an
offset, rather than an absolute; however, that would require coders to call
window.performance.now() more frequently.

To schedule the next note of a sequence with absolute times, presuming
"note" is an object that contains the next note's data and timestamp as an
absolute start time:

    out.send( note.data, sequence.startTime + note.timestamp );

if you had offsets instead:

    out.send( note.data, note.timestamp - ( window.performance.now() -
sequence.startTime );

There are other ways to structure this, of course - and in those cases, you
may not have this kind of offset.  For example, Standard MIDI Files record
offsets from the last note - but that's actually not as relevant as it
seems, because "the last note" is not where you're likely scheduling THIS
note.  The main point is, it's going to be easier to not have to convert
between a time offset *from the current time* all the time.  The symmetry
with receiving is also a very good thing, of course.

Your example is a relevant counter-example - but keep in mind, it is quite
rare that you would want to actually call send() on a large number of notes
at once, because then you can't cancel them or control tempo.

I've had it on my list to do a sequence-playing example for a while; hope
to get to that in the next couple of weeks.

-Chris


On Fri, Dec 14, 2012 at 10:27 AM, Marcos Caceres <marcos@marcosc.com> wrote:

>
>
> On Friday, December 14, 2012 at 8:12 PM, Jussi Kalliokoski wrote:
>
> >
> > Not necessarily. Some platform MIDI APIs have an internal clock and
> allow MIDI messages to be sent and processed ahead of time, so when you
> receive a MIDI message, it might not be supposed to take effect
> immediately. The timestamps being synchronized with DOMHRTF essentially let
> you leverage/emulate this feature without exposing meaningless details
> about the underlying API, such as its internal clock.
> So 2 things:
>
> 1. the spec is not clear with regards to this, as it says: "Let event be a
> newly constructed MIDIEvent, with the timestamp attribute set to the time
> the message was received by the system". That doesn't seem to match what
> you described above.
> 2. how common is this use case? or how common are these devices? (I
> honestly don't know, but it would be good to have some data).
>
> I presume most people will just want a little delay in sending data. Here
> is two things I just made for fun by changing the API to use delay instead
> of timestamp:
>
> for(var i=2, fib=[0,1]; i<12; i++){
> fib[i] = fib[i-2] + fib[i-1];
> output.send([0x91, fib[i] ,0x5f], (100 * fib[i]));
> }
>
>
> for (var l = 128, i = 0; i < l; i++) {
> var value = Math.floor(l * (Math.sin(i * 0.09)) + l) / 2;
> output.send([0x91, value ,0x5f], (50 * i));
> };
>
>
> The current API would require me to prefix everything with
> performance.now() or an alias - which is inconvenient for my use cases
> above.
>
> I'm still not sure what the most common use cases for this API will be.
>
> --
> Marcos Caceres
>
>
>
>
>
Received on Friday, 14 December 2012 21:13:27 UTC

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