music21.spanner

A spanner is a music21 object that represents a connection usually between two or more music21 objects that might live in different streams but need some sort of connection between them. A slur is one type of spanner – it might connect notes in different Measure objects or even between different parts.

This package defines some of the most common spanners. Other spanners can be found in modules such as music21.dynamics (for things such as crescendos).

Spanner

class music21.spanner.Spanner(*spannedElements: Music21Object | Sequence[Music21Object], **keywords)

Spanner objects live on Streams in the same manner as other Music21Objects, but represent and store connections between one or more other Music21Objects.

Commonly used Spanner subclasses include the Slur, RepeatBracket, Crescendo, and Diminuendo objects.

In some cases you will want to subclass Spanner for specific purposes.

In the first demo, we create a spanner to represent a written-out accelerando, such as Elliott Carter uses in his second string quartet (he marks them with an arrow).

>>> class CarterAccelerandoSign(spanner.Spanner):
...    pass
>>> n1 = note.Note('C4')
>>> n2 = note.Note('D4')
>>> n3 = note.Note('E4')
>>> sp1 = CarterAccelerandoSign(n1, n2, n3)  # or as a list: [n1, n2, n3]
>>> sp1.getSpannedElements()
[<music21.note.Note C>, <music21.note.Note D>, <music21.note.Note E>]

We can iterate over a spanner to get the contexts:

>>> print(' '.join([repr(n) for n in sp1]))
<music21.note.Note C> <music21.note.Note D> <music21.note.Note E>

Now we put the notes and the spanner into a Stream object. Note that the convention is to put the spanner at the beginning of the innermost Stream that contains all the Spanners:

>>> s = stream.Stream()
>>> s.append([n1, n2, n3])
>>> s.insert(0, sp1)

Now we can get at the spanner in one of three ways.

  1. it is just a normal element in the stream:

>>> for e in s:
...    print(e)
<music21.note.Note C>
<music21.CarterAccelerandoSign <music21.note.Note C><music21.note.Note D><music21.note.Note E>>
<music21.note.Note D>
<music21.note.Note E>
  1. we can get a stream of spanners (equiv. to getElementsByClass(spanner.Spanner)) by calling the .spanner property on the stream.

>>> spannerCollection = s.spanners  # a stream object
>>> for thisSpanner in spannerCollection:
...     print(thisSpanner)
<music21.CarterAccelerandoSign <music21.note.Note C><music21.note.Note D><music21.note.Note E>>

(3) we can get the spanner by looking at the list getSpannerSites() on any object that has a spanner:

>>> n2.getSpannerSites()
[<music21.CarterAccelerandoSign
        <music21.note.Note C><music21.note.Note D><music21.note.Note E>>]

In this example we will slur a few notes and then iterate over the stream to see which are slurred:

>>> n1 = note.Note('C4')
>>> n2 = note.Note('D4')
>>> n3 = note.Note('E4')
>>> n4 = note.Note('F4')
>>> n5 = note.Note('G4')
>>> n6 = note.Note('A4')

Create a slur over the second and third notes at instantiation:

>>> slur1 = spanner.Slur([n2, n3])

Slur the fifth and the sixth notes by adding them to an existing slur:

>>> slur2 = spanner.Slur()
>>> slur2.addSpannedElements([n5, n6])

Now add them all to a stream:

>>> part1 = stream.Part()
>>> part1.append([n1, n2, n3, n4, n5, n6])
>>> part1.insert(0, slur1)
>>> part1.insert(0, slur2)

Say we wanted to know which notes in a piece started a slur, here’s how we could do it:

>>> for n in part1.notes:
...    ss = n.getSpannerSites()
...    for thisSpanner in ss:
...       if 'Slur' in thisSpanner.classes:
...            if thisSpanner.isFirst(n):
...                print(n.nameWithOctave)
D4
G4

Alternatively, you could iterate over the spanners of part1 and get their first elements:

>>> for thisSpanner in part1.spanners:
...     firstNote = thisSpanner.getSpannedElements()[0]
...     print(firstNote.nameWithOctave)
D4
G4

The second method is shorter, but the first is likely to be useful in cases where you are doing other things to each note object along the way.

Oh, and of course, slurs do print properly in musicxml:

>>> part1.show()
../_images/slur1_example.png

(the Carter example would not print an arrow since that element has no corresponding musicxml representation).

Implementation notes:

The elements that are included in a spanner are stored in a Stream subclass called SpannerStorage found as the .spannerStorage attribute. That Stream has an attribute called client which links to the original spanner. Thus, spannerStorage is smart enough to know where it’s stored, but it makes deleting/garbage-collecting a spanner a tricky operation:

Ex. Prove that the spannedElement Stream is linked to container via client:

>>> sp1.spannerStorage.client is sp1
True

Spanners have a .completeStatus attribute which can be used to find out if all spanned elements have been added yet. It’s up to the processing agent to set this, but it could be useful in deciding where to append a spanner.

>>> sp1.completeStatus
False

When we’re done adding elements:

>>> sp1.completeStatus = True

Spanner bases

Spanner read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

Spanner read/write properties

Read/write properties inherited from Music21Object:

Spanner methods

Spanner.__getitem__(key)
>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> c1 = clef.BassClef()
>>> sl = spanner.Spanner(n1, n2, c1)
>>> sl[0] == n1
True
>>> sl[-1] == c1
True
>>> sl[clef.BassClef][0] == c1
True
Spanner.addSpannedElements(spannedElements: Sequence[Music21Object] | Music21Object, *otherElements: Music21Object)

Associate one or more elements with this Spanner.

The order in which elements are added is retained and may or may not be significant to the spanner.

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> n3 = note.Note('e')
>>> n4 = note.Note('d-')
>>> n5 = note.Note('c')
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements(n1)
>>> sl.addSpannedElements(n2, n3)
>>> sl.addSpannedElements([n4, n5])
>>> sl.getSpannedElementIds() == [id(n) for n in [n1, n2, n3, n4, n5]]
True
Spanner.fill(searchStream=None, *, includeEndBoundary: bool = False, mustFinishInSpan: bool = False, mustBeginInSpan: bool = True, includeElementsThatEndAtStart: bool = False)

Fills in the intermediate elements of a spanner, that are found in searchStream between the first element’s offset and the last element’s offset+duration. If searchStream is None, the first element’s activeSite is used. If the first element’s activeSite is None, a SpannerException is raised.

Ottava is an example of a Spanner that can be filled. The Ottava does not need to be inserted into the stream in order to be filled.

>>> m = stream.Measure([note.Note('A'), note.Note('B'), note.Note('C')])
>>> ott1 = spanner.Ottava(m.notes[0], m.notes[2])
>>> ott1.fill(m)
>>> ott1
<music21.spanner.Ottava 8va transposing<...Note A><...Note B><...Note C>>

If the searchStream is not passed in, fill still happens in this case, because the first note’s activeSite is used instead.

>>> ott2 = spanner.Ottava(m.notes[0], m.notes[2])
>>> ott2.fill()
>>> ott2
<music21.spanner.Ottava 8va transposing<...Note A><...Note B><...Note C>>

If the searchStream is not passed, and the spanner’s first element doesn’t have an activeSite, a SpannerException is raised.

>>> ott3 = spanner.Ottava(note.Note('D'), note.Note('E'))
>>> ott3.fill()
Traceback (most recent call last):
music21.spanner.SpannerException: ...requires a searchStream or getFirst().activeSite
Spanner.getFirst()

Get the object of the first spannedElement (or None if it’s an empty spanner)

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> n3 = note.Note('e')
>>> n4 = note.Note('c')
>>> n5 = note.Note('d-')
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements(n1, n2, n3, n4, n5)
>>> sl.getFirst() is n1
True
>>> spanner.Slur().getFirst() is None
True
Spanner.getLast()

Get the object of the last spannedElement (or None if it’s an empty spanner)

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> n3 = note.Note('e')
>>> n4 = note.Note('c')
>>> n5 = note.Note('d-')
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements(n1, n2, n3, n4, n5)
>>> sl.getLast() is n5
True
>>> spanner.Slur().getLast() is None
True
Spanner.getSpannedElementIds()

Return all id() for all stored objects. Was performance critical, until most uses removed in v7. Used only as a testing tool now. Spanner.__contains__() was optimized in 839c7e5.

Spanner.getSpannedElements()

Return all the elements of .spannerStorage for this Spanner as a list of Music21Objects.

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements(n1)
>>> sl.getSpannedElements() == [n1]
True
>>> sl.addSpannedElements(n2)
>>> sl.getSpannedElements() == [n1, n2]
True
>>> sl.getSpannedElementIds() == [id(n1), id(n2)]
True
>>> c1 = clef.TrebleClef()
>>> sl.addSpannedElements(c1)
>>> sl.getSpannedElements() == [n1, n2, c1]  # make sure that not sorting
True
Spanner.getSpannedElementsByClass(classFilterList)
>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> c1 = clef.AltoClef()
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements([n1, n2, c1])
>>> sl.getSpannedElementsByClass('Note') == [n1, n2]
True
>>> sl.getSpannedElementsByClass(clef.Clef) == [c1]
True
Spanner.hasSpannedElement(spannedElement: Music21Object) bool

