Midi time to milliseconds?

647e430ca6b2c38d008dc55b1c3a7ecc
0
karligula 101 Mar 08, 2007 at 15:28

Hi folks,

I’m writing some stuff using midi files and I need to convert the midi time stamps for each midi event into milliseconds. I’ve searched the web and every document I come across blathers on about quarter notes, tempo, beats per minute, measures, bars, ticks, metronomes, clocks per click, midi this and midi that… not being a musician I’m utterly confused. So can anyone tell me, in language a simpleton (ie ME) can understand, what numbers and formulas I need to convert the midi time to milliseconds?

Any help is most appreciated…

9 Replies

Please log in or register to post a reply.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Mar 08, 2007 at 17:03

Well, learning a little bit about music would certainly help you, since MIDI follows music notation pretty closely. :)

The MIDI header chunk contains a 16-bit value that gives the number of ticks per quarter note. (Ticks are the units measured by the delta-time values). This value is a constant over the whole file. Within the MIDI data stream are tempo meta-events, which contain a 24-bit value that give the number of microseconds per quarter note. Divide this one by the first one, and you get the number of microseconds per tick, which should be what you want. Note that the track may contain multiple tempo events, since the tempo frequently changes in the middle of a piece.

Now for a basic music lesson. :lol: The fundamental time unit of music is the beat. Beats can be slower or faster depending on the kind of music, and the tempo (speed of the beats) can change even in a single piece as I mentioned. Tempos in standard music notation are typically given in beats per minute.

Beats are organized into measures, also called bars (since they are separated by vertical bars in music notation). The number of beats per measure is usually 2, 3, 4, or 6. The majority of music is in 4, while for example a waltz would be in 3. The top number (numerator) of a time signature is the number of beats per measure. (The denominator indicates which note length gets one beat, which is normally a quarter note.)

Notes come in different power-of-two lengths. A quarter note normally is one beat long (although this isn’t always the case). A half note is two beats, and a whole note is four beats. (It’s called a whole note because it takes up a whole measure, if you’re in 4.) An eighth note is half a quarter note, so there are two eighth notes per beat, a sixteenth note is half an eighth so there are 4 sixteenths per beat, and so on.

Hope this helps, and if you’re going to be doing any significant processing of MIDI files, I would highly recommend learning a little basic music theory…it will help you understand what you’re doing.

647e430ca6b2c38d008dc55b1c3a7ecc
0
karligula 101 Mar 08, 2007 at 17:40

Thanks Reedbeta, for attempting to explain this to me… I think you either get this stuff or you don’t. I reckon it’s like chemistry, I just couldn’t get my head around that either. It seems so convoluted, everything is defined in terms of something else!

Too late… brain switched off… going home… back tomorrow…

647e430ca6b2c38d008dc55b1c3a7ecc
0
karligula 101 Mar 16, 2007 at 16:10

Hi folks,

I’ve got my midi sequencer just about working, but I’ve got a strange quirk… my midi events always seem to be half a beat off. I’ve looked through all the maths about twenty times and I cannot for the life of me see how totally different files can be exactly half a beat out with totally different midi events, tempos and divisions and whatnot. The sequences match up to the music perfectly but for this problem. So, I can only assume that the midi times are half a beat out. It’s almost as if the beat times hit the middle of the beat duration, rather than the start of the beat, if you see what I mean. But I’ve never seen any mention of this anywhere so I’m dubious. I’ve fudged it by just adding half a beat to all the midi times and now everything seems to be fine. But I HATE fudges…

Has anybody else ever had this sort of trouble?

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Mar 16, 2007 at 17:03

What do you mean, half a beat off? What is half a beat off from what?

647e430ca6b2c38d008dc55b1c3a7ecc
0
karligula 101 Mar 19, 2007 at 09:35

Ok, to clarify, I’m doing a dancing game where we play mp3 music, but to synchronise the character dancing with the music we also want a midi file with events marking the notes we’re interested in dancing to. So then we just display a little marker when a note arrives, and the player presses a button to match the music.

Now it all matches fine in the music sequencer, so there’s nothing wrong with the data, but when I calculate the time of the midi events, my note markers appear half a beat off (actually half a beat early). So, obviously I’m doing something wrong. If it were simply a delay playing the mp3 file, then I don’t see how would it always be half a beat off with music of varying tempos. I’ve looked at my time conversion code and the events seem to be at the correct tempo.

Here’s my conversion code (with an extra half beat (division/2) added to the midi event time to fix the problem):

int eventtime=(((thistrack->currentevent->time+(s_Midi->header->division/2))*60000)/(s_Midi->tempo*s_Midi->header->division))+s_MidiStartTime;

