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.
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.