Delta times in MIDI files
After dealings with ym2149 and external midi module communication, came about right time to face delta times which are crucial to replaying midi files. As a small remainder I would like to mention that it’s a relative time from last event occurence in midi sequence. Having this value we know in what time period the adequate events have to be sent e.g. from sequencer to midi device (note on, note off, instrument change etc..). I will not write anything about MIDI files structure, because there is alot of good articles on the web.
After the lecture of MIDI file format specification [0,1,2 type] one question arises – how long time takes one delta time and how to implement sending midi data and tempo change? Ok, firstly we have to see what’s interesting connected with tempo can be found in our MID file. There are two things:
1) time division in “MThd” chunk (0x4D546864), indicates number of ticks of our internal clock in one quaternote (PPQN – pulses per quaternote). And it’s plainly resolution in which the pulses are counted in one quaternote, which one has finite period (more about it later). So if we have time division equal to 96 PPQN, then it means that 96 of these ticks indicate end of one quaternote duration. Musicians for example are measuring tempo in number of quaternotes per minute (simply speaking amount of triggered notes per minute), in computers and electronic devices time tracking has to be done in another way. Time division can be coded also in SMPTE format, but it’s rare in midi files and I will not describe it here..
2) Set tempo value which contains an amount of microseconds per one quaternote. It can be found in tracks meta event “MTrk” chunk (0x4D54726B)
If we have these two informations we can calculate frequency with which one clock tick has to occur :
MSPM – microseconds per minute = 60 000 000 BPM – beats per minute MPQN – microseconds per quaternote |
BPM = MSPM / MPQN MPQN = MSPM / BPM |
So if we have 120 BPM (standard, default tempo) then one quaternote period is:
Next step is calculation of the one clock tick frequency, when we have one quaternote period the rest is easy:
With those two things from above we program tick counting with ~0,005208333333 Hz frequency, e.g. for resolution 96PPQN there is 96 ticks that give us gives one full quaternote duration (96 * 1 tick = 96 * 0,005208333333 Hz = 500 000 microseconds = 0,5s).
Now, we can solve an issue how to translate delta times to real time. Below there are three example tempo values:
Tempo 1 | Tempo 2 | Tempo 3 | |
BPM | 120 | 130 | 130 |
PPQN | 120 | 120 | 96 |
MPQN | 500000 | 461538 | 461538 |
QLS | 0,5 | 0,4615 | 0,46 |
TDPS | 0,0041666667 | 0,0038461500 | 0,0048076875 |
BPM – it’s a beats per minute 60 000 000/MPQN
PPQN – Pulses per quater note, resolution found in MIDI file
MPQN – microseconds per quaternote or quaternote duration, range is 0-8355711 microseconds (according to MIDI file specification)
QLS – seconds per quaternote (MPQN/1000000)
TDPS – seconds per tick (QLS/PPQN)
And here’s how we translate each delta value to real time for those three different tempos. All the events are triggered relative to the last event. So first events will be fired with 0 delta, after that there have to be 15 ticks to fire the next event, and after that 45 ticks has to pass (in relation to the last event) before event is triggered. As you can see accumulated delta represents the absolute timeline of events.
So to receive the specific real time at which event has to be launched we multiply given tempo TDPS*accumulated delta. Results are presented in three columns below for each tempo.
If you want to see how will look other tempos I’ve prepared simple sheet in OpenOffice Calc, so you can alter values and see how they change.
Tempo 1 | Tempo 2 | Tempo 3 | |||
lp | accumulated delta | delta | delta [s] | delta [s] | delta [s] |
1 | 0 | 0 | 0,00 | 0,00 | 0,00 |
2 | 15 | 15 | 0,06 | 0,06 | 0,07 |
3 | 60 | 45 | 0,25 | 0,23 | 0,29 |
4 | 70 | 10 | 0,29 | 0,27 | 0,34 |
5 | 90 | 20 | 0,38 | 0,35 | 0,43 |
6 | 100 | 10 | 0,42 | 0,38 | 0,48 |
7 | 120 | 20 | 0,50 | 0,46 | 0,58 |
8 | 135 | 15 | 0,56 | 0,52 | 0,65 |
9 | 180 | 45 | 0,75 | 0,69 | 0,87 |
10 | 195 | 15 | 0,81 | 0,75 | 0,94 |
11 | 240 | 45 | 1,00 | 0,92 | 1,15 |
12 | 250 | 10 | 1,04 | 0,96 | 1,20 |
13 | 270 | 20 | 1,13 | 1,04 | 1,30 |
14 | 280 | 10 | 1,17 | 1,08 | 1,35 |
15 | 300 | 20 | 1,25 | 1,15 | 1,44 |
16 | 315 | 15 | 1,31 | 1,21 | 1,51 |
17 | 360 | 45 | 1,50 | 1,38 | 1,73 |
18 | 480 | 120 | 2,00 | 1,85 | 2,31 |
19 | 555 | 75 | 2,31 | 2,13 | 2,67 |
20 | 570 | 15 | 2,38 | 2,19 | 2,74 |
21 | 615 | 45 | 2,56 | 2,37 | 2,96 |
22 | 630 | 15 | 2,63 | 2,42 | 3,03 |
23 | 675 | 45 | 2,81 | 2,60 | 3,25 |
24 | 735 | 60 | 3,06 | 2,83 | 3,53 |
25 | 765 | 30 | 3,19 | 2,94 | 3,68 |
26 | 780 | 15 | 3,25 | 3,00 | 3,75 |
27 | 795 | 15 | 3,31 | 3,06 | 3,82 |
28 | 810 | 15 | 3,38 | 3,12 | 3,89 |
29 | 855 | 45 | 3,56 | 3,29 | 4,11 |
30 | 955 | 100 | 3,98 | 3,67 | 4,59 |
31 | 975 | 20 | 4,06 | 3,75 | 4,69 |
32 | 990 | 15 | 4,13 | 3,81 | 4,76 |
33 | 1005 | 15 | 4,19 | 3,87 | 4,83 |
34 | 1020 | 15 | 4,25 | 3,92 | 4,90 |
35 | 1035 | 15 | 4,31 | 3,98 | 4,98 |
36 | 1155 | 120 | 4,81 | 4,44 | 5,55 |
So, basically for MIDI 0 type replay (only one track, to keep it simple) we increase our delta in TDPS periods. We check current event in sequence delta time. If event delta is equal to counted ticks we fire up event, reset tick counter and go to the following event event. Otherwise we increase tick counter and check current event delta once again.
For multi track replay we have to maintain one tick counter per individual track and iterate through events on all tracks.
Second option would be triggering events at precalculated real time timeline (based on accumulated delta), but handling this would be complicated with multitrack files.