Search code examples
pythonmusic21

music21 : given midi input, output correctly-spelled pitches with octave numbers


See Edit below for a work-in-progress MRE

I'm trying to transform the text output of music21 to include octave numbers and correct enharmonic spelling.

As background, I'm a javascript programmer, new to music21 and python.

The following

myMusic = converter.parse("midi")
myMusic.show("text")

yields

Time      Actual output      Desired Output 
{0.0} <music21.note.Note C>      C4
{0.1} <music21.note.Note D#>     E-4
{0.2} <music21.note.Note G>      G4
{0.3} <music21.note.Note G>      G4

Where there are two issues:

  1. How do I include the octave in the text output? (Interestingly, when there are chords, the octave of each note does appear.

  2. The D# should be an Eb, i.e., should be interpreted in a tonal context. I'd like to run EnharmonicSimplifier.bestPitches() on the whole parsed midi file to correct this, but from the docs, it appears that it can only be run on a note list.

Am I going about this wrong? Should I be outputting to a different format to get this info? I need timepoints (offset is ok), octave numbers, and correctly-spelled pitches. Maybe I'm missing intermediate processing?

Any guidance appreciated.

EDIT: Work in progress MRE, solves issue 1. (badly?), but not 2.

from music21 import *
environment.set('autoDownload', 'allow')

stream1 = converter.parse("https://upload.wikimedia.org/wikipedia/commons/5/55/MIDI_sample.mid")

for n in stream1.recurse().notes:
  try: 
    print(n.offset, n.nameWithOctave)
  except Exception as e: 
    print(n.offset, *n.pitches)

Yields (excerpted)

237.5 C2
238.0 F#2
238.2 F#2
238.5 C2 F#2
238.7 B-2

Solution

  • 1

    .show() is nice at a glance when debugging but not ideal for structured output. Have a look at recurse(). music21 has a container ontology: objects are "in" voices, "in" measures, "in" parts, "in" scores. So if you start top-down from a score and want to walk every nested container, just use recurse():

    for n in myStream.recurse().notes:
       print(n.offset, ' '.join(p.nameWithOctave for p in n.pitches))
    

    Note properties: http://web.mit.edu/music21/doc/moduleReference/moduleNote.html

    2

    simplifyMultipleEnharmonics() takes an iterable of pitches (or things that can be converted to pitches, but faster to give it the pitches if you've got 'em). Every Note or Chord object has a pitches attribute, so you can safely call .pitches on Notes or Chords while looping through your parsed file and send that tuple of pitches to simplifyMultipleEnharmonics, along with your Key object.

    for n in myStream.recurse().notes:
      closest_key = n.getContextByClass(key.Key)
      n.pitches = pitch.simplifyMultipleEnharmonics(n.pitches, keyContext=closest_key)