Return True if this Spanner has the spannedElement.

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> span = spanner.Spanner()
>>> span.addSpannedElements(n1)
>>> span.hasSpannedElement(n1)
True
>>> span.hasSpannedElement(n2)
False

Note that a simple in does the same thing:

>>> n1 in span
True
>>> n2 in span
False
Spanner.isFirst(spannedElement)

Given a spannedElement, is it first?

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> n3 = note.Note('e')
>>> n4 = note.Note('c')
>>> n5 = note.Note('d-')
>>> sl = spanner.Spanner()
>>> sl.addSpannedElements(n1, n2, n3, n4, n5)
>>> sl.isFirst(n2)
False
>>> sl.isFirst(n1)
True
>>> sl.isLast(n1)
False
>>> sl.isLast(n5)
True
Spanner.isLast(spannedElement)

Given a spannedElement, is it last? Returns True or False

Spanner.purgeLocations(rescanIsDead=False)

Remove references to all locations in objects that no longer exist.

Spanner.purgeOrphans(excludeStorageStreams=True)

A Music21Object may, due to deep copying or other reasons, have a site (with an offset) which no longer contains the Music21Object. These lingering sites are called orphans. This method gets rid of them.

The excludeStorageStreams are SpannerStorage and VariantStorage.

Spanner.replaceSpannedElement(old, new) None

When copying a Spanner, we need to update the spanner with new references for copied (if the Notes of a Slur have been copied, that Slur’s Note references need references to the new Notes). Given the old spanned element, this method will replace the old with the new.

The old parameter can be either an object or object id.

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> c1 = clef.AltoClef()
>>> c2 = clef.BassClef()
>>> sl = spanner.Spanner(n1, n2, c1)
>>> sl.replaceSpannedElement(c1, c2)
>>> sl[-1] == c2
True

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

Spanner instance variables

Instance variables inherited from Music21Object:

Glissando

class music21.spanner.Glissando(*spannedElements, lineType: str = 'wavy', label: str | None = None, **keywords)

A between two Notes specifying a glissando or similar alteration. Different line types can be specified.

Glissandos can have a label and a lineType. Label is a string or None. lineType defaults to ‘wavy’

>>> gl = spanner.Glissando()
>>> gl.lineType
'wavy'
>>> print(gl.label)
None
>>> gl.label = 'gliss.'

Note – not a Line subclass for now, but that might change.

Glissando bases

Glissando read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

Glissando read/write properties

Glissando.lineType

Get or set the lineType property. See Line for valid line types.

Glissando.slideType

Get or set the slideType which determines how the glissando or slide is to be played. Values are ‘chromatic’ (default), ‘continuous’ (like a slide or smear), ‘diatonic’ (like a harp gliss), ‘white’ (meaning a white-key gliss as on a marimba), or ‘black’ (black-key gliss).

‘continuous’ slides export to MusicXML as a <slide> object. All others export as <glissando>.

Read/write properties inherited from Music21Object:

Glissando methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

Glissando instance variables

Instance variables inherited from Music21Object:

Line

class music21.spanner.Line(*spannedElements, lineType: str = 'solid', tick: str = 'down', startTick: str = 'down', endTick: str = 'down', startHeight: int | float | None = None, endHeight: int | float | None = None, **keywords)

A line or bracket represented as a spanner above two Notes.

Brackets can take many line types.

>>> b = spanner.Line()
>>> b.lineType = 'dotted'
>>> b.lineType
'dotted'
>>> b = spanner.Line(endHeight=20)
>>> b.endHeight
20

Line bases

Line read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

Line read/write properties

Line.endHeight

Get or set the endHeight property.

>>> b = spanner.Line()
>>> b.endHeight = -20
Traceback (most recent call last):
music21.spanner.SpannerException: not a valid value: -20
Line.endTick

Get or set the endTick property.

Line.lineType

Get or set the lineType property. Valid line types are listed in .validLineTypes.

