vishnubob / python-midi

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

Appending an event with a tick less than the last one in absolute mode results in invalid tick #169

Open MaurizioB opened 5 years ago

MaurizioB commented 5 years ago

When appending events in absolute mode and the tick of the event added is less than the previous events, the event order is kept, resulting in an invalid order if the mode is kept as absolute, or negative and invalid tick values if restored to relative.

pattern = midi.Pattern()
pattern.make_ticks_abs()
track = midi.Track()
pattern.append(track)
track.append(midi.NoteOffEvent(tick=50))
track.append(midi.NoteOnEvent(tick=0))

While, from the list inheritance point of view this makes sense, it does not from the midi file perspective. The previous code will result in an invalid order of absolute ticks (and inconsistent results in a saved file) and, most importantly, negative values whenever the pattern is set back to relative mode: in this case, the saved file results like this:

midi.Pattern(format=1, resolution=220, tracks=\
[midi.Track(\
    [midi.NoteOffEvent(tick=50, channel=0, data=[0, 0]),
    midi.NoteOnEvent(tick=268435406, channel=0, data=[0, 0])])])

I've not yet understood how the write_midifile() actually behaves whenever the make_ticks methods are inconsistent between pattern and tracks, maybe I'll do some digging about this. The workaround is to do a [track.sort(key=lambda e: e.tick) for track in pattern if not track.tick_relative] before actually exporting the midi file. Maybe it is better to implement append() and use bisect_right whenever the track is using the absolute tick mode, but the insert() method might need some further investigation too.