0
votes

I have a multi-track midi file that I'm reading with music21:

import music21

f = music21.midi.MidiFile()
f.open('1079-02.mid')
f.read()
stream = music21.midi.translate.midiFileToStream(f).flat
note_filter = music21.stream.filters.ClassFilter('Note')
for n in stream.recurse().addFilter(note_filter):
  offset = n.offset # offset from song start in beats
  note = n.pitch # letter of the note, e.g. C4, F5
  midi_note = n.pitch.midi # midi number of the pitch, e.g. 60, 72
  duration = n.duration # duration of the note in beats
  instrument = n.activeSite.getInstrument() # instrument voice

I'd like to figure out which track each note in this stream belongs to. E.g. when I open the file in GarageBand, the notes are organized into tracks:

enter image description here

In mido, each MidiFile has a tracks attribute that contains one list of notes for each track.

Is there a way to get the same with music21? Any help would be appreciated!

1
I hadn't seen that! I'd like to keep the stream method though because I want to merge all tracks into one stream so the time attribute is absolute rather than relative though. f.tracks exists and contains one entry for each track. But then the time is based on relative offsets within the given track...duhaime
web.mit.edu/music21/doc/moduleReference/… Can't you just change the tracks' data and call this method in the parent midi object ?Niloct
Hmm, how do you mean?duhaime

1 Answers

3
votes

The music tracks are parsed into separate stream.Part objects, so you can just walk through the parts of the stream.Score that you produced if you avoid flattening it (here, I've just produced a stream with converter.parse():

s = converter.parse('1079-02.mid')
for part in s.parts:
    for note in part.notes:
       print("I WAS IN PART ", part)

or look up the containing part:

s = converter.parse('1079-02.mid')
for note in s.recurse().notes:
    part = note.sites.getObjByClass('Part')
    print("I WAS IN PART ", part)

I doubt you really need to flatten anything. Good luck!