>>> b = spanner.Line()
>>> b.lineType = 'dotted'
>>> b.lineType = 'navyblue'
Traceback (most recent call last):
music21.spanner.SpannerException: not a valid value: navyblue
>>> b.validLineTypes
('solid', 'dashed', 'dotted', 'wavy')
Line.startHeight

Get or set the startHeight property.

>>> b = spanner.Line()
>>> b.startHeight = None
Traceback (most recent call last):
music21.spanner.SpannerException: not a valid value: None
Line.startTick

Get or set the startTick property.

Line.tick

Set the start and end tick to the same value

>>> b = spanner.Line()
>>> b.tick = 'arrow'
>>> b.startTick
'arrow'
>>> b.endTick
'arrow'

Read/write properties inherited from Music21Object:

Line methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

Line instance variables

Instance variables inherited from Music21Object:

MultiMeasureRest

class music21.spanner.MultiMeasureRest(*spannedElements, **keywords)

A grouping symbol that indicates that a collection of rests lasts multiple measures.

MultiMeasureRest bases

MultiMeasureRest read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

MultiMeasureRest read/write properties

MultiMeasureRest.numRests

Returns the number of measures involved in the multi-measure rest.

Calculated automatically from the number of rests in the spanner. Or can be set manually to override the number.

>>> mmr = spanner.MultiMeasureRest()
>>> for i in range(6):
...     mmr.addSpannedElements([note.Rest(type='whole')])
>>> mmr.numRests
6
>>> mmr.numRests = 10
>>> mmr.numRests
10

Read/write properties inherited from Music21Object:

MultiMeasureRest methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

MultiMeasureRest instance variables

MultiMeasureRest.maxSymbols

An int, specifying the maximum number of rests to display as symbols. Default is 11. If useSymbols is False then this setting does nothing.

Change defaults.multiMeasureRestMaxSymbols to change globally.

MultiMeasureRest.useSymbols

Boolean to indicate whether rest symbols (breve, longa, etc.) should be used when displaying the rest. Your music21 inventor is a medievalist, so this defaults to True.

Change defaults.multiMeasureRestUseSymbols to change globally.

Instance variables inherited from Music21Object:

Ottava

class music21.spanner.Ottava(*spannedElements, type: str = '8va', transposing: bool = True, placement: Literal['above', 'below'] = 'above', **keywords)

An octave shift line:

>>> ottava = spanner.Ottava(type='8va')
>>> ottava.type
'8va'
>>> ottava.type = 15
>>> ottava.type
'15ma'
>>> ottava.type = (8, 'down')
>>> ottava.type
'8vb'
>>> print(ottava)
<music21.spanner.Ottava 8vb transposing>

An Ottava spanner can either be transposing or non-transposing. In a transposing Ottava spanner, the notes in the stream should be in their written octave (as if the spanner were not there) and all the notes in the spanner will be transposed on Stream.toSoundingPitch().

A non-transposing spanner has notes that are at the pitch that they would sound (therefore the Ottava spanner is a decorative line).

>>> ottava.transposing
True
>>> n1 = note.Note('D4')
>>> n2 = note.Note('E4')
>>> n2.offset = 2.0
>>> ottava.addSpannedElements([n1, n2])
>>> s = stream.Stream([ottava, n1, n2])
>>> s.atSoundingPitch = False
>>> s2 = s.toSoundingPitch()
>>> s2.show('text')
{0.0} <music21.spanner.Ottava 8vb non-transposing<music21.note.Note D><music21.note.Note E>>
{0.0} <music21.note.Note D>
{2.0} <music21.note.Note E>
>>> for n in s2.notes:
...     print(n.nameWithOctave)
D3
E3

All valid types are given below:

>>> ottava.validOttavaTypes
('8va', '8vb', '15ma', '15mb', '22da', '22db')

Ottava bases

Ottava read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

Ottava read/write properties

Ottava.type

Get or set Ottava type. This can be set by as complete string (such as 8va or 15mb) or with a pair specifying size and direction.

>>> os = spanner.Ottava()
>>> os.type = '8vb'
>>> os.type
'8vb'
>>> os.type = 15, 'down'
>>> os.type
'15mb'

Read/write properties inherited from Music21Object:

Ottava methods

Ottava.interval(reverse=False)

return an interval.Interval() object representing this ottava

>>> ottava = spanner.Ottava(type='15mb')
>>> i = ottava.interval()
>>> i
<music21.interval.Interval P-15>
Ottava.performTransposition()

On a transposing spanner, switch to non-transposing, and transpose all notes and chords in the spanner. The note/chords will all be transposed to their sounding pitch (at least as far as the ottava is concerned; transposing instruments are handled separately).

