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
, andDiminuendo
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.
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>
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()
(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: Spanner.fill() 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 aspannerBundle
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.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
andDiminuendo
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
: