Home > Net >  How long is a MIDI file note in milliseconds?
How long is a MIDI file note in milliseconds?

Time:04-25

So I have this very simple MIDI file: enter image description here

I simply want do define the length in milliseconds for these 2 notes. In real time the first note is less than a second and the second note is about 2 seconds.

As I iterate the MIDI events, every time I get a note-on or note-off event I fetch there delta ticks. I'm 100% sure these delta ticks are correct because I compared them with an algorithm that works properly.

As far as I understand, if the F5 key in this file hits note-on in an X time, the note-off of that event will hit after the number of deltaTicks of the note-off event. So that means that to find how long in milliseconds the length of the F5 key is, all I need to do is to convert the note-off event delta ticks to milliseconds. (Am I correct?)

I parsed the MIDI file so I have the tempo and the PPQ, and I searched a lot about this so till now I found this formula:

double bpm =  (60000000 / mTempo);
deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
        

but I'm probably doing something wrong here because the delta time in milliseconds for the F5 key note-off event is about 7000, which means 7 seconds.

Thank's for any help!

More of the file:

    public class MidiParserN {
            private static double mTempo;
            private static short division;
            ...
            ...
            private static TrackChunkEvent 
            parseMTrkEvent() throws Exception {

            int deltaTicks = Math.abs(calculateDeltaTicks(getVariableLengthQuantity()));
            double deltaTimesInMilliseconds = 0;
    
            int eventType = parseEventType();
            int eventNum = -1;
            String eventName = "";

            if (eventType == EventTypes.MIDI_EVENT.ordinal()) {
                eventNum = parseMidiMessage();
                eventName = midiEventsTypes[eventNum].toString();
            } else if (eventType == EventTypes.SYSEX_EVENT.ordinal()) {
                parseSysexEvent();
            } else {
                eventNum = parseAndExecuteMetaEvent();
                eventName = metaEventsTypes[eventNum].toString();
            }
    
            if (eventName.equals("NOTE_ON_EVENT") && deltaTimes != 0 && mTempo != 0) {
                double bpm =  (60000000 / mTempo);
                deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));              
                log("DELTA_TIMES_MILLISECONDS", Double.toString(deltaTimesInMilliseconds));
                    }
       

             else if (eventName.equals("NOTE_OFF_EVENT") && deltaTimes != 0 && mTempo != 0) {
                double bpm =  (60000000 / mTempo);
                deltaTimesInMilliseconds = deltaTicks * (60000 / (bpm * division));
                log("DELTA_TIMES_MILISECONDS", Double.toString(deltaTimesInMilliseconds));
            }
      return new TrackChunkEvent(eventNum, eventName);
            }
...
...
}

Updating tempo on tempo event:

  if (firstByteOfMetaEvent == 0x51 && secondByteOfMetaEvent == 3) {
            int tempo = byteArrayToInt(parseMetaEventSetTempo());
            mTempo = tempo;
            return MetaEventsTypes.SET_TEMPO.ordinal();
        }

The MIDI file:

4d 54 68 64 00 00 00 06 00 01 00 02 01 80 4d 54 72 6b 00 00 00 13 00 ff 58 04 04 02 18 08 00 ff 51 03 08 52 ae 00 ff 2f 00 4d 54 72 6b 00 00 00 33 00 ff 03 15 45 6c 65 63 2e 20 50 69 61 6e 6f 20 28 43 6c 61 73 73 69 63 29 00 c0 00 82 20 90 3c 32 60 80 3c 00 83 00 90 41 32 8d 40 80 41 00 00 ff 2f 00

As an image: enter image description here

CodePudding user response:

First of all, your formula should work in your simple case. Please see my enter image description here

Now let's apply your formula to the second note's length in ticks:

enter image description here

Well...it's obviously wrong number. But fix is supereasy. The problem with your formala in this line:

double bpm =  (60000000 / mTempo);

You defined bpm as double, but you don't use double. In fact you have integer division and thus you lose precision which leads to bad result. Let's check new code:

enter image description here

Great! We have right number now. But I don't see 7000 here. I suppose your VLQ calculations are wrong. I mean this line:

int deltaTicks = Math.abs(calculateDeltaTicks(getVariableLengthQuantity()));

Here correct time and length of the second note in ticks so please check your numbers:

enter image description here

BUT your approach is wrong in general case. It will fail on most MIDI files. Your formula use two main assumptions:

  • there are no tempo changes in a file;
  • there are no other MIDI events between Note On and corresponding Note Off.

Both assumptions won't work in real world. To correctly calculate the length of a note in ms you need:

  • calculate time of Note On event in ms;
  • calculate time of corresponding Note Off event in ms;
  • take the difference between those times.

Calculation of time is simple if there are no tempo changes. You just need to calculate absolute time of an event summing all delta-times of previous events and then apply your formula.

But calculation of time with tempo changes requires more complex logic. You need to watch each range of constant tempo summing the length of each one in ms along the way until the time of a MIDI event falls in one of those ranges.

You're on Java so I can't recommend appropriate tool for that task. For .NET you can use my DryWetMIDI library which supports different formats of times and lengths correctly handling tempo changes.

  • Related