Delta time in MIDI (a.k.a. V-time)

Dan Laüt's icon

Delta time is an integer value expressed as a daisy chain of 7-bit values with the high bit set in all daisies except the last.

With respect to MIDI, v(ariable)-time is in fact a better name, because delta time is generally understood to read low byte first, while in MIDI the high byte comes first.

A v-time value, usually 1 or 2 bytes, precedes every MIDI message and marks the time passed since the previous message.

(Note: The MIDI file header also contains a “delta value”, which denotes the number of ticks per quarter note.)

In some situations it is necessary to convert v-time to an integer, or vice versa.

This turned out to be a bit more laborious than anticipated, not least because the literature on it is by and large rather opaque.

In hopes to save other Cyclists in such situations said labor, here is the result of mine:

Max Patch
Copy patch and select New From Clipboard in Max.

Andy Maskell's icon

I could see this being potentially very useful in resolving some time critical issues that I have in my project at the moment. How can I actually get hold of the timing bytes in a MIDI packet and hopefully bring them into Max?

Dan Laüt's icon

Here's the lay-out:

MIDI file structure.pdf
application/pdf 58.09 KB

The basic routine is:
1. Read file header (14 bytes);
2. Read track header (8 bytes);
3. Read delta time;
4. Read status byte (or, if the byte <80h, use running status; this is tricky);
5. Read data byte(s);
6. Goto 3 until EOT
7. If multiple tracks, goto 2 else done.

Andy Maskell's icon

Ok, thanks. I was thinking of the timing between normal MIDI messages rather than reading a sequence from a file.

Source Audio's icon

If you mean midi stream as midi packet, then there is no delta time sent.
It is only part of midi file.
If you want to compare timing of arrival of midi mesages to timing in midi file played remotely, you can simply record midi and compare it.

what makes it difficult to extract delta time events, in case you load midi file into filein and output byte by byte to parse events is running status:
status bytes get omitted in midi event till it changes,
example with ticks delta:
delta 0 event 144 60 100 note on
delta 320 event 62 120 note on
delta 120 event 60 0 note off
delta 164 event 62 0 note off
delta 0 event 176 11 44 CC status byte changed
...
means one can't simply group bytes after status byte
which form known event length.
this woud be readout of bytes for above example :
0 144 60 100 130 64 62 120 120 60 0 129 36 62 0 0 176 11 44
delta time bytes underlined.

but there is also mf2t converter, for mac and win (midiox)
to convert midi file into plain text, much easier then to bother with filein.

Max seq and detonate use absolute event times, ms instead of ticks
when they read/record midi files.
1 ms is smallest time fraction...
But from that, knowing tempo one can also extract tick delta
between absolute values respecting ppq (960 default),
which max translate object can't cope with, so one has to divide by 2 to get ms, or make own translator using few objects.
----------------
Another example are packet based sysex messages, which
break the string into several parts.
In midi file sysex message must include data string length, set right after F0 byte

240 5 22 56 80 33 247

same applies for packet messages:
240 3 11 22 33 sysex part 1
100 delta 100 ticks
247 4 44 55 66 77 sysex part 2
129 52 delta 180 ticks
247 3 88 99 247 sysex last part
----------
240 3 11 22 33 100 247 4 44 55 66 77 129 52 247 3 88 99 247

delta values in such midi file can be easily parsed,
after 240 get number of bytes after length byte, parse delta using
remaining bytes till next 247, and so on ...








Andy Maskell's icon

This discussion is all very interesting but it's not what I'm after!

In my project, I am interfacing to a piece of software the sends out MIDI messages in reponse to actions in that software package and the mixer that it is controlling. It's all about using MIDI messages as control signals rather than music.

These messages can come in just two and threes but sometimes a whole flood of messages can come in one large batch or a number of smaller batches. The problem I have is that due to the way that the software is written, these batches don't always come in an order that makes logical sense in terms of what my Max app is trying to do, and the message order can vary according to the context of actions in the mixer software. Furthermore, any given message can end up influencing the behaviour of several sub-patchers.

So, to get round it, I have a rather large patch that manages all these messages with numerous buffers, pipes and delays so that I can process the messages in a more sensible order.

What I really need is a means to record all the incoming messages with precise timing measurements using a method that has the minimal scheduler overheads so that I can then make more sense of the order and timing of the batches of messages, such that I can make my code more responsive and efficient.

