Previous topic

music21.sites

Next topic

music21.stream

Table Of Contents

Table Of Contents

This Page

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) or in music21.meter (a ritardando, for instance).

Spanner

class music21.spanner.Spanner(*arguments, **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’)) 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>>
  1. 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 .spannedElements attribute. That Stream has an attribute called spannerParent which links to the original spanner. Thus, spannedElements 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 spannerParent:

>>> sp1.spannedElements.spannerParent == 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:

Spanner read/write properties

Read/write properties inherited from Music21Object:

Spanner methods

Spanner.addSpannedElements(spannedElements, *arguments, **keywords)

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('c')
>>> n5 = note.Note('d-')
>>> 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.getDurationBySite(site)

Return a Duration object representing the value between the first spanned element’s offset and the last spanned-element’s offset plus duration.

Spanner.getDurationSpanBySite(site)

Return the duration span, or the distance between the first spanned element’s offset and the last spanned element’s offset plus its duration in quarterLength.

returns a two-element tuple of the offset of the first element and the end-time of the last element.

Offsets are relative to the site given; this is because it’s very likely that different elements in the Spanner are located in different Streams in the hierarchy.

Spanner.getFirst()

Get the object of the first spannedElement

>>> 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.getLast()

Get the object of the first spannedElement

>>> 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.getOffsetSpanBySite(site)

Return the span, or min and max values, of all offsets for a given site.

Spanner.getOffsetsBySite(site)

Given a site shared by all , return a list of offset values.

>>> n1 = note.Note('g')
>>> n2 = note.Note('f#')
>>> s = stream.Stream()
>>> s.insert(3, n1)
>>> s.insert(11, n2)
>>> sp = spanner.Spanner(n1, n2)
>>> sp.getOffsetsBySite(s)
[3.0, 11.0]
Spanner.getSpannedElementIds()

Return all id() for all stored objects.

Spanner.getSpannedElements()

Return all the elements of .spannedElements 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') == [c1]
True
Spanner.getSpannerStorageId()

Return the object id of the SpannerStorage object

Spanner.hasSpannedElement(spannedElement)

Return True if this Spanner has the spannedElement.

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)
Spanner.purgeOrphans()
Spanner.replaceSpannedElement(old, new)

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:

Spanner instance variables

Instance variables inherited from Music21Object:

Glissando

class music21.spanner.Glissando(*arguments, **keywords)

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

Glissando bases

Glissando read-only properties

Read-only properties inherited from Music21Object:

Glissando read/write properties

Glissando.label

Get or set the label property.

Glissando.lineType

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

Read/write properties inherited from Music21Object:

Glissando methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Glissando instance variables

Instance variables inherited from Spanner:

  • isStream
  • hideObjectOnPrint
  • classSortOrder
  • groups
  • isVariant
  • id
  • isSpanner
  • xPosition

Instance variables inherited from Music21Object:

Line

