vishnubob / python-midi

Python MIDI library
MIT License
1.49k stars 370 forks source link

Get a note at the time its suposed to play #155

Closed Wall3rHM2 closed 5 years ago

Wall3rHM2 commented 5 years ago

Can someone help me get a note at the time its suposed to play in reading a file? e.g: After 2 secs a C note plays then after 1 sec, a G note.

MaurizioB commented 5 years ago

First, you need to know the tick resolution from Pattern.resolution, which is the resolution per beat (the smallest fraction of a beat in which events can happen). It is better to convert all the pattern to absolute ticks, as they are relative by default, using Pattern.make_ticks_abs(). Then you have to iter through all events in all tracks to map all tempo events SetTempoEvents and then interpolate those changes. It's not that easy if there are tempo changes in the file. If you are lucky and only have one tempo event (or none, which means tempo is default, 120bpm) it's not that hard, as you can compute everything by dividing 60 by bpm and resolution just once; here's a simple example:

notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
#uncomment this if you want absolute time
#pattern.make_ticks_abs()

tickLength = None
for track in pattern:
    for event in track:
        if isinstance(event, midi.SetTempoEvent):
            tickLength = 60. / event.bpm / pattern.resolution
            break
    else:
        continue
    break
else:
    tickLength = .5 / pattern.resolution

def getSeconds(tick):
    return tickLength * tick

for track in pattern:
    for event in track:
        if isinstance(event, midi.NoteEvent):
            octave, note = divmod(event.pitch, 12)
            print('Note {}{} plays at {:.02f} seconds'.format(
                notes[note], octave, getSeconds(event.tick)))

Also, have a look at this: #62

Wall3rHM2 commented 5 years ago

Thank you very much, there was before an related issue in the original python-midi but i couldn't get help.