music21.stream.iterator

this class contains iterators and filters for walking through streams

StreamIterators are explicitly allowed to access private methods on streams.

StreamIterator

class music21.stream.iterator.StreamIterator(srcStream, *, filterList=None, restoreActiveSites=True, activeInformation=None, ignoreSorting=False)

An Iterator object used to handle getting items from Streams. The __iter__() method returns this object, passing a reference to self.

Note that this iterator automatically sets the active site of returned elements to the source Stream.

There is one property to know about: .overrideDerivation which overrides the set derivation of the class when .stream() is called

Sets:

  • StreamIterator.srcStream – the Stream iterated over

  • StreamIterator.index – current index item

  • StreamIterator.streamLength – length of elements.

  • StreamIterator.srcStreamElements – srcStream._elements

  • StreamIterator.cleanupOnStop – should the StreamIterator delete the reference to srcStream and srcStreamElements when stopping? default False

  • StreamIterator.activeInformation – a dict that contains information about where we are in the parse. Especially useful for recursive streams. ‘stream’ = the stream that is currently active, ‘index’ where in .elements we are, iterSection is _elements or _endElements, and sectionIndex is where we are in the iterSection, or -1 if we have not started. This dict is shared among all sub iterators.

Constructor keyword only arguments:

  • filterList is a list of stream.filters.Filter objects to apply

  • if restoreActiveSites is True (default) then on iterating, the activeSite is set to the Stream being iterated over.

  • if ignoreSorting is True (default is False) then the Stream is not sorted before iterating. If the Stream is already sorted, then this value does not matter, and no time will be saved by setting to False.

  • For activeInformation see above.

Changed in v.5.2 – all arguments except srcStream are keyword only.

StreamIterator read-only properties

StreamIterator.activeElementList

returns the element list (‘_elements’ or ‘_endElements’) for the current activeInformation

StreamIterator.classSet

this is not cached – it should be if we end up using it a lot…

StreamIterator.classes
StreamIterator.notes

Returns all NotRest objects

(will sometime become simply Note and Chord objects…)

>>> s = stream.Stream()
>>> s.append(note.Note('C'))
>>> s.append(note.Rest())
>>> s.append(note.Note('D'))
>>> for el in s.iter.notes:
...     print(el)
<music21.note.Note C>
<music21.note.Note D>
StreamIterator.notesAndRests

Returns all GeneralNote objects

>>> s = stream.Stream()
>>> s.append(meter.TimeSignature('4/4'))
>>> s.append(note.Note('C'))
>>> s.append(note.Rest())
>>> s.append(note.Note('D'))
>>> for el in s.iter.notesAndRests:
...     print(el)
<music21.note.Note C>
<music21.note.Rest rest>
<music21.note.Note D>

chained filters… (this makes no sense since notes is a subset of notesAndRests)

>>> for el in s.iter.notesAndRests.notes:
...     print(el)
<music21.note.Note C>
<music21.note.Note D>
StreamIterator.parts

Adds a ClassFilter for Part objects

StreamIterator.spanners

Adds a ClassFilter for Spanner objects

StreamIterator.variants

To be deprecated soon…

Adds a ClassFilter for Variant

StreamIterator.voices

Adds a ClassFilter for Voice objects

StreamIterator methods

StreamIterator.addFilter(newFilter)

adds a filter to the list.

resets caches – do not add filters any other way

StreamIterator.cleanup()

stop iteration; and cleanup if need be.

StreamIterator.getElementById(elementId)

Returns a single element (or None) that matches elementId.

If chaining filters, this should be the last one, as it returns an element

>>> s = stream.Stream(id="s1")
>>> s.append(note.Note('C'))
>>> r = note.Rest()
>>> r.id = 'restId'
>>> s.append(r)
>>> r2 = s.recurse().getElementById('restId')
>>> r2 is r
True
>>> r2.id
'restId'
StreamIterator.getElementsByClass(classFilterList)

Add a filter to the Iterator to remove all elements except those that match one or more classes in the classFilterList. A single class can also used for the classFilterList parameter instead of a List.

