MIDIUtil
MIDIUtil copied to clipboard
IndexError with overlapping notes
When the caller adds two overlapping notes (same time, same pitch) with different durations, then writeFile() causes the following exception to be raised in deInterleaveNotes():
File "./midiutil-overlapping-note-bug.py", line 16, in <module>
midifile.writeFile(sys.stdout.buffer)
File "[...]/midiutil/MidiFile.py", line 1637, in writeFile
self.close()
File "[...]/midiutil/MidiFile.py", line 1688, in close
self.tracks[i].closeTrack()
File "[...]/midiutil/MidiFile.py", line 826, in closeTrack
self.processEventList()
File "[...]/midiutil/MidiFile.py", line 789, in processEventList
self.deInterleaveNotes()
File "[...]/midiutil/MidiFile.py", line 889, in deInterleaveNotes
stack[noteeventkey].pop()
IndexError: pop from empty list
(I think it's confused when a note-off event comes before the corresponding note-on event in the stream. Are the events sorted funny?)
The problem does not seem to arise when the overlapping notes have the exact same duration.
One could argue that it's the caller's responsibility not to add overlapping notes, but it's easy to do so by accident so it would be nice if the library did one of the following:
- merge the notes when writing the midi file (keep the one with the longest duration?)
- merge the notes when adding them
- silently drop overlapping notes when they are added, keeping only the first note
- raise an easy-to-understand exception saying something about overlapping notes
Here's a minimal example to reproduce the error:
#! /usr/bin/env python3
import sys
from midiutil.MidiFile import MIDIFile
TRACK = CHANNEL = TIME = 0
VOLUME = 100
DURATION1 = 1.0
DURATION2 = 2.0
PITCH = 42
midifile = MIDIFile(1, adjust_origin=False)
midifile.addNote(TRACK, CHANNEL, PITCH, TIME, DURATION1, VOLUME)
midifile.addNote(TRACK, CHANNEL, PITCH, TIME, DURATION2, VOLUME)
midifile.writeFile(sys.stdout.buffer)
I'm using MIDIUtil==1.2.1 from pip.
I tried changing the code in file MidiFile.py line 889 form
else:
stack[noteeventkey].pop()
tempEventList.append(event)
to
else:
if not stack[noteeventkey]==[]:
stack[noteeventkey].pop()
tempEventList.append(event)
and it works
I had same error which I fixed with : elif len(stack[noteeventkey]) == 1: stack[noteeventkey].pop() tempEventList.append(event)
Its a really great module thanks so much for all the hard work
I had another error from the section before
KeyError 450
line 878 elif event.evtname == 'NoteOff': if len(stack[noteeventkey]) > 1: event.tick = stack[noteeventkey].pop() tempEventList.append(event)
I don't know how to reproduce, but this was from the result of scanning hundreds of Ableton XML midi files and converting them to .mid
I put a temp fix in with Try/Except
Sorry I cant help more
I am just making the assumption that there could be some badly formed inputs and covering all bases
I have also had the strange IndexError: pop from empty list even though I shouldn't have duplicate notes, and after trying @ToverPomelo's solution I get: KeyError: '580'
For me, I didn't think I had any duplicate notes but I kept getting the error so I just ignored it:
else:
try:
stack[noteeventkey].pop()
tempEventList.append(event)
except IndexError as e:
print("IndexError skipped")
I found a workaround until a solution is found for this library.
set deinterleave to False
self.mid = MIDIFile(
numTracks=num_tracks,
removeDuplicates=True,
deinterleave=False,
adjust_origin=False,
file_format=2,
ticks_per_quarternote=960,
eventtime_is_ticks=True)