midi-json-parser icon indicating copy to clipboard operation
midi-json-parser copied to clipboard

Determine the total length of a midi file

Open tqwewe opened this issue 4 years ago • 12 comments

I've created a function to convert the delta to miliseconds:

const microsecondsPerQuarter = (
  midi.tracks[0].find(({ setTempo }) => setTempo).setTempo
).microsecondsPerQuarter;

const deltaToMs = (delta, bpm = 110) =>
  (60_000_000 / (bpm * microsecondsPerQuarter)) * delta;

I'm not sure if this is even correct, but it seems to work for now. https://github.com/chrisguttandin/midi-json-parser/issues/305 Says something about divisions but I'm not sure how to use that.

From here, how can I determine the total miliseconds of the midi track in order for a perfect loop?

tqwewe avatar Jul 01 '21 14:07 tqwewe

Assuming you only have one setTempo event for the entire track you could do it like this.

seconds = delta * microsecondsPerQuarter / division / 1000000

chrisguttandin avatar Jul 01 '21 16:07 chrisguttandin

Hi thanks for your reply!

Where does delta come from exactly?

My data looks like this:

{
  "division": 384,
  "format": 1,
  "tracks": [
    [
      {
        "timeSignature": {
          "denominator": 4,
          "metronome": 24,
          "numerator": 4,
          "thirtyseconds": 8
        },
        "delta": 0
      },
      {
        "setTempo": {
          "microsecondsPerQuarter": 545454
        },
        "delta": 0
      },
      {
        "endOfTrack": true,
        "delta": 0
      }
    ],
    [
      {
        "trackName": "Electric Piano",
        "delta": 0
      },
      {
        "programChange": {
          "programNumber": 0
        },
        "channel": 0,
        "delta": 0
      },
      {
        "noteOn": {
          "noteNumber": 59,
          "velocity": 50
        },
        "channel": 0,
        "delta": 0
      },
      {
        "noteOff": {
          "noteNumber": 59,
          "velocity": 0
        },
        "channel": 0,
        "delta": 96
      },
      ...
      {
        "endOfTrack": true,
        "delta": 0
      }
    ]
  ]
}

tqwewe avatar Jul 01 '21 17:07 tqwewe

It's the delta of the event. Let's say you want to figure out the time of the noteOff event in your example. Then it would be:

seconds = 96 * 545454 / 384 / 1000000

In other words the first node stops after about 0.136 seconds.

chrisguttandin avatar Jul 01 '21 17:07 chrisguttandin

Ah yep I managed to calculate that. But I'm curious to get the entire duration for a perfect loop. For example, if the last note is not at the end of the bar, then there would be some space after of silence

tqwewe avatar Jul 01 '21 17:07 tqwewe

I guess there is no way to derive that information from the file. If you know that your file contains the events for 16 beats you could just use microsecondsPerQuarter * 16 as the length of the loop. But if the last note ends at beat 15 then this will be the end of the MIDI file.

chrisguttandin avatar Jul 01 '21 23:07 chrisguttandin

If I can know how many beats my file contains, then that'd be perfect. But I cannot find how to get that information.

Importing the file into Ableton live, it detects the full length correctly. I used this website to write the midi: https://onlinesequencer.net/ And Ableton was able to get the correct lenght of my midi track.

I think the information must be in the file somewhere.

tqwewe avatar Jul 02 '21 11:07 tqwewe

What's the time difference between your last event in the MIDI file and the expected duration that Ableton detects?

chrisguttandin avatar Jul 03 '21 02:07 chrisguttandin

I created a midi using onlinesequencer.net.

I marked with a red line where I'd expect the loop to occur, and this is also where Ableton puts the loop (at the end of bar 2):

Midi Example

tqwewe avatar Jul 03 '21 07:07 tqwewe

Maybe Ableton just rounds to the next full bar. You can use the denominator of the timeSignature event to determine how many beats a bar has.

chrisguttandin avatar Jul 03 '21 11:07 chrisguttandin

Hmm I'm still trying to figure out how to use the denominator to calculate this.

tqwewe avatar Jul 07 '21 14:07 tqwewe

I'm not pretending that I understood the ins and outs of the timeSignature event. I guess it takes years to understand it completely. :-)

Anyway, as far as I understand a denominator of 4 says a bar has 4 beats. And the microsecondsPerQuarter says a beat is 545454 ticks. When we convert this to seconds using the formula from above we end up with about 136ms.

Given that a bar has 4 beats a bar is about 544ms long. So if we want to end up with a full bar we need to make sure the length is a multiple of 544ms or 2181816 ticks (= 545454 * 4 ticks).

By looking at the image from above I guess the accumulated delta of your last noteOff event is about 3409088 ticks (something close to 545454 * 6.25 ticks).

If you now pick the next multiple of 4 you end up with 8.

Does that make sense?

chrisguttandin avatar Jul 07 '21 18:07 chrisguttandin

Thanks so much for this reply! I'm trying it out now. I'm just a little confused why you chose to include the noteOff 96 ticks in the equasion, I guess because it's the last note off in the track?

I'll let you know how I go

tqwewe avatar Jul 07 '21 19:07 tqwewe