>>> s = stream.Stream(id="s1")
>>> s.append(note.Note('C'))
>>> r = note.Rest()
>>> s.append(r)
>>> s.append(note.Note('D'))
>>> for el in s.iter.getElementsByClass('Rest'):
...     print(el)
<music21.note.Rest rest>

ActiveSite is restored…

>>> s2 = stream.Stream(id="s2")
>>> s2.insert(0, r)
>>> r.activeSite.id
's2'
>>> for el in s.iter.getElementsByClass('Rest'):
...     print(el.activeSite.id)
s1

Classes work in addition to strings…

>>> for el in s.iter.getElementsByClass(note.Rest):
...     print(el)
<music21.note.Rest rest>
StreamIterator.getElementsByGroup(groupFilterList)
>>> n1 = note.Note("C")
>>> n1.groups.append('trombone')
>>> n2 = note.Note("D")
>>> n2.groups.append('trombone')
>>> n2.groups.append('tuba')
>>> n3 = note.Note("E")
>>> n3.groups.append('tuba')
>>> s1 = stream.Stream()
>>> s1.append(n1)
>>> s1.append(n2)
>>> s1.append(n3)
>>> tboneSubStream = s1.iter.getElementsByGroup("trombone")
>>> for thisNote in tboneSubStream:
...     print(thisNote.name)
C
D
>>> tubaSubStream = s1.iter.getElementsByGroup("tuba")
>>> for thisNote in tubaSubStream:
...     print(thisNote.name)
D
E
StreamIterator.getElementsByOffset(offsetStart, offsetEnd=None, *, includeEndBoundary=True, mustFinishInSpan=False, mustBeginInSpan=True, includeElementsThatEndAtStart=True)

Adds a filter keeping only Music21Objects that are found at a certain offset or within a certain offset time range (given the start and optional stop values).

There are several attributes that govern how this range is determined:

If mustFinishInSpan is True then an event that begins between offsetStart and offsetEnd but which ends after offsetEnd will not be included. The default is False.

For instance, a half note at offset 2.0 will be found in getElementsByOffset(1.5, 2.5) or getElementsByOffset(1.5, 2.5, mustFinishInSpan = False) but not by getElementsByOffset(1.5, 2.5, mustFinishInSpan = True).

The includeEndBoundary option determines if an element begun just at the offsetEnd should be included. For instance, the half note at offset 2.0 above would be found by getElementsByOffset(0, 2.0) or by getElementsByOffset(0, 2.0, includeEndBoundary = True) but not by getElementsByOffset(0, 2.0, includeEndBoundary = False).

Setting includeEndBoundary to False at the same time as mustFinishInSpan is set to True is probably NOT what you want to do unless you want to find things like clefs at the end of the region to display as courtesy clefs.

The mustBeginInSpan option determines whether notes or other objects that do not begin in the region but are still sounding at the beginning of the region are excluded. The default is True – that is, these notes will not be included. For instance the half note at offset 2.0 from above would not be found by getElementsByOffset(3.0, 3.5) or getElementsByOffset(3.0, 3.5, mustBeginInSpan = True) but it would be found by getElementsByOffset(3.0, 3.5, mustBeginInSpan = False)

Setting includeElementsThatEndAtStart to False is useful for zeroLength searches that set mustBeginInSpan == False to not catch notes that were playing before the search but that end just before the end of the search type. See the code for allPlayingWhileSounding for a demonstration.

This chart, and the examples below, demonstrate the various features of getElementsByOffset. It is one of the most complex methods of music21 but also one of the most powerful, so it is worth learning at least the basics.

../_images/getElementsByOffset.png
>>> st1 = stream.Stream()
>>> n0 = note.Note("C")
>>> n0.duration.type = "half"
>>> n0.offset = 0
>>> st1.insert(n0)
>>> n2 = note.Note("D")
>>> n2.duration.type = "half"
>>> n2.offset = 2
>>> st1.insert(n2)
>>> out1 = list(st1.iter.getElementsByOffset(2))
>>> len(out1)
1
>>> out1[0].step
'D'
>>> out2 = list(st1.iter.getElementsByOffset(1, 3))
>>> len(out2)
1
>>> out2[0].step
'D'
>>> out3 = list(st1.iter.getElementsByOffset(1, 3, mustFinishInSpan=True))
>>> len(out3)
0
>>> out4 = list(st1.iter.getElementsByOffset(1, 2))
>>> len(out4)
1
>>> out4[0].step
'D'
>>> out5 = list(st1.iter.getElementsByOffset(1, 2, includeEndBoundary=False))
>>> len(out5)
0
>>> out6 = list(st1.iter.getElementsByOffset(1, 2, includeEndBoundary=False,
...                                          mustBeginInSpan=False))
>>> len(out6)
1
>>> out6[0].step
'C'
>>> out7 = list(st1.iter.getElementsByOffset(1, 3, mustBeginInSpan=False))
>>> len(out7)
2
>>> [el.step for el in out7]
['C', 'D']