class music21.spanner.Line(*arguments, **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:

Line read/write properties

Line.endHeight

Get or set the endHeight property.

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 = 'junk'
Traceback (most recent call last):
SpannerException: not a valid value: junk
>>> b.validLineTypes
('solid', 'dashed', 'dotted', 'wavy')
Line.startHeight

Get or set the startHeight property.

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

Line.getEndParameters()

Return the parameters for the start of this spanner required by MusicXML output.

Line.getStartParameters()

Return the parameters for the start of this spanners required by MusicXML output.

Methods inherited from Spanner:

Methods inherited from Music21Object:

Line instance variables

Instance variables inherited from Spanner:

  • isStream
  • hideObjectOnPrint
  • classSortOrder
  • groups
  • isVariant
  • id
  • isSpanner
  • xPosition

Instance variables inherited from Music21Object:

Ottava

class music21.spanner.Ottava(*arguments, **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 >

All valid types

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

Ottava bases

Ottava read-only properties

Read-only properties inherited from Music21Object:

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 = 15, 'down'
>>> os.type
'15mb'
>>> os.type = '8vb'
>>> os.type
'8vb'

Read/write properties inherited from Music21Object:

Ottava methods

Ottava.getEndParameters()

Return the parameters for the start of this spanner required by MusicXML output.

>>> ottava = spanner.Ottava(type=8)
>>> st = ottava.getStartParameters()
>>> st['type']
'down'
>>> st['size']
8
>>> en = ottava.getEndParameters()
>>> en['type']
'stop'
>>> en['size']
8
Ottava.getStartParameters()

Return the parameters for the start of this spanners required by MusicXML output.

>>> ottava = spanner.Ottava(type='15mb')
>>> st = ottava.getStartParameters()
>>> st['type']
'up'
>>> st['size']
15
>>> en = ottava.getEndParameters()
>>> en['type']
'stop'
>>> en['size']
15

Methods inherited from Spanner:

Methods inherited from Music21Object:

Ottava instance variables

Instance variables inherited from Spanner:

  • isStream
  • hideObjectOnPrint
  • classSortOrder
  • groups
  • isVariant
  • id
  • isSpanner
  • xPosition

Instance variables inherited from Music21Object:

RepeatBracket

class music21.spanner.RepeatBracket(*arguments, **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.

>>> 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 # can be one or more measures
<music21.spanner.RepeatBracket 3 <music21.stream.Measure 0 offset=0.0>>
>>> sp.getNumberList() # the list of repeat numbers
[3]
>>> sp.number
'3'
>>> sp.number = '1-3' # range of repeats
>>> sp.getNumberList()
[1, 2, 3]
>>> sp.number
'1-3'
>>> sp.number = [2,3] # range of repeats
>>> sp.getNumberList()
[2, 3]
>>> sp.number
'2, 3'
>>> sp.number = '1, 2, 3' # comma separated
>>> sp.getNumberList()
[1, 2, 3]
>>> sp.number
'1-3'
>>> sp.number = '1, 2, 3, 7' # disjunct
>>> sp.getNumberList()
[1, 2, 3, 7]
>>> sp.number
'1, 2, 3, 7'

RepeatBracket bases

RepeatBracket read-only properties

Read-only properties inherited from Music21Object:

RepeatBracket read/write properties

RepeatBracket.number

Read/write properties inherited from Music21Object:

RepeatBracket methods

RepeatBracket.getNumberList()

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

>>> rb = spanner.RepeatBracket()
>>> rb.number = '1,2'
>>> rb.getNumberList()
[1, 2]

Methods inherited from Spanner:

Methods inherited from Music21Object:

RepeatBracket instance variables

Instance variables inherited from Spanner:

  • isStream
  • hideObjectOnPrint
  • classSortOrder
  • groups
  • isVariant
  • id
  • isSpanner
  • xPosition

Instance variables inherited from Music21Object:

Slur

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

A slur represented as a spanner between two Notes.

The idLocal attribute, defined in the Spanner base class, is used to mark start and end tags of potentially overlapping indicators.

Slur bases

Slur read-only properties

Read-only properties inherited from Music21Object:

Slur read/write properties

Read/write properties inherited from Music21Object:

Slur methods

Methods inherited from Spanner:

Methods inherited from Music21Object:

Slur instance variables

Instance variables inherited from Spanner:

  • isStream
  • hideObjectOnPrint
  • classSortOrder
  • groups
  • isVariant
  • id
  • isSpanner
  • xPosition

Instance variables inherited from Music21Object:

SpannerBundle

class music21.spanner.SpannerBundle(*arguments, **keywords)

A utility object for collecting and processing collections of Spanner objects. This is necessary because often processing routines that happen at many different levels 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.

SpannerBundle read-only properties

SpannerBundle.list

Return the bundle as a list.

>>> su1 = spanner.Slur()
>>> su1.idLocal = 1
>>> su2 = spanner.Glissando()
>>> su2.idLocal = 2
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> sb.list
[<music21.spanner.Slur >, <music21.spanner.Glissando >]

SpannerBundle methods

SpannerBundle.append(other)

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

SpannerBundle.freePendingSpannedElementAssignment(spannedElementCandidate)
SpannerBundle.getByClass(className)

Given a spanner class, return a bundle 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)

Classes can be strings (short class) or classes.

>>> sb.getByClass(spanner.Slur).list == [su1]
True
>>> sb.getByClass('Slur').list == [su1]
True
>>> sb.getByClass('StaffGroup').list == [su2, su3]
True
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)
>>> sb.getByClassIdLocalComplete('StaffGroup', 3, False).list == [su2]
True
>>> su2.completeStatus = True
>>> sb.getByClassIdLocalComplete('StaffGroup', 3, False).list == []
True
SpannerBundle.getByCompleteStatus(completeStatus)

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=None)

Get spanners by idLocal or complete status.

Returns a new SpannerBundle object

>>> su1 = spanner.Slur()
>>> su1.idLocal = 1
>>> su2 = spanner.Slur()
>>> su2.idLocal = 2
>>> sb = spanner.SpannerBundle()
>>> sb.append(su1)
>>> sb.append(su2)
>>> len(sb)
2
>>> len(sb.getByIdLocal(1))
1
>>> len(sb.getByIdLocal(2))
1
SpannerBundle.getBySpannedElement(spannedElement)

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)
>>> sb.getBySpannedElement(n1).list == [su1]
True
>>> sb.getBySpannedElement(n2).list == [su1, su2]
True
>>> sb.getBySpannedElement(n3).list == [su2]
True
SpannerBundle.getSpannerStorageIds()

Return all SpannerStorage ids from all contained Spanners

SpannerBundle.remove(item)

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.remove(su2)
>>> len(sb)
1
SpannerBundle.replaceSpannedElement(old, new)

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

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

If no replacements are found, no errors are raised.

>>> 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')
>>> sb.replaceSpannedElement(n2, n3)
>>> su1
<music21.spanner.Line <music21.note.Note C><music21.note.Note E>>
>>> su2
<music21.spanner.Glissando <music21.note.Note E><music21.note.Note C>>
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('Slur')]
[None, None]
>>> sb.setIdLocalByClass('Slur')
>>> [sp.idLocal for sp in sb.getByClass('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 set 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)]
SpannerBundle.setPendingSpannedElementAssignment(sp, className)