>>> ottava = spanner.Ottava(type='8va')
>>> n1 = note.Note('D#4')
>>> n2 = note.Note('E#4')
>>> ottava.addSpannedElements([n1, n2])
>>> ottava.transposing
True
>>> ottava.performTransposition()
>>> ottava.transposing
False
>>> n1.nameWithOctave
'D#5'
Ottava.shiftDirection(reverse=False)

Returns up or down depending on the type of shift:

Ottava.shiftMagnitude()

Get basic parameters of shift.

Returns either 8, 15, or 22 depending on the amount of shift

Ottava.undoTransposition()

Change a non-transposing spanner to a transposing spanner, and transpose back all the notes and chords in the spanner. The notes/chords will all be transposed to their written pitch (at least as far as the ottava is concerned; transposing instruments are handled separately).

>>> ottava = spanner.Ottava(type='8va')
>>> n1 = note.Note('D#4')
>>> n2 = note.Note('E#4')
>>> ottava.addSpannedElements([n1, n2])
>>> ottava.transposing = False
>>> ottava.undoTransposition()
>>> ottava.transposing
True
>>> n1.nameWithOctave
'D#3'

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

Ottava instance variables

Instance variables inherited from Music21Object:

RepeatBracket

class music21.spanner.RepeatBracket(*spannedElements, number: int | str | Iterable[int] = 0, overrideDisplay: str | None = None, **keywords)

A grouping of one or more measures, presumably in sequence, that mark an alternate repeat.

These gather what are sometimes called first-time bars and second-time bars.

It is assumed that numbering starts from 1. Numberings above 2 are permitted. The number keyword argument can be used to pass in the desired number.

overrideDisplay if set will display something other than the number. For instance ouvert and clos for medieval music. However, if you use it for something like ‘1-3’ be sure to set number properly too.

>>> m = stream.Measure()
>>> sp = spanner.RepeatBracket(m, number=1)
>>> sp  # can be one or more measures
<music21.spanner.RepeatBracket 1 <music21.stream.Measure 0 offset=0.0>>
>>> sp.number = 3
>>> sp
<music21.spanner.RepeatBracket 3 <music21.stream.Measure 0 offset=0.0>>
>>> sp.numberRange  # the list of repeat numbers
[3]
>>> sp.number
'3'

Range of repeats as string:

>>> sp.number = '1-3'
>>> sp.numberRange
[1, 2, 3]
>>> sp.number
'1-3'

Range of repeats as list:

>>> sp.number = [2, 3]
>>> sp.numberRange
[2, 3]
>>> sp.number
'2, 3'

Comma separated numbers:

>>> sp.number = '1, 2, 3'
>>> sp.numberRange
[1, 2, 3]
>>> sp.number
'1-3'

Disjunct numbers:

>>> sp.number = '1, 2, 3, 7'
>>> sp.numberRange
[1, 2, 3, 7]
>>> sp.number
'1, 2, 3, 7'

Override the display.

>>> sp.overrideDisplay = '1-3, 7'
>>> sp
<music21.spanner.RepeatBracket 1-3, 7
     <music21.stream.Measure 0 offset=0.0>>

number is not affected by display overrides:

>>> sp.number
'1, 2, 3, 7'

RepeatBracket bases

RepeatBracket read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

RepeatBracket read/write properties

RepeatBracket.number

Get or set the number – returning a string always.

>>> rb = spanner.RepeatBracket()
>>> rb.number
''
>>> rb.number = '5-7'
>>> rb.number
'5-7'
>>> rb.numberRange
[5, 6, 7]
>>> rb.number = 1

Read/write properties inherited from Music21Object:

RepeatBracket methods

RepeatBracket.getNumberList()

Deprecated – just look at .numberRange

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

RepeatBracket instance variables

RepeatBracket.numberRange

Get a contiguous list of repeat numbers that are applicable for this instance.

Will always have at least one element, but [0] means undefined

>>> rb = spanner.RepeatBracket()
>>> rb.numberRange
[0]
>>> rb.number = '1,2'
>>> rb.numberRange
[1, 2]
RepeatBracket.overrideDisplay

Override the string representation of this bracket, or use None to not override.

Instance variables inherited from Music21Object:

Slur

class music21.spanner.Slur(*spannedElements, **keywords)

A slur represented as a spanner between two Notes.

Slurs have .placement options (‘above’ or ‘below’) and .lineType (‘dashed’ or None)

Slur bases

Slur read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

Slur read/write properties

Read/write properties inherited from Music21Object:

Slur methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

Slur instance variables

Instance variables inherited from Music21Object:

SpannerAnchor

class music21.spanner.SpannerAnchor(**keywords)

A simple Music21Object that can be used to define the beginning or end of a Spanner, in the place of a GeneralNote.

This is useful for (e.g.) a Crescendo that ends partway through a note (e.g. in a violin part). Exporters (like MusicXML) are configured to remove the SpannerAnchor itself on output, exporting only the Spanner start and stop locations.

Here’s an example of a whole note that has a Crescendo for the first half of the note, and a Diminuendo for the second half of the note.

>>> n = note.Note('C4', quarterLength=4)
>>> measure = stream.Measure([n], number=1)
>>> part = stream.Part([measure], id='violin')
>>> score = stream.Score([part])

Add a crescendo from the note’s start to the first anchor, place in the middle of the note, and then a diminuendo from that first anchor to the second, placed at the end of the note.

>>> anchor1 = spanner.SpannerAnchor()
>>> anchor2 = spanner.SpannerAnchor()
>>> measure.insert(2.0, anchor1)
>>> measure.insert(4.0, anchor2)
>>> cresc = dynamics.Crescendo(n, anchor1)
>>> dim = dynamics.Diminuendo(anchor1, anchor2)
>>> score.append((cresc, dim))
>>> score.show('text')
{0.0} <music21.stream.Part violin>
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.note.Note C>
        {2.0} <music21.spanner.SpannerAnchor at 2.0>
        {4.0} <music21.spanner.SpannerAnchor at 4.0>
{4.0} <music21.dynamics.Crescendo <music21.note.Note C><...SpannerAnchor at 2.0>>
{4.0} <music21.dynamics.Diminuendo <...SpannerAnchor at 2.0><...SpannerAnchor at 4.0>>

SpannerAnchor bases

SpannerAnchor read-only properties

Read-only properties inherited from Music21Object:

Read-only properties inherited from ProtoM21Object:

SpannerAnchor read/write properties

Read/write properties inherited from Music21Object:

SpannerAnchor methods

Methods inherited from Music21Object:

Methods inherited from ProtoM21Object:

SpannerAnchor instance variables

Instance variables inherited from Music21Object:

SpannerBundle

class music21.spanner.SpannerBundle(spanners: list[music21.spanner.Spanner] | None = None)

An advanced utility object for collecting and processing collections of Spanner objects. This is necessary because often processing routines that happen at many levels still need access to the same collection of spanners.

Because SpannerBundles are so commonly used with Stream objects, the Stream has a spannerBundle property that stores and caches a SpannerBundle of the Stream.

If a Stream or Stream subclass is provided as an argument, all Spanners on this Stream will be accumulated herein.

Not to be confused with SpannerStorage (which is a Stream class inside a spanner that stores Elements that are spanned)

  • Changed in v7: only argument must be a List of spanners. Creators of SpannerBundles are required to check that this constraint is True

SpannerBundle bases

SpannerBundle read-only properties

Read-only properties inherited from ProtoM21Object:

SpannerBundle methods

SpannerBundle.__getitem__(key) Spanner
SpannerBundle.append(other: Spanner)

adds a Spanner to the bundle. Will be done automatically when adding a Spanner to a Stream.

SpannerBundle.freePendingSpannedElementAssignment(spannedElementCandidate)

Assigns and frees up a pendingSpannedElementAssignment if one is active and the candidate matches the class. See setPendingSpannedElementAssignment for documentation and tests.

It is set up via a first-in, first-out priority.

SpannerBundle.getByClass(searchClass: str | type | tuple[type, ...]) SpannerBundle

Given a spanner class, return a new SpannerBundle of all Spanners of the desired class.

>>> su1 = spanner.Slur()
>>> su2 = layout.StaffGroup()
>>> su3 = layout.StaffGroup()
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> sb.append(su3)

searchClass should be a Class.

>>> slurs = sb.getByClass(spanner.Slur)
>>> slurs
<music21.spanner.SpannerBundle of size 1>
>>> list(slurs) == [su1]
True
>>> list(sb.getByClass(spanner.Slur)) == [su1]
True
>>> list(sb.getByClass(layout.StaffGroup)) == [su2, su3]
True

A tuple of classes can also be given:

>>> len(sb.getByClass((spanner.Slur, layout.StaffGroup)))
3