I'm wondering if I could "record" the messages into a seq buffer first and then process them from there, knowing what the timing intervals were?

Dan Laüt's icon

It is hard to imagine what it is you are trying to accomplish without seeing the patcher. You can feed seq a text file with lines of the format time - status - data, where time is in ms and relative to the previous event (or 0 if there isn't any). The messages can control anything you want.
With regard to timing incoming messages, I'd use the timer object to determine the intervals. Of course, it gets more complicated if the messages come in through multiple ports.

Source Audio's icon

midi - being serial communication has one advanage:
everything travels byte by byte.
If you know what your midi messages are, it is simple to capture them using timestamp.
is seq with 1ms resolution not fine grained enough ?

Andy Maskell's icon

Unfortunately, the complexity of the software that I'm interfacing to and the fact that the order of MIDI messages can change according to context makes it very difficult to predict. Added to that, some events come as Note On/Note Off pairs, some as just a Note On or Off, and some messages are repeated more than once in response to screen updates! It's been a minefield trying to pick it all apart and ensure that the order of events in my app follows a workable pattern in all instances, hence the need for all the buffers, pipes and delays.

Maybe I'll have to use a coll to store the timestamp along with the MIDI messages and then sort through that somehow. Whilst 1ms is perfectly ok for understanding the sequence timing in my app, it doesn't help in situations where multiple messages arive within the same 1ms! Ideally, I need to resolve it down to CPU cycles or the system clock.

Perhaps I should just stick with what I have done already - "If it ain't broke, don't fix it"!

Source Audio's icon

if you record into seq and save as text, you can see all events
including absolute time in ms.
seq can stuff many messages under same ms if thy arrive so.
each event gets own line, like
1000 144 55 60;
1000 144 58 120;
1000 176 7 120;
1000 240 22 33 56 78 90 122 247;


you can also record messages that you send out to mixer to request data
in a separate seq and then check the timing.

Andy Maskell's icon

I've been trying something out with [cpuclock] and [coll] just to see what I can capture.What then need is something a bit like an Excel spreadsheet that can sort on multiple columns and then extract lines of data at random based on cell matches!

Andy Maskell's icon

Ha Ha! Well that little test was rather revealing!

I've been telling the developer about all the conflicting and repeating messages for a while now. He's just released quite a major update and whilst he's not explained any detail, he did tell me that he had done quite a bit of "backroom" work to optimse a lot of his code in preparation for a raft of new developments.

Well, what do I find, he's eliminated all the unneccesary messages! I could quite easily dump a huge chunk of my code now that I previously needed to sort through the mess!

Source Audio's icon

I am so glad you finally have some relief with that bugger, your fight with that mixer is almost historic battle.
But dos that mean that now you have to deoptimise all that buffers, pipes
delays etc ?

And sorry @Dan , this thread driffted a bit from the original topic
and your helpfull infos.

Andy Maskell's icon

Thankyou.

It's not exactly the prettiest bit of coding, is it?

And that's only the first half!

I guess I have three options:
1) Leave it alone as it still works.
2) Start removing chunks of redundant code until I get rid of everything I don't need anymore.
3) Start agin from scratch knowing that writing a new module will be much easier now.

Source Audio's icon

I would keep using old one, but at same time start fresh one
from scratch and enjoy the work without pressure....

Andy Maskell's icon

That's my plan.....

Andy Maskell's icon

And sorry for hijacking your thread @Dan Laut, but I have learnt a lot from it.

Dan Laüt's icon

No worries, Andy. The Path of Life consists mainly of side tracks, as every programmer knows all too well. And even when they dead-end, you always learn something new. Like, I had no inkling that seq could save as text. RTFM, eh?

Roman Thilenius's icon

i still dont understand the issue. what could be the benefit of reading out the delta time float value reversed?

Dan Laüt's icon

It depends on the application. MIDI is little endian, high byte first, but (most) others are big endian, where the low byte comes first. The value is the same. They do not mix, however.

Andy Maskell's icon

I managed to dump about 1/3 of the code in that troublesome module as a result of this prompt! Thank you! There are now only 6 delays and 2 pipes, which are necessary to allow the host app's screen redrawing to take place, but they are now very short. The rest runs alot faster and without having to trap all the endless special exceptions. I'm well chuffed!