Note, that elements that end at the start offset are included if mustBeginInSpan is False

>>> out8 = list(st1.iter.getElementsByOffset(2, 4, mustBeginInSpan=False))
>>> len(out8)
2
>>> [el.step for el in out8]
['C', 'D']

To change this behavior set includeElementsThatEndAtStart=False

>>> out9 = list(st1.iter.getElementsByOffset(2, 4, mustBeginInSpan=False,
...                                          includeElementsThatEndAtStart=False))
>>> len(out9)
1
>>> [el.step for el in out9]
['D']
>>> a = stream.Stream(id='a')
>>> n = note.Note('G')
>>> n.quarterLength = .5
>>> a.repeatInsert(n, list(range(8)))
>>> b = stream.Stream(id='b')
>>> b.repeatInsert(a, [0, 3, 6])
>>> c = list(b.iter.getElementsByOffset(2, 6.9))
>>> len(c)
2
>>> c = list(b.flat.iter.getElementsByOffset(2, 6.9))
>>> len(c)
10

Testing multiple zero-length elements with mustBeginInSpan:

>>> c = clef.TrebleClef()
>>> ts = meter.TimeSignature('4/4')
>>> ks = key.KeySignature(2)
>>> s = stream.Stream()
>>> s.insert(0.0, c)
>>> s.insert(0.0, ts)
>>> s.insert(0.0, ks)
>>> len(list(s.iter.getElementsByOffset(0.0, mustBeginInSpan=True)))
3
>>> len(list(s.iter.getElementsByOffset(0.0, mustBeginInSpan=False)))
3

Changed in v5.5 – all arguments changing behavior are keyword only.

StreamIterator.getElementsNotOfClass(classFilterList)

Adds a filter, removing all Elements that do not match the one or more classes in the classFilterList.

In lieu of a list, a single class can be used as the classFilterList parameter.

>>> a = stream.Stream()
>>> a.repeatInsert(note.Rest(), range(10))
>>> for x in range(4):
...     n = note.Note('G#')
...     n.offset = x * 3
...     a.insert(n)
>>> found = a.iter.getElementsNotOfClass(note.Note)
>>> len(found)
10
>>> found = a.iter.getElementsNotOfClass('Rest')
>>> len(found)
4
>>> found = a.iter.getElementsNotOfClass(['Note', 'Rest'])
>>> len(found)
0
>>> b = stream.Stream()
>>> b.repeatInsert(note.Rest(), range(15))
>>> a.insert(b)
>>> found = a.recurse().getElementsNotOfClass([note.Rest, 'Stream'])
>>> len(found)
4
>>> found = a.recurse().getElementsNotOfClass([note.Note, 'Stream'])
>>> len(found)
25
StreamIterator.matchesFilters(e)

returns False if any filter returns False, True otherwise.

StreamIterator.matchingElements()

returns a list of elements that match the filter.

This sort of defeats the point of using a generator, so only used if it’s requested by __len__ or __getitem__ etc.

Subclasses should override to cache anything they need saved (index, recursion objects, etc.)

activeSite will not be set.

Cached for speed.

>>> s = converter.parse('tinynotation: 3/4 c4 d e f g a', makeNotation=False)
>>> s.id = 'tn3/4'
>>> sI = s.iter
>>> sI
<music21.stream.iterator.StreamIterator for Part:tn3/4 @:0>
>>> sI.matchingElements()
[<music21.meter.TimeSignature 3/4>, <music21.note.Note C>, <music21.note.Note D>,
 <music21.note.Note E>, <music21.note.Note F>, <music21.note.Note G>,
 <music21.note.Note A>]