Note that the ability to search via a string will be removed in version 10.

SpannerBundle.getByClassIdLocalComplete(className, idLocal, completeStatus)

Get all spanners of a specified class className, an id idLocal, and a completeStatus. This is a convenience routine for multiple filtering when searching for relevant Spanners to pair with.

>>> su1 = spanner.Slur()
>>> su2 = layout.StaffGroup()
>>> su2.idLocal = 3
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> list(sb.getByClassIdLocalComplete(layout.StaffGroup, 3, False)) == [su2]
True
>>> su2.completeStatus = True
>>> list(sb.getByClassIdLocalComplete(layout.StaffGroup, 3, False)) == []
True
SpannerBundle.getByCompleteStatus(completeStatus: bool) SpannerBundle

Get spanners by matching status of completeStatus to the same attribute

>>> su1 = spanner.Slur()
>>> su1.idLocal = 1
>>> su1.completeStatus = True
>>> su2 = spanner.Slur()
>>> su2.idLocal = 2
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> sb2 = sb.getByCompleteStatus(True)
>>> len(sb2)
1
>>> sb2 = sb.getByIdLocal(1).getByCompleteStatus(True)
>>> sb2[0] == su1
True
SpannerBundle.getByIdLocal(idLocal: int | None = None) SpannerBundle

Get spanners by idLocal.

Returns a new SpannerBundle object

>>> su = spanner.Slur()
>>> su.idLocal = 1
>>> rb = spanner.RepeatBracket()
>>> rb.idLocal = 2
>>> sb = spanner.SpannerBundle()
>>> sb.append(su)
>>> sb.append(rb)
>>> len(sb)
2
>>> sb.getByIdLocal(2)
<music21.spanner.SpannerBundle of size 1>
>>> sb.getByIdLocal(2)[0]
<music21.spanner.RepeatBracket >
>>> len(sb.getByIdLocal(1))
1
>>> sb.getByIdLocal(3)
<music21.spanner.SpannerBundle of size 0>
SpannerBundle.getBySpannedElement(spannedElement: Spanner) SpannerBundle

Given a spanner spannedElement (an object), return a new SpannerBundle of all Spanner objects that have this object as a spannedElement.

>>> n1 = note.Note()
>>> n2 = note.Note()
>>> n3 = note.Note()
>>> su1 = spanner.Slur(n1, n2)
>>> su2 = spanner.Slur(n2, n3)
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> list(sb.getBySpannedElement(n1)) == [su1]
True
>>> list(sb.getBySpannedElement(n2)) == [su1, su2]
True
>>> list(sb.getBySpannedElement(n3)) == [su2]
True
SpannerBundle.getSpannerStorageIds() list[int]

Return all SpannerStorage ids from all contained Spanners

SpannerBundle.remove(item: Spanner)

Remove a stored Spanner from the bundle with an instance. Each reference must have a matching id() value.

>>> su1 = spanner.Slur()
>>> su1.idLocal = 1
>>> su2 = spanner.Slur()
>>> su2.idLocal = 2
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> len(sb)
2
>>> sb
<music21.spanner.SpannerBundle of size 2>
>>> sb.remove(su2)
>>> len(sb)
1
SpannerBundle.replaceSpannedElement(old: Music21Object, new: Music21Object) list[music21.spanner.Spanner]

Given a spanner spannedElement (an object), replace all old spannedElements with new spannedElements for all Spanner objects contained in this bundle.

The old parameter must be an object, not an object id.

If no replacements are found, no errors are raised.

Returns a list of spanners that had elements replaced.

>>> n1 = note.Note('C')
>>> n2 = note.Note('D')
>>> su1 = spanner.Line(n1, n2)
>>> su2 = spanner.Glissando(n2, n1)
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> su1
<music21.spanner.Line <music21.note.Note C><music21.note.Note D>>
>>> su2
<music21.spanner.Glissando <music21.note.Note D><music21.note.Note C>>
>>> n3 = note.Note('E')
>>> replacedSpanners = sb.replaceSpannedElement(n2, n3)
>>> replacedSpanners == [su1, su2]
True
>>> su1
<music21.spanner.Line <music21.note.Note C><music21.note.Note E>>
>>> su2
<music21.spanner.Glissando <music21.note.Note E><music21.note.Note C>>
  • Changed in v7: id() is no longer allowed for old.

>>> sb.replaceSpannedElement(id(n1), n2)
Traceback (most recent call last):
TypeError: send elements to replaceSpannedElement(), not ids.
SpannerBundle.setIdLocalByClass(className, maxId=6)

