96 lines
3.6 KiB
Plaintext
96 lines
3.6 KiB
Plaintext
|
|
From dirk@diaspar.fb10.TU-Berlin.DEMon Feb 27 20:22:36 1995
|
|
Date: Mon, 27 Feb 95 16:43:59 +0100
|
|
From: Dirk Schwarzhans <dirk@diaspar.fb10.TU-Berlin.DE>
|
|
Reply to: dirk@kalium.physik.TU-Berlin.DE
|
|
To: sean@cnct.com
|
|
Subject: Re: MIDI TIME DATA
|
|
|
|
Hello,
|
|
|
|
in alt.binaries.sounds.midi you asked for info about MIDI timing.
|
|
I had the same problems too some time ago.
|
|
|
|
Here is the solution:
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
|
deltaRealtime = tempo * deltaTime / division
|
|
|
|
gives you the difference between two midi events (which are
|
|
separated by the deltaTime value specified in the file) in micro
|
|
seconds. "division" is from the midi file header.
|
|
|
|
But you CANNOT use this formula because of accumulating errors,
|
|
even if you use double precision floating points calculations.
|
|
|
|
The way to prevent this is:
|
|
---------------------------
|
|
|
|
1) You calculate a quantity called "midiTime" for every event by
|
|
adding the deltaTimes of the previous events.
|
|
|
|
2) Then you use this times for merging the events from different tracks.
|
|
|
|
3) Then you calculate the realTime of every event like this:
|
|
|
|
-initialize the following variables:
|
|
long midiOffset = 0;
|
|
long realOffset = 0;
|
|
long division; /* = from file header */
|
|
long tempo = 5000000; /* default value from midi file spec. */
|
|
|
|
-for every event do:
|
|
realTime = realOffset + (long)((double)(midiTime - midiOffset) *
|
|
(double)tempo / (double)division);
|
|
|
|
-for every tempo change do:
|
|
tempo = newTempo; /* newTempo specified in SET TEMPO command */
|
|
realOffset = realtime; /* calculated above */
|
|
midiOffset = midiTime;
|
|
--------------------------------
|
|
|
|
Now rounding errors only accumulate on tempo changes which don't
|
|
occure as often as midi events.
|
|
|
|
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
|
For the case of negative division in the file header someone sent
|
|
me the following text:
|
|
|
|
If "division" is negative then its high byte is minus the number
|
|
of frames per second, and its low byte is the number of ticks per
|
|
frame. So if you multiply the two bytes together and negate the
|
|
result, you have the number of ticks per second (1000 is a typical
|
|
value - gives millisecond timing). Then, divide each Delta-Time by
|
|
this number to get the length in seconds between each two events.
|
|
This will give non-integer results!! so be sure to use real number
|
|
division. If ticks per second=1000 then the conversion of Delta-Times
|
|
is unnecessary - they're already in units of milliseconds, which
|
|
is useful enough.
|
|
|
|
The purpose of a tempo change in this environment is not to alter
|
|
the rate at which the music plays, but to change the rate at which
|
|
it is represented as quarter notes, eighth notes, etc. You can also
|
|
use it to derive a value for the number of ticks per quarter note
|
|
= (Tempo change value) * (ticks per second) / 1000000, because
|
|
the tempo change value is in microseconds per quarter note.
|
|
|
|
There is another factor that (rarely) affects timing: at the end
|
|
of a time signature meta-event (FF 58 04 nn dd cc bb) the bb is
|
|
the number of notated 32nd notes in every MIDI quarter note. I have
|
|
never seen this be anything other than 8, but if it's different,
|
|
a program which deals with measures, beats, or notation has to
|
|
convert the MIDI timing info to useful notation info. The way to
|
|
do that would be to get a new value for the number of ticks per
|
|
quarter note:
|
|
new value = 8 * (old value) / (bb)
|
|
This new value should then be used to recalculate the relevant
|
|
timing data: either the number of microseconds per tick if "division"
|
|
is positive or the number of ticks per second if "division" is
|
|
negative.
|
|
|
|
|
|
Hope this helps,
|
|
|
|
Dirk Schwarzhans
|