0

I want to convert a sequence of notes (defined by time, duration, pitch) to a audio-file. For doing so, I thought creating a midi first and then compile it to wav is the way to go.

I'm quite new to audio processing and MIDI-Files, so even though I read several tutorials, it can be, that I didn't get the point.

Edit: I found the problem, see solution below.

What's the problem

Writing notes at a specific time with a specific duration via python's MIDIUtil doesn't work as expected. In fact, the time in seconds, where a note is placed heavily depends on the track's bpm, even though I think I took the bpm into account, when converting the note time to MIDI's time measure in quarter notes.

What I've tried

I'm creating a MIDI track with a given bpm. Then I'm converting a note's event time via t_{quarter} = t_{seconds} * bpm/60

Example

I'm writing with the following code two notes, the last at t=5 seconds with a duration of 1s; i.e. I'm expecting a midi-file with lasts 6seconds. But at a bpm=600, the file is 14s long. At a bpm=100 it's almost the expected 6s.

Here's my code


from midiutil import MIDIFile

def convert_seconds_to_quarter(time_in_sec, bpm):
    quarter_per_second = (bpm/60)
    time_in_quarter = time_in_sec * quarter_per_second
    return time_in_quarter

def write_test_midi():
    bpm = 600

    MyMIDI = MIDIFile(1)
    MyMIDI.addTrackName(track=0, time=0, trackName="Sample Track")
    MyMIDI.addTempo(track=0, time=0, tempo=bpm)

    MyMIDI.addNote(track=0, channel=0, pitch=60,
                   time=convert_seconds_to_quarter(1, bpm),
                   duration=convert_seconds_to_quarter(1, bpm), volume=100)
    MyMIDI.addNote(track=0, channel=0, pitch=60,
                   time=convert_seconds_to_quarter(5, bpm),
                   duration=convert_seconds_to_quarter(1, bpm), volume=100)

    with open("/tmp/output.mid", 'wb') as binfile:
        MyMIDI.writeFile(binfile)

Additional infos

The hex content of the file with bpm=100:

ADDRESS        00 01 02 03   04 05 06 07   08 09 0a 0b   0c 0d 0e 0f       ASCII
00000010       4d 54 68 64   00 00 00 06   00 01 00 02   03 c0 4d 54       MThd..........MT
00000020       72 6b 00 00   00 0b 00 ff   51 03 09 27   c0 00 ff 2f       rk......Q..'.../
00000030       00 4d 54 72   6b 00 00 00   28 00 ff 03   0c 53 61 6d       .MTrk...(....Sam
00000040       70 6c 65 20   54 72 61 63   6b 8c 40 90   3c 64 8c 40       ple.Track.@.<d.@
00000050       80 3c 64 a5   40 90 3c 64   8c 40 80 3c   64 00 ff 2f       .<d.@.<d.@.<d../
00000060       00 00 00 00                                                 .

The content of the file with bpm=600:

ADDRESS        00 01 02 03   04 05 06 07   08 09 0a 0b   0c 0d 0e 0f       ASCII
00000010       4d 54 68 64   00 00 00 06   00 01 00 02   03 c0 4d 54       MThd..........MT
00000020       72 6b 00 00   00 0b 00 ff   51 03 01 86   a0 00 ff 2f       rk......Q....../
00000030       00 4d 54 72   6b 00 00 00   29 00 ff 03   0c 53 61 6d       .MTrk...)....Sam
00000040       70 6c 65 20   54 72 61 63   6b cb 00 90   3c 64 cb 00       ple.Track...<d..
00000050       80 3c 64 81   e1 00 90 3c   64 cb 00 80   3c 64 00 ff       .<d....<d...<d..
00000060       2f 00 00 00                                                 /.

Solution

The code, conversion function and files I posted are all correct. The problem was the VLC player which I used to listening to the midi.

physicus
  • 361
  • 4
  • 12
  • 1
    You wrote two sentences with "at a bpm=600". Is this correct? – CL. Mar 29 '20 at 08:36
  • Thx for your hint! This was a typo, I fixed it! ((I wanted to say "at bpm=100 it's almost the expected 6s". – physicus Mar 29 '20 at 12:19
  • Changing `bpm` should not change the absolute time of events. How are you measuring the length of the MIDI file? Can you show the actual file contents? – CL. Mar 30 '20 at 08:07
  • Do you mean, changing `bpm` shouldn't alter the absolute time in seconds or the absolute time in ticks/beats/quarter notes? The absolute time in seconds should change when everything is left constant. In my case, I'm trying to figure out, how I need to change the absolute time in beats/ticks/quarter with the bpm in order to keep the absolute timing in seconds constant. About the midi file contant. I read it with a hex viewer, I hope this is, what you asked me for? Thx! – physicus Mar 30 '20 at 14:57

1 Answers1

1

first file:

delta     message            absolute time
time                         ticks  seconds

      ... 03 c0 ...                        960 ticks per quarter note
      ... ff 51 03 09 27 c0      0   0     tempo: 600000 microseconds per quarter note
8c 40     90 3c 64            1600   1     note on
8c 40     80 3c 64            3200   2     note off
a5 40     90 3c 64            8000   5     note on
8c 40     80 3c 64            9600   6     note off
00        ff 2f 00            9600   6     end of track

second file:

      ... 03 c0 ...                        960 ticks per quarter note
      ... ff 51 03 01 86 a0      0   0     tempo: 100000 microseconds per quarter note
cb 00     90 3c 64            9600   1     note on
cb 00     80 3c 64           19200   2     note off
81 e1 00  90 3c 64           48000   5     note on
cb 00     80 3c 64           57600   6     note off
00        ff 2f 00           57600   6     end of track

Both files are exactly six seconds long. Your code and the files are correct.

The problem is whatever tool you're using to process the files.

CL.
  • 173,858
  • 17
  • 217
  • 259
  • Thank you so much for helping me and parsing the hex! Indeed, my VLC Player for listening to the midi was problem. I'm now switching over to `timidity` for converting the midi to wav, and now I also see, that the files were correct! By the way, for being able to verify on my own the validity of my midis: Did you parse the midi hex "manually" or which tool did you use to extract the midi events and timings from the hex? – physicus Mar 30 '20 at 22:20
  • These few bytes can be parsed by hand. (This is of course easier for somebody who has written several MIDI parsers.) – CL. Mar 31 '20 at 08:45