>>> sI.notes
<music21.stream.iterator.StreamIterator for Part:tn3/4 @:0>
>>> sI.notes is sI
True
>>> sI.filters
[<music21.stream.filters.ClassFilter NotRest>]
>>> sI.matchingElements()
[<music21.note.Note C>, <music21.note.Note D>,
 <music21.note.Note E>, <music21.note.Note F>, <music21.note.Note G>,
 <music21.note.Note A>]
StreamIterator.removeFilter(oldFilter)
StreamIterator.reset()

reset prior to iteration

StreamIterator.resetCaches()

reset any cached data. – do not use this at the start of iteration since we might as well save this information. But do call it if the filter changes.

StreamIterator.stream(returnStreamSubClass=True)

return a new stream from this iterator.

Does nothing except copy if there are no filters, but a drop in replacement for the old .getElementsByClass() etc. if it does.

In other words:

s.getElementsByClass() == s.iter.getElementsByClass().stream()

>>> s = stream.Part()
>>> s.insert(0, note.Note('C'))
>>> s.append(note.Rest())
>>> s.append(note.Note('D'))
>>> b = bar.Barline()
>>> s.storeAtEnd(b)
>>> s2 = s.iter.getElementsByClass('Note').stream()
>>> s2.show('t')
{0.0} <music21.note.Note C>
{2.0} <music21.note.Note D>
>>> s2.derivation.method
'getElementsByClass'
>>> s2
<music21.stream.Part ...>
>>> s3 = s.iter.stream()
>>> s3.show('t')
{0.0} <music21.note.Note C>
{1.0} <music21.note.Rest rest>
{2.0} <music21.note.Note D>
{3.0} <music21.bar.Barline type=regular>
>>> s3.elementOffset(b, stringReturns=True)
'highestTime'
>>> s4 = s.iter.getElementsByClass('Barline').stream()
>>> s4.show('t')
{0.0} <music21.bar.Barline type=regular>

Note that this routine can create Streams that have elements that the original stream did not, in the case of recursion:

>>> bach = corpus.parse('bwv66.6')
>>> bn = bach.flat[30]
>>> bn
<music21.note.Note E>
>>> bn in bach
False
>>> bfn = bach.recurse().notes.stream()
>>> bn in bfn
True
>>> bn.getOffsetBySite(bfn)
2.0
>>> bn.getOffsetInHierarchy(bach)
2.0
StreamIterator.updateActiveInformation()

Updates the (shared) activeInformation dictionary with information about where we are.

Call before any element return

RecursiveIterator

class music21.stream.iterator.RecursiveIterator(srcStream, *, filterList=None, restoreActiveSites=True, activeInformation=None, streamsOnly=False, includeSelf=False, ignoreSorting=False)

One of the most powerful iterators in music21. Generally not called directly, but created by being invoked on a stream with Stream.recurse()

>>> b = corpus.parse('bwv66.6')
>>> ri = stream.iterator.RecursiveIterator(b, streamsOnly=True)
>>> for x in ri:
...     print(x)
<music21.stream.Part Soprano>
<music21.stream.Measure 0 offset=0.0>
<music21.stream.Measure 1 offset=1.0>
<music21.stream.Measure 2 offset=5.0>
...
<music21.stream.Part Alto>
<music21.stream.Measure 0 offset=0.0>
...
<music21.stream.Part Tenor>
...
<music21.stream.Part Bass>
...

But this is how you’ll actually use it:

>>> for x in b.recurse(streamsOnly=True, includeSelf=True):
...     print(x)
<music21.stream.Score 0x10484fd68>
<music21.stream.Part Soprano>
<music21.stream.Measure 0 offset=0.0>
<music21.stream.Measure 1 offset=1.0>
<music21.stream.Measure 2 offset=5.0>
...
<music21.stream.Part Alto>
<music21.stream.Measure 0 offset=0.0>
...
<music21.stream.Part Tenor>
...
<music21.stream.Part Bass>
...
>>> hasExpressions = lambda el, i: True if (hasattr(el, 'expressions')
...       and el.expressions) else False
>>> expressive = b.recurse().addFilter(hasExpressions)
>>> expressive
<music21.stream.iterator.RecursiveIterator for Score:0x10487f550 @:0>
>>> for el in expressive:
...     print(el, el.expressions)
<music21.note.Note C#> [<music21.expressions.Fermata>]
<music21.note.Note A> [<music21.expressions.Fermata>]
<music21.note.Note F#> [<music21.expressions.Fermata>]
<music21.note.Note C#> [<music21.expressions.Fermata>]
<music21.note.Note G#> [<music21.expressions.Fermata>]
<music21.note.Note F#> [<music21.expressions.Fermata>]
>>> len(expressive)
6
>>> expressive[-1].measureNumber
9
>>> bool(expressive)
True

RecursiveIterator bases

RecursiveIterator read-only properties

Read-only properties inherited from StreamIterator:

RecursiveIterator methods

RecursiveIterator.currentHierarchyOffset()

Called on the current iterator, returns the current offset in the hierarchy. Or None if we are not currently iterating.

>>> b = corpus.parse('bwv66.6')
>>> bRecurse = b.recurse().notes
>>> print(bRecurse.currentHierarchyOffset())
None
>>> for n in bRecurse:
...     print(n.measureNumber, bRecurse.currentHierarchyOffset(), n)
0 0.0 <music21.note.Note C#>
0 0.5 <music21.note.Note B>
1 1.0 <music21.note.Note A>
1 2.0 <music21.note.Note B>
1 3.0 <music21.note.Note C#>
1 4.0 <music21.note.Note E>
2 5.0 <music21.note.Note C#>
...
9 34.5 <music21.note.Note E#>
9 35.0 <music21.note.Note F#>
0 0.0 <music21.note.Note E>
1 1.0 <music21.note.Note F#>
...

After iteration completes, the figure is reset to None:

>>> print(bRecurse.currentHierarchyOffset())
None

The offsets are with respect to the position inside the stream being iterated, so for instance, this will not change the output from above:

>>> o = stream.Opus()
>>> o.insert(20.0, b)
>>> bRecurse = b.recurse().notes
>>> for n in bRecurse:
...     print(n.measureNumber, bRecurse.currentHierarchyOffset(), n)
0 0.0 <music21.note.Note C#>
...

But of course, this will add 20.0 to all numbers:

>>> oRecurse = o.recurse().notes
>>> for n in oRecurse:
...     print(n.measureNumber, oRecurse.currentHierarchyOffset(), n)
0 20.0 <music21.note.Note C#>
...

New in v.4

RecursiveIterator.getElementsByOffsetInHierarchy(offsetStart, offsetEnd=None, *, includeEndBoundary=True, mustFinishInSpan=False, mustBeginInSpan=True, includeElementsThatEndAtStart=True)

Adds a filter keeping only Music21Objects that are found at a certain offset or within a certain offset time range (given the start and optional stop values) from the beginning of the hierarchy.

>>> b = corpus.parse('bwv66.6')
>>> for n in b.recurse().getElementsByOffsetInHierarchy(8, 9.5).notes:
...     print(n, n.getOffsetInHierarchy(b),
...           n.measureNumber, n.getContextByClass('Part').id)
<music21.note.Note C#> 8.0 2 Soprano
<music21.note.Note A> 9.0 3 Soprano
<music21.note.Note B> 9.5 3 Soprano
<music21.note.Note G#> 8.0 2 Alto
<music21.note.Note F#> 9.0 3 Alto
<music21.note.Note G#> 9.5 3 Alto
<music21.note.Note C#> 8.0 2 Tenor
<music21.note.Note C#> 9.0 3 Tenor
<music21.note.Note D> 9.5 3 Tenor
<music21.note.Note E#> 8.0 2 Bass
<music21.note.Note F#> 9.0 3 Bass
<music21.note.Note B> 9.5 3 Bass

Changed in v5.5 – all behavior changing options are keyword only.

Return type

StreamIterator

RecursiveIterator.iteratorStack()

Returns a stack of RecursiveIterators at this point in the iteration. Last is most recent.