I need the event’s game time in milliseconds. s_MidiStartTime is the game time when I started playing the mp3 file.

Like I said it works fine by adding half a beat, it just bothers me that I have to do that!

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Mar 19, 2007 at 17:21

What is this 60000 / (tempo * division)? I think it should be tempo / (division * 1000). Since tempo is in microseconds per quarter-note, and division is in ticks per quarter-note, you should divide them to get the microseconds per tick, then divide by 1000 to get milliseconds per tick.

Also, you are aware that the MIDI event timestamps are *delta* times, i.e. the time since the previous event, not the time since the beginning of the file? It doesn’t look in that equation you posted you are adding the time to the time of the previous event, but to the start time of the whole MIDI file.

647e430ca6b2c38d008dc55b1c3a7ecc
0
karligula 101 Mar 20, 2007 at 10:13

Hi Reedbeta,

When it loads the midi file it adds the previous event time to the current event time, to take into account the running status conditions in Midi files, and to avoid having to do that stuff at run time. So all the event times are made relative to the start of the midi file at load time. Then after it’s calculated the relative milliseconds since I started playing, it adds the start time to get (supposedly) the game time for that event.

I’ve tried using your equation, it’s very sensible and logical and I understand your thinking, but I can’t get it to work. I know it should, and it’s only one line of code so the potential for bugs is limited. I don’t understand it myself either!

I’m getting pretty frustrated with myself for taking so long over this, it’s only a simple time conversion after all… GRRRRRRR… obviously I’m just fundamentally misunderstanding something. I’ll have to stick with my cumbersome equation for the moment and get on with something else before I chuck the PC out the window, come back to it later with a fresh mind. But many thanks for giving me your help on this anyway!

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Mar 20, 2007 at 14:53

@karligula

Hi Reedbeta, When it loads the midi file it adds the previous event time to the current event time, to take into account the running status conditions in Midi files, and to avoid having to do that stuff at run time.

Note that doing it that way could cause trouble if the tempo changes in the middle of the file. (Which it seems might be likely if your midi-files are constructed to synchronize mp3s, as real live performers don’t hold to a precise constant tempo even when they mean to.) Also, it could be that the tempo is only set once, but the tempo event does not come till a short time after the beginning of the file - maybe this is the source of your half beat offset?

Ed6c4e1f1c569ce8975cdb39dfa9e2ca
0
squeakypants 101 Jul 02, 2008 at 18:44

Hey, sorry to reply to an old post, but I’ve been trying to get this right for over a day now and it’s killing me. This is actually the post that I’ve been referencing :D. I’m creating a rhythm game, and I’ve parsed the midi into a list of tracks (which are lists of notes). The MIDI playback is actually a completely separate library (pygame, aka SDL) which only reports back the time in ms. To keep everything in sync, I need to know the time in ticks. I have tempo as a list of 2-part tuples: (time of event in ticks, tempo)

Now my problem doesn’t seem to be with converting from ticks to milliseconds (seems to work fine), but rather from milliseconds to ticks:

def ms(time, tempo, division = 96): #from ticks
    division *= 1.0
    if isinstance(tempo, list):
        ms = 0
        tempos = len(tempo)
        for i, t in enumerate(tempo):
            if i+2 > tempos:
                end = time
            else:
                end = tempo[i+1][0]
                if end > time:
                    end = time
            if t[0] < time:
                ms += (end - t[0]) * t[1] / division
        return ms / 2000
    return time * tempo/(division * 2000)

def ticks(time, tempo, division = 96): #from ms
    division *= 1.0
    if isinstance(tempo, list):
        new = []
        for t in tempo:
            new += [(ms(t[0], tempo, division), t[1])]
        ticks = 0
        tempos = len(new)
        for i, t in enumerate(new):
            if t[0] < time:
                if i+2 > tempos:
                    end = time
                else:
                    end = new[i+1][0]
                    if end > time:
                        end = time
                ticks += (end - t[0]) * division / t[1]
            else:
                break
        return ticks * 2000
    return time * (division * 2000)/tempo

As you can see, the tempo’s time-codes were in ticks so the first thing the ticks method did was change them to ms. Also, you can see that instead of dividing (or in the ticks method, multiplying) by 1000 I did it by 2000. I have no idea why, but when making the ms method I found that the length of every song exactly 2x as long as they should have been.

If you don’t know python, basically all they do are convert each “tempo section” separately until the time that was input, then add them together. t[0] would be time, while t[1] would be tempo.

Any ideas of what’s wrong? I have absolutely no idea at this point.