Migrating to music21 v11

(This is an in-progress guide that was automatically generated by Myke’s AI Agent.)

What changed between the v10 line and v11, and what to use instead. The big, code-breaking items come first, then smaller improvements by area, then one table of everything removed or deprecated.

Python 3.12 or newer

Python 3.11 support is gone; 3.12, 3.13, and 3.14 are tested. The payoff is real generics: Stream and its iterators use the PEP 695 bracket form, so a type checker can follow stream.Stream[note.Note]() all the way to for n in s: n.pitch.

Octaves are always integers

In one line: octave is always an int, and octaveIsImplicit says whether you gave one.

Before v11, pitch.Pitch('F#').octave was None: a lovely idea (an F-sharp in any octave) that crashed the moment someone wrote p.octave + 1. The pitch knew all along which octave it would use for MIDI or a staff. It kept that in implicitOctave, a property that nobody remembered. Now octave does that job itself:

>>> from music21 import *
>>> anyFSharp = pitch.Pitch('F#')
>>> anyFSharp.octave
4
>>> anyFSharp.octaveIsImplicit
True

Nothing else about an octave-less pitch changes. It still prints without a number, transposes without one, and is not equal to an explicit F#4:

>>> anyFSharp
<music21.pitch.Pitch F#>
>>> anyFSharp.transpose('P8')
<music21.pitch.Pitch F#>
>>> anyFSharp == pitch.Pitch('F#4')
False

Give it an octave and the flag flips:

>>> anyFSharp.octave = 5
>>> anyFSharp.octaveIsImplicit
False
>>> anyFSharp
<music21.pitch.Pitch F#5>

Notes follow along: note.Note('B-').octave is 4, and the flag lives on the note’s pitch, n.pitch.octaveIsImplicit.

Before v11

v11

if p.octave is None:

if p.octaveIsImplicit:

p.octave = None

p.octaveIsImplicit = True (the old spelling is deprecated; removed in v13)

p.implicitOctave

p.octave

if p.octave is None: p.octave = p.implicitOctave

p.octaveIsImplicit = False

octave: int | None

octave: int

p.octave + 1 and f'{p.name}{p.octave}' no longer crash. The few methods that need a pitch’s own octave, such as transposeAboveTarget(), still raise for an implicit one; set .octave first. implicitOctave stays as a synonym so nothing breaks today, but it is deprecated and starts warning in v12.

KeySignature.sharps is always an int

Similar idea to Octave. KeySignature.sharps (and Key.sharps) is an int you can add to and subtract from. A non-traditional signature is a flag plus a list of pitches. sharps=None still works but warns:

>>> unusual = key.KeySignature()
>>> unusual.isNonTraditional = True
>>> unusual.alteredPitches = ['E-', 'G#4']
>>> unusual
<music21.key.KeySignature of pitches: [E-, G#4]>

Chord indexing gets simpler

c[1], c['G4'] and c[somePitch] still return the chord’s Note; everything fancier is gone. Attribute paths such as c['2.tie'] and c['D-4.style.color'] no longer work, a Note is no longer accepted as a key (use its pitch), and the per-note getters and setters (getTie, setColor, getNotehead, and their siblings) are deprecated. Index, then use the Note:

>>> c = chord.Chord('C4 E4 G4')
>>> c['E4'].tie = tie.Tie('start')
>>> c[1].tie
<music21.tie.Tie start>

VoiceLeadingQuartet needs its four notes

voiceLeading.VoiceLeadingQuartet(v1n1, v1n2, v2n1, v2n2) requires all four notes up front, the first Music21Object ever to insist on arguments. In return they are always Notes, never None. analyticKey is now key, vIntervals and hIntervals are tuples, and two dogmatic names got truer ones: opensIncorrectly() is not modalOpening() and closesIncorrectly() is not clausulaVera().

Roman numerals

  • romanNumeralFromChord keeps the sharp on major-quality chords built on the raised sixth and seventh degrees in minor: #VI, not VI#63. correctRNAlterationForMinor gained a chordHasMajorThird keyword for the same reason.

  • Figures containing x, y or z raise. RomanNumeral('IIIx') used to be a III13 chord by accident.

  • FigureTuple and PitchFigureTuple are typed NamedTuples, and FigureTuple.fromPitchAndReference() replaces figureTupleSolo.

Durations and sorting

  • Two Durations with expressionIsInferred True are equal when their quarterLengths match; type, dots and tuplets are free to be re-expressed.

  • sorting.SortTuple is a modern NamedTuple. priority and classSortOrder may be floats, and modify() with a bad field name raises ValueError.

  • tinyNotation’s undocumented 0 duration (a whole bar plus a fermata) is deprecated.

Streams

  • New Stream.isAtEnd(el) says whether storeAtEnd put el there.

  • Stream.hasElement(el) is removed: write el in s. hasElementOfClass is deprecated: write if s.getElementsByClass(X):.

Pitches

pitch.simplifyMultipleEnharmonics takes criterion and keyContext as keyword-only arguments.

File formats

  • ABC: w: lyric lines are imported, with hyphenation and * skips. abcToStreamOpus always returns an Opus.

  • Humdrum: grace notes keep their written duration instead of becoming eighths; duration parsing lives in a new hdStringToDuration; a token with no duration warns when a quarter is assumed; GlobalReference is no longer a Music21Object.

  • LilyPond: output targets current LilyPond (\tuplet, modern barline names, \markuplist), and every stringOutput() returns a str.

  • MIDI: midiEventsToInstrument is removed; use midiEventToInstrument.

  • Vexflow: the music21.vexflow module is gone. It had not worked in a decade; music21j is the way to draw in a browser.

  • configure: choosing MuseScore as the MusicXML reader also sets musescoreDirectPNGPath.

figuredBass, features, tree

  • figuredBass: hiddenFifth and hiddenOctave are now hiddenFifths and hiddenOctaves, matching parallelFifths. FiguredBassScale’s first argument is scaleTonic, not scaleValue. Rests inside a possibility are typed sentinels rather than the string 'RT'.

  • features: Feature.vector starts as [] rather than None, and FeatureExtractor.dimensions defaults to 1.

  • tree: ElementTimespan and PitchedTimespan take their arguments in the same order as Timespan; offset, endTime and element are required.

For developers

  • Type annotations across nearly the whole library, with t.cast() for narrowing and @property decorators throughout.

  • common.enums.ContainsEnum is not needed: Python 3.12’s StrEnum suffices, or HexEnum for hex values such as MIDI. The alias leaves in v12. common.defaultlist is deprecated.

  • The test runners import modules the normal way, so a module’s tests no longer need from music21.key import KeySignature to compare types.

  • AI agents get their own instructions, skills and shared memory in AGENTS.md and .agents/.

Removed and deprecated

Removed in v11

Gone

Use instead

Python 3.11

Python 3.12 or newer

music21.vexflow

music21j

midi.translate.midiEventsToInstrument

midiEventToInstrument

Stream.hasElement(el)

el in s

musicxml.xmlToM21.MusicXMLImporter.identificationToMetadata

addIdentificationToMetadata

a string for musicxml.xmlToM21.MeasureParser.getStaffNumber

an int

VoiceLeadingQuartet.unison / .fifth / .octave

interval.Interval('P1') and friends

c['2.tie'] attribute paths into a Chord

c[2].tie

a Note as a Chord index, c[someNote]

c[someNote.pitch]

Deprecated in v11 (removed in v12 unless noted)

Deprecated

Use instead

Stream.hasElementOfClass(X)

if s.getElementsByClass(X):

Chord.getTie, setTie, getColor, setColor, getNotehead, setNotehead, getNoteheadFill, setNoteheadFill, getStemDirection, setStemDirection

c[i].tie, c[i].style.color, c[i].notehead, c[i].noteheadFill, c[i].stemDirection

roman.figureTupleSolo

FigureTuple.fromPitchAndReference()

VoiceLeadingQuartet.opensIncorrectly() / closesIncorrectly()

not modalOpening() / not clausulaVera()

KeySignature(sharps=None)

ks.isNonTraditional = True

variant.addVariant(replacementDuration=...)

replacementQuarterLength=...

common.defaultlist

a list or a dict

common.enums.ContainsEnum

not needed: enum.StrEnum suffices, or HexEnum for hex values such as MIDI

tinyNotation 0 duration

the duration plus an expressions.Fermata

Pitch.implicitOctave

Pitch.octave (warns from v12, removed later)