(See setIdLocals() for an explanation of what an idLocal is.)

Automatically set idLocal values for all members of the provided class. This is necessary in cases where spanners are newly created in potentially overlapping boundaries and need to be tagged for MusicXML or other output. Note that, if some Spanners already have idLocals, they will be overwritten.

The maxId parameter sets the largest number that is available for this class. In MusicXML it is 6.

Currently, this method just iterates over the spanners of this class and counts the number from 1-6 and then recycles numbers. It does not check whether more than 6 overlapping spanners of the same type exist, nor does it reset the count to 1 after all spanners of that class have been closed. The example below demonstrates that the position of the contents of the spanner have no bearing on its idLocal (since we don’t even put anything into the spanners).

>>> su1 = spanner.Slur()
>>> su2 = layout.StaffGroup()
>>> su3 = spanner.Slur()
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> sb.append(su3)
>>> [sp.idLocal for sp in sb.getByClass(spanner.Slur)]
[None, None]
>>> sb.setIdLocalByClass('Slur')
>>> [sp.idLocal for sp in sb.getByClass(spanner.Slur)]
[1, 2]
SpannerBundle.setIdLocals()

Utility method for outputting MusicXML (and potentially other formats) for spanners.

Each Spanner type (slur, line, glissando, etc.) in MusicXML has a number assigned to it. We call this number, idLocal. idLocal is a number from 1 to 6. This does not mean that your piece can only have six slurs total! But it does mean that within a single part, only up to 6 slurs can happen simultaneously. But as soon as a slur stops, its idLocal can be reused.

This method sets all idLocals for all classes in this SpannerBundle. This will assure that each class has a unique idLocal number.

Calling this method is destructive: existing idLocal values will be lost.

>>> su1 = spanner.Slur()
>>> su2 = layout.StaffGroup()
>>> su3 = spanner.Slur()
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> sb.append(su3)
>>> [sp.idLocal for sp in sb.getByClass('Slur')]
[None, None]
>>> sb.setIdLocals()
>>> [(sp, sp.idLocal) for sp in sb]
[(<music21.spanner.Slur>, 1),
 (<music21.layout.StaffGroup>, 1),
 (<music21.spanner.Slur>, 2)]

DynamicWedge objects are commingled. That is, Crescendo and Diminuendo are not numbered separately:

>>> sb2 = spanner.SpannerBundle()
>>> c = dynamics.Crescendo()
>>> d = dynamics.Diminuendo()
>>> sb2.append(c)
>>> sb2.append(d)
>>> sb2.setIdLocals()
>>> [(sp, sp.idLocal) for sp in sb2]
[(<music21.dynamics.Crescendo>, 1),
 (<music21.dynamics.Diminuendo>, 2)]
SpannerBundle.setPendingSpannedElementAssignment(sp: Spanner, className: str)

A SpannerBundle can be set up so that a particular spanner (sp) is looking for an element of class (className) to complete it. Any future element that matches the className which is passed to the SpannerBundle via freePendingSpannedElementAssignment() will get it.

>>> n1 = note.Note('C')
>>> r1 = note.Rest()
>>> n2 = note.Note('D')
>>> n3 = note.Note('E')
>>> su1 = spanner.Slur([n1])
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> su1.getSpannedElements()
[<music21.note.Note C>]
>>> n1.getSpannerSites()
[<music21.spanner.Slur <music21.note.Note C>>]

Now set up su1 to get the next note assigned to it.

>>> sb.setPendingSpannedElementAssignment(su1, 'Note')

Call freePendingSpannedElementAssignment to attach.

Should not get a rest, because it is not a ‘Note’

>>> sb.freePendingSpannedElementAssignment(r1)
>>> su1.getSpannedElements()
[<music21.note.Note C>]

But will get the next note:

>>> sb.freePendingSpannedElementAssignment(n2)
>>> su1.getSpannedElements()
[<music21.note.Note C>, <music21.note.Note D>]
>>> n2.getSpannerSites()
[<music21.spanner.Slur <music21.note.Note C><music21.note.Note D>>]

And now that the assignment has been made, the pending assignment has been cleared, so n3 will not get assigned to the slur:

>>> sb.freePendingSpannedElementAssignment(n3)
>>> su1.getSpannedElements()
[<music21.note.Note C>, <music21.note.Note D>]
>>> n3.getSpannerSites()
[]

Methods inherited from ProtoM21Object: