diff --git a/polytrackermidi/exporters/midi.py b/polytrackermidi/exporters/midi.py index ff2fe1c..ef931e0 100644 --- a/polytrackermidi/exporters/midi.py +++ b/polytrackermidi/exporters/midi.py @@ -108,7 +108,13 @@ def generate_midi(self, midi_file: MIDIFile = None, tempo = 60 # In BPM midi_tracks_count = len(instruments) - midi_file = MIDIFile(midi_tracks_count) + + # There is a bug in MIDIUtil that sometimes crashes when a + # note played by the same instrument is overlapping with + # itself: https://github.com/MarkCWirt/MIDIUtil/issues/34 + # + # As a workaround, skip removing duplicate MIDI events. + midi_file = MIDIFile(midi_tracks_count, removeDuplicates=False) midi_file.addTempo(track=0, time=0, tempo=self.tempo_bpm) for i in range(len(instruments)): @@ -203,14 +209,17 @@ def generate_midi(self, midi_file: MIDIFile = None, # so we going to jsut shorten its duration arp_note_duration = arp_end_time - arp_note_start_time - midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], - channel=channel, - pitch=PatternToMidiExporter.get_midi_note_value(note), - time=start_time_offset + arp_note_start_time, - duration=arp_note_duration, - # TODO: write velocity fx value if set (needs to be converted to 0...127!!!) - volume=default_volume, - ) + # Workaround for MIDIUtil, which crashes if a note has a duration of 0. + # See also: https://github.com/MarkCWirt/MIDIUtil/issues/34 + if arp_note_duration >= 1 / midi_file.ticks_per_quarternote: + midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], + channel=channel, + pitch=PatternToMidiExporter.get_midi_note_value(note), + time=start_time_offset + arp_note_start_time, + duration=arp_note_duration, + # TODO: write velocity fx value if set (needs to be converted to 0...127!!!) + volume=default_volume, + ) # increment starting time for the next note arp_note_start_time += arp_note_duration @@ -231,13 +240,9 @@ def generate_midi(self, midi_file: MIDIFile = None, ) else: - # note that there is a bug in MIDIUtil - # that sometimes crashes when a note played by the same - # instrument is overlapping with itself - # https://github.com/MarkCWirt/MIDIUtil/issues/34 - # there is also a bug with track numbers/note values overlapping - # that is also described in the same deInterlaveNotes method in MIDIUtil - + # note that there is a bug in MIDIUtil with track numbers /note values + # overlapping that is described in the deInterlaveNotes method in MIDIUtil: + # https://github.com/MarkCWirt/MIDIUtil/blob/8f858794b03fcbfdd9d689ac39cf0f9a6792e416/src/midiutil/MidiFile.py#L873-L876 # default case - just a regular single note playing midi_file.addNote(track=instrument_to_midi_track_map[step.instrument_number], @@ -287,7 +292,7 @@ def generate_midi(self) -> MIDIFile: # this should be faster than calling instruments.indexOf() instrument_to_midi_track_map = {} - midi_file = MIDIFile(midi_tracks_count) + midi_file = MIDIFile(midi_tracks_count, removeDuplicates=False) #FIXME: write bpm to song to get it from there midi_file.addTempo(track=0, time=0, tempo=self.song.bpm)