>>> b = corpus.parse('bwv66.6')
>>> bRecurse = b.recurse()
>>> i = 0
>>> for x in bRecurse:
...     i += 1
...     if i > 12:
...         break
>>> bRecurse.iteratorStack()
[<music21.stream.iterator.RecursiveIterator for Score:0x10475cdd8 @:2>,
 <music21.stream.iterator.RecursiveIterator for Part:Soprano @:3>,
 <music21.stream.iterator.RecursiveIterator for Measure:m.1 @:3>]
RecursiveIterator.matchingElements()

returns a list of elements that match the filter.

This sort of defeats the point of using a generator, so only used if it’s requested by __len__ or __getitem__ etc.

Subclasses should override to cache anything they need saved (index, recursion objects, etc.)

activeSite will not be set.

Cached for speed.

>>> s = converter.parse('tinynotation: 3/4 c4 d e f g a', makeNotation=False)
>>> s.id = 'tn3/4'
>>> sI = s.iter
>>> sI
<music21.stream.iterator.StreamIterator for Part:tn3/4 @:0>
>>> sI.matchingElements()
[<music21.meter.TimeSignature 3/4>, <music21.note.Note C>, <music21.note.Note D>,
 <music21.note.Note E>, <music21.note.Note F>, <music21.note.Note G>,
 <music21.note.Note A>]
>>> sI.notes
<music21.stream.iterator.StreamIterator for Part:tn3/4 @:0>
>>> sI.notes is sI
True
>>> sI.filters
[<music21.stream.filters.ClassFilter NotRest>]
>>> sI.matchingElements()
[<music21.note.Note C>, <music21.note.Note D>,
 <music21.note.Note E>, <music21.note.Note F>, <music21.note.Note G>,
 <music21.note.Note A>]
RecursiveIterator.reset()

reset prior to iteration

RecursiveIterator.streamStack()

Returns a stack of Streams at this point. Last is most recent.

However, the current element may be the same as the last element in the stack

>>> b = corpus.parse('bwv66.6')
>>> bRecurse = b.recurse()
>>> i = 0
>>> for x in bRecurse:
...     i += 1
...     if i > 12:
...         break
>>> bRecurse.streamStack()
[<music21.stream.Score 0x1049a0710>,
 <music21.stream.Part Soprano>,
 <music21.stream.Measure 1 offset=1.0>]

Methods inherited from StreamIterator:

OffsetIterator

class music21.stream.iterator.OffsetIterator(srcStream, *, filterList=None, restoreActiveSites=True, activeInformation=None, ignoreSorting=False)

An iterator that with each iteration returns a list of elements that are at the same offset (or all at end)

>>> s = stream.Stream()
>>> s.insert(0, note.Note('C'))
>>> s.insert(0, note.Note('D'))
>>> s.insert(1, note.Note('E'))
>>> s.insert(2, note.Note('F'))
>>> s.insert(2, note.Note('G'))
>>> s.storeAtEnd(bar.Repeat('end'))
>>> s.storeAtEnd(clef.TrebleClef())
>>> oIter = stream.iterator.OffsetIterator(s)
>>> for groupedElements in oIter:
...     print(groupedElements)
[<music21.note.Note C>, <music21.note.Note D>]
[<music21.note.Note E>]
[<music21.note.Note F>, <music21.note.Note G>]
[<music21.bar.Repeat direction=end>, <music21.clef.TrebleClef>]

Does it work again?

>>> for groupedElements2 in oIter:
...     print(groupedElements2)
[<music21.note.Note C>, <music21.note.Note D>]
[<music21.note.Note E>]
[<music21.note.Note F>, <music21.note.Note G>]
[<music21.bar.Repeat direction=end>, <music21.clef.TrebleClef>]
>>> for groupedElements in oIter.notes:
...     print(groupedElements)
[<music21.note.Note C>, <music21.note.Note D>]
[<music21.note.Note E>]
[<music21.note.Note F>, <music21.note.Note G>]
>>> for groupedElements in stream.iterator.OffsetIterator(s).getElementsByClass('Clef'):
...     print(groupedElements)
[<music21.clef.TrebleClef>]

OffsetIterator bases

OffsetIterator read-only properties

Read-only properties inherited from StreamIterator:

OffsetIterator methods

OffsetIterator.reset()

runs before iteration

Methods inherited from StreamIterator: