Search code examples
pythonparsingmidimusic21musicxml

How do I read midi / musicxml files in music21 for solo piano where there can be multiple notes in a voice simultaneously?


I have written a python script to process midi files with music21 and write again a midi file. This works if the solo piano is "simple" in the sense, that there are not multiple pitches / notes played simultaneously in a voice.

https://github.com/githubuser1983/algorithmic_python_music/blob/main/12RootOf2.py

The relevant part from above is:

def parseMidi(fp,part=0):
    import os
    from music21 import converter
    print(fp)
    score = converter.parse(fp,quantizePost=True)
    print(list(score.elements[0].notesAndRests))
    #print([e.partAbbreviation for e in score.elements][0])
    from music21 import chord
    durs = []
    ll0 = []
    vols = []
    isPauses = []
    for p in score.elements[part].notesAndRests:
        #print(p)
        if type(p)==chord.Chord:
            pitches = median([e.pitch.midi-21 for e in p]) # todo: think about chords
            vol = median([e.volume.velocity for e in p])
            dur = float(p.duration.quarterLength)
            #print(pitches)
            ll0.append(pitches)
            isPause = False
        elif (p.name=="rest"):
            pitches = 89
            vol = 1
            dur = float(p.duration.quarterLength)
            ll0.append(pitches)
            isPause = True
        else:
            pitches = p.pitch.midi-21
            vol = p.volume.velocity
            dur = float(p.duration.quarterLength)
            ll0.append(pitches)
            isPause =  False
        durs.append(dur/(12*4.0))
        vols.append(vol*1.0/127.0)
        isPauses.append(isPause)
            #print(p.name,p.octave,p.duration.quarterLength)
    #print(dir(score)) 
    #print(ll0)
    #print(durs)
    return ll0,durs,vols,isPauses

Another option would be to to read musicxml instead of midi. What I need for the algorithm to work, is a list of note(s) = (pitch, duration, volume, isPause) for each voice.

Thanks for your help.


Solution

  • Currently, in music21, stream.Voice objects are more of a display concept than a logical concept. Voices and Chords are both simultaneities, and that's all that a MIDI file captures. (In fact, there are pending changes in version 7, to be released this week, that make fewer voices and more chords from MIDI files, in addition to making measures. If there are small overlaps from reverb or from a recorded performance you may get "voices" that an engraver would never print in sheet music.)

    In your case, I would probably just take a .flat of the Part object to get rid of Voices (and eventually Measures in v.7), and then run chordify() if you want to ensure there are no overlaps. Otherwise, if you don't want chords at all, you can still take the output of chordify() and find the root of each chord. Several possibilities that all depend on what your sources look like.