Previous topic

music21.stream.timespanAnalysis

Next topic

music21.tempo

Table Of Contents

Table Of Contents

This Page

music21.stream.timespans

Tools for grouping notes and chords into a searchable tree organized by start and stop offsets.

This is a lower-level tool that for now at least normal music21 users won’t need to worry about.

Functions

music21.stream.timespans.makeElement(verticality, quarterLength)

Makes an element from a verticality and quarterLength.

TODO: DOCS!

music21.stream.timespans.makeExampleScore()

Makes example score for use in stream-to-timespan conversion docs.

>>> score = stream.timespans.makeExampleScore()
>>> score.show('text')
{0.0} <music21.stream.Part ...>
    {0.0} <music21.instrument.Instrument PartA: : >
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.clef.BassClef>
        {0.0} <music21.meter.TimeSignature 2/4>
        {0.0} <music21.note.Note C>
        {1.0} <music21.note.Note D>
    {2.0} <music21.stream.Measure 2 offset=2.0>
        {0.0} <music21.note.Note E>
        {1.0} <music21.note.Note F>
    {4.0} <music21.stream.Measure 3 offset=4.0>
        {0.0} <music21.note.Note G>
        {1.0} <music21.note.Note A>
    {6.0} <music21.stream.Measure 4 offset=6.0>
        {0.0} <music21.note.Note B>
        {1.0} <music21.note.Note C>
        {2.0} <music21.bar.Barline style=final>
{0.0} <music21.stream.Part ...>
    {0.0} <music21.instrument.Instrument PartB: : >
    {0.0} <music21.stream.Measure 1 offset=0.0>
        {0.0} <music21.clef.BassClef>
        {0.0} <music21.meter.TimeSignature 2/4>
        {0.0} <music21.note.Note C>
    {2.0} <music21.stream.Measure 2 offset=2.0>
        {0.0} <music21.note.Note G>
    {4.0} <music21.stream.Measure 3 offset=4.0>
        {0.0} <music21.note.Note E>
    {6.0} <music21.stream.Measure 4 offset=6.0>
        {0.0} <music21.note.Note D>
        {2.0} <music21.bar.Barline style=final>
music21.stream.timespans.streamToTimespanCollection(inputStream, flatten=True, classList=None)

Recurses through a score and constructs a TimespanCollection. Use Stream.asTimespans() generally since that caches the TimespanCollection.

>>> score = corpus.parse('bwv66.6')
>>> tree = stream.timespans.streamToTimespanCollection(score)
>>> tree
<TimespanCollection {165} (0.0 to 36.0) <music21.stream.Score ...>>
>>> for x in tree[:5]:
...     x
...
<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>
<ElementTimespan (0.0 to 1.0) <music21.note.Note E>>
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
<ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
>>> tree = stream.timespans.streamToTimespanCollection(
...     score,
...     flatten=False,
...     classList=(),
...     )

Each of these has 11 elements – mainly the Measures

>>> for x in tree:
...     x
...
<ElementTimespan (0.0 to 0.0) <music21.metadata.Metadata object at 0x...>>
<TimespanCollection {11} (0.0 to 36.0) <music21.stream.Part Soprano>>
<TimespanCollection {11} (0.0 to 36.0) <music21.stream.Part Alto>>
<TimespanCollection {11} (0.0 to 36.0) <music21.stream.Part Tenor>>
<TimespanCollection {11} (0.0 to 36.0) <music21.stream.Part Bass>>
<ElementTimespan (0.0 to 0.0) <music21.layout.StaffGroup <music21.stream.Part Soprano><music21.stream.Part Alto><music21.stream.Part Tenor><music21.stream.Part Bass>>>
>>> tenorElements = tree[3]
>>> tenorElements
<TimespanCollection {11} (0.0 to 36.0) <music21.stream.Part Tenor>>
>>> tenorElements.source
<music21.stream.Part Tenor>
>>> tenorElements.source is score[3]
True
music21.stream.timespans.timespansToChordifiedStream(timespans, templateStream=None)

Creates a score from the ElementTimespan objects stored in this offset-tree.

A “template” score may be used to provide measure and time-signature information.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> chordifiedScore = stream.timespans.timespansToChordifiedStream(
...     tree, templateStream=score)
>>> chordifiedScore.show('text')
{0.0} <music21.stream.Measure 0 offset=0.0>
    {0.0} <music21.clef.TrebleClef>
    {0.0} <music21.key.KeySignature of 3 sharps, mode minor>
    {0.0} <music21.meter.TimeSignature 4/4>
    {0.0} <music21.chord.Chord A3 E4 C#5>
    {0.5} <music21.chord.Chord G#3 B3 E4 B4>
{1.0} <music21.stream.Measure 1 offset=1.0>
    {0.0} <music21.chord.Chord F#3 C#4 F#4 A4>
    {1.0} <music21.chord.Chord G#3 B3 E4 B4>
    {2.0} <music21.chord.Chord A3 E4 C#5>
    {3.0} <music21.chord.Chord G#3 B3 E4 E5>
{5.0} <music21.stream.Measure 2 offset=5.0>
    {0.0} <music21.chord.Chord A3 E4 C#5>
    {0.5} <music21.chord.Chord C#3 E4 A4 C#5>
    {1.0} <music21.chord.Chord E3 E4 G#4 B4>
    {1.5} <music21.chord.Chord E3 D4 G#4 B4>
    {2.0} <music21.chord.Chord A2 C#4 E4 A4>
    {3.0} <music21.chord.Chord E#3 C#4 G#4 C#5>
{9.0} <music21.stream.Measure 3 offset=9.0>
    {0.0} <music21.layout.SystemLayout>
    {0.0} <music21.chord.Chord F#3 C#4 F#4 A4>
    {0.5} <music21.chord.Chord B2 D4 G#4 B4>
    {1.0} <music21.chord.Chord C#3 C#4 E#4 G#4>
    {1.5} <music21.chord.Chord C#3 B3 E#4 G#4>
    {2.0} <music21.chord.Chord F#2 A3 C#4 F#4>
    {3.0} <music21.chord.Chord F#3 C#4 F#4 A4>
...

TODO: Remove assert

music21.stream.timespans.timespansToPartwiseStream(timespans, templateStream=None)

todo docs

TimespanCollection

class music21.stream.timespans.TimespanCollection(timespans=None, source=None)

A datastructure for efficiently slicing a score.

This datastructure stores timespans: objects which implement both a startOffset and stopOffset property. It provides fast lookups of such objects and can quickly locate vertical overlaps.

While you can construct an offset-tree by hand, inserting timespans one at a time, the common use-case is to construct the offset-tree from an entire score at once:

>>> bach = corpus.parse('bwv66.6')
>>> tree = stream.timespans.streamToTimespanCollection(bach)
>>> print(tree.getVerticalityAt(17.0))
<Verticality 17.0 {F#3 C#4 A4}>

All offsets are assumed to be relative to the score’s origin.

Example: How many moments in Bach are consonant and how many are dissonant:

>>> totalConsonances = 0
>>> totalDissonances = 0
>>> for v in tree.iterateVerticalities():
...     if v.toChord().isConsonant():
...        totalConsonances += 1
...     else:
...        totalDissonances += 1
...
>>> (totalConsonances, totalDissonances)
(34, 17)

So 1/3 of the vertical moments in Bach are dissonant! But is this an accurate perception? Let’s sum up the total consonant duration vs. dissonant duration.

Do it again pairwise to figure out the length (actually this won’t include the last element)

>>> totalConsonanceDuration = 0
>>> totalDissonanceDuration = 0
>>> iterator = tree.iterateVerticalitiesNwise(n=2)
>>> for verticality1, verticality2 in iterator:
...     startOffset1 = verticality1.startOffset
...     startOffset2 = verticality2.startOffset
...     quarterLength = startOffset2 - startOffset1
...     if verticality1.toChord().isConsonant():
...        totalConsonanceDuration += quarterLength
...     else:
...        totalDissonanceDuration += quarterLength
...
>>> (totalConsonanceDuration, totalDissonanceDuration)
(25.5, 9.5)

Remove neighbor tones from the Bach chorale:

Here in Alto, measure 7, there’s a neighbor tone E#.

>>> bach.parts['Alto'].measure(7).show('text')
{0.0} <music21.note.Note F#>
{0.5} <music21.note.Note E#>
{1.0} <music21.note.Note F#>
{1.5} <music21.note.Note F#>
{2.0} <music21.note.Note C#>

We’ll get rid of it and a lot of other neighbor tones.

>>> for verticalities in tree.iterateVerticalitiesNwise(n=3):
...    horizontalities = tree.unwrapVerticalities(verticalities)
...    for unused_part, horizontality in horizontalities.items():
...        if horizontality.hasNeighborTone:
...            merged = horizontality[0].new(
...               stopOffset=horizontality[2].stopOffset,
...            ) # merged is a new ElementTimeSpan
...            tree.remove(horizontality[0])
...            tree.remove(horizontality[1])
...            tree.remove(horizontality[2])
...            tree.insert(merged)
...
>>> newBach = stream.timespans.timespansToPartwiseStream(
...     tree,
...     templateStream=bach,
...     )
>>> newBach.parts[1].measure(7).show('text')
{0.0} <music21.chord.Chord F#4>
{1.5} <music21.chord.Chord F#3>
{2.0} <music21.chord.Chord C#4>

The second F# is an octave lower, so it wouldn’t get merged even if adjacent notes were fused together (which they’re not).

Note

TimespanCollection is an implementation of an extended AVL tree. AVL trees are a type of binary tree, like Red-Black trees. AVL trees are very efficient at insertion when the objects being inserted are already sorted - which is usually the case with data extracted from a score. TimespanCollection is an extended AVL tree because each node in the tree keeps track of not just the start offsets of ElementTimespans stored at that node, but also the earliest and latest stop offset of all ElementTimespans stores at both that node and all nodes which are children of that node. This lets us quickly located ElementTimespans which overlap offsets or which are contained within ranges of offsets. This also means that the contents of a TimespanCollection are always sorted.

TODO: newBach.parts[‘Alto’].measure(7).show(‘text’) should work. KeyError: ‘provided key (Alto) does not match any id or group’

TODO: Doc examples for all functions, including privates.

TimespanCollection read-only properties

TimespanCollection.allOffsets

Gets all unique offsets (both starting and stopping) of all timespans in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for offset in tree.allOffsets[:10]:
...     offset
...
0.0
0.5
1.0
2.0
3.0
4.0
5.0
5.5
6.0
6.5
TimespanCollection.allParts
TimespanCollection.allStartOffsets

Gets all unique start offsets of all timespans in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for offset in tree.allStartOffsets[:10]:
...     offset
...
0.0
0.5
1.0
2.0
3.0
4.0
5.0
5.5
6.0
6.5
TimespanCollection.allStopOffsets

Gets all unique stop offsets of all timespans in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for offset in tree.allStopOffsets[:10]:
...     offset
...
0.5
1.0
2.0
4.0
5.5
6.0
7.0
8.0
9.5
10.5
TimespanCollection.earliestStartOffset

Gets the earlies start offset in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.earliestStartOffset
0.0
TimespanCollection.earliestStopOffset

Gets the earliest stop offset in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.earliestStopOffset
0.5
TimespanCollection.latestStartOffset

Gets the lateset start offset in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.latestStartOffset
35.0
TimespanCollection.latestStopOffset

Gets the latest stop offset in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.latestStopOffset
36.0
TimespanCollection.maximumOverlap

The maximum number of timespans overlapping at any given moment in this timespan collection.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.maximumOverlap
4
TimespanCollection.minimumOverlap

The minimum number of timespans overlapping at any given moment in this timespan collection.

In a tree created from a monophonic stream, the minimumOverlap will probably be either zero or one.

>>> score = corpus.parse('bwv66.6')
>>> tree = stream.timespans.streamToTimespanCollection(
...     score, flatten=False)
>>> tree[0].minimumOverlap
1
TimespanCollection.startOffset
TimespanCollection.stopOffset

TimespanCollection read/write properties

TimespanCollection.element

defined so a TimespanCollection can be used like an ElementTimespan

TODO: Look at subclassing or at least deriving from a common base...

TimespanCollection.source

TimespanCollection methods

TimespanCollection.copy()

Creates a new offset-tree with the same timespans as this offset-tree.

This is analogous to dict.copy().

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> newTree = tree.copy()
TimespanCollection.findNextElementTimespanInSameStreamByClass(elementTimespan, classList=None)

Finds next element timespan in the same stream class as elementTimespan.

Default classList is (stream.Part, )

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> timespan = tree[0]
>>> timespan
<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>
>>> timespan.part
<music21.stream.Part Soprano>
>>> timespan = tree.findNextElementTimespanInSameStreamByClass(timespan)
>>> timespan
<ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
>>> timespan.part
<music21.stream.Part Soprano>
>>> timespan = tree.findNextElementTimespanInSameStreamByClass(timespan)
>>> timespan
<ElementTimespan (1.0 to 2.0) <music21.note.Note A>>
>>> timespan.part
<music21.stream.Part Soprano>
TimespanCollection.findPreviousElementTimespanInSameStreamByClass(elementTimespan, classList=None)

Finds next element timespan in the same Part/Measure, etc. (specify in classList) as the elementTimespan.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> timespan = tree[-1]
>>> timespan
<ElementTimespan (35.0 to 36.0) <music21.note.Note F#>>
>>> timespan.part
<music21.stream.Part Bass>
>>> timespan = tree.findPreviousElementTimespanInSameStreamByClass(timespan)
>>> timespan
<ElementTimespan (34.0 to 35.0) <music21.note.Note B>>
>>> timespan.part
<music21.stream.Part Bass>
>>> timespan = tree.findPreviousElementTimespanInSameStreamByClass(timespan)
>>> timespan
<ElementTimespan (33.0 to 34.0) <music21.note.Note D>>
>>> timespan.part
<music21.stream.Part Bass>
TimespanCollection.findTimespansOverlapping(offset)

Finds timespans in this offset-tree which overlap offset.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for timespan in tree.findTimespansOverlapping(0.5):
...     timespan
...
<ElementTimespan (0.0 to 1.0) <music21.note.Note E>>
TimespanCollection.findTimespansStartingAt(offset)

Finds timespans in this offset-tree which start at offset.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for timespan in tree.findTimespansStartingAt(0.5):
...     timespan
...
<ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
<ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
<ElementTimespan (0.5 to 1.0) <music21.note.Note G#>>
TimespanCollection.findTimespansStoppingAt(offset)

Finds timespans in this offset-tree which stop at offset.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for timespan in tree.findTimespansStoppingAt(0.5):
...     timespan
...
<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
TimespanCollection.getStartOffsetAfter(offset)

Gets start offset after offset.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.getStartOffsetAfter(0.5)
1.0
>>> tree.getStartOffsetAfter(35) is None
True

Returns none if no succeeding offset exists.

TimespanCollection.getStartOffsetBefore(offset)

Gets the start offset immediately preceding offset in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.getStartOffsetBefore(100)
35.0
>>> tree.getStartOffsetBefore(0) is None
True

Return None if no preceding offset exists.

TimespanCollection.getVerticalityAt(offset)

Gets the verticality in this offset-tree which starts at offset.

>>> bach = corpus.parse('bwv66.6')
>>> tree = bach.asTimespans()
>>> tree.getVerticalityAt(2.5)
<Verticality 2.5 {G#3 B3 E4 B4}>

Verticalities outside the range still return a Verticality, but it might be empty...

>>> tree.getVerticalityAt(2000)
<Verticality 2000 {}>


Test that it still works if the tree is empty...

>>> tree = bach.asTimespans(classList=(instrument.Tuba,))
>>> tree
<TimespanCollection {0} (-inf to inf) <music21.stream.Score ...>>
>>> tree.getVerticalityAt(5.0)
<Verticality 5.0 {}>           

Return verticality.

TimespanCollection.getVerticalityAtOrBefore(offset)

Gets the verticality in this offset-tree which starts at offset.

If the found verticality has no start timespans, the function returns the next previous verticality with start timespans.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.getVerticalityAtOrBefore(0.125)
<Verticality 0.0 {A3 E4 C#5}>
>>> tree.getVerticalityAtOrBefore(0.)
<Verticality 0.0 {A3 E4 C#5}>
TimespanCollection.index(timespan)

Gets index of timespan in tree.

>>> timespans = [
...     stream.timespans.Timespan(0, 2),
...     stream.timespans.Timespan(0, 9),
...     stream.timespans.Timespan(1, 1),
...     stream.timespans.Timespan(2, 3),
...     stream.timespans.Timespan(3, 4),
...     stream.timespans.Timespan(4, 9),
...     stream.timespans.Timespan(5, 6),
...     stream.timespans.Timespan(5, 8),
...     stream.timespans.Timespan(6, 8),
...     stream.timespans.Timespan(7, 7),
...     ]
>>> tree = stream.timespans.TimespanCollection()
>>> tree.insert(timespans)
>>> for timespan in timespans:
...     print("%r %d" % (timespan, tree.index(timespan)))
...
<Timespan 0 2> 0
<Timespan 0 9> 1
<Timespan 1 1> 2
<Timespan 2 3> 3
<Timespan 3 4> 4
<Timespan 4 9> 5
<Timespan 5 6> 6
<Timespan 5 8> 7
<Timespan 6 8> 8
<Timespan 7 7> 9
>>> tree.index(stream.timespans.Timespan(-100, 100))
Traceback (most recent call last):
ValueError: <Timespan -100 100> not in timespan collection.
TimespanCollection.insert(timespans)

Inserts timespans into this offset-tree.

TimespanCollection.iterateConsonanceBoundedVerticalities()

Iterates consonant-bounded verticality subsequences in this offset-tree.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> for subsequence in tree.iterateConsonanceBoundedVerticalities():
...     print('Subequence:')
...     for verticality in subsequence:
...         print('\t[{}] {}: {} [{}]'.format(
...             verticality.measureNumber,
...             verticality,
...             verticality.isConsonant,
...             verticality.beatStrength,
...             ))
...
Subequence:
    [2] <Verticality 6.0 {E3 E4 G#4 B4}>: True [0.25]
    [2] <Verticality 6.5 {E3 D4 G#4 B4}>: False [0.125]
    [2] <Verticality 7.0 {A2 C#4 E4 A4}>: True [0.5]
Subequence:
    [3] <Verticality 9.0 {F#3 C#4 F#4 A4}>: True [1.0]
    [3] <Verticality 9.5 {B2 D4 G#4 B4}>: False [0.125]
    [3] <Verticality 10.0 {C#3 C#4 E#4 G#4}>: True [0.25]
Subequence:
    [3] <Verticality 10.0 {C#3 C#4 E#4 G#4}>: True [0.25]
    [3] <Verticality 10.5 {C#3 B3 E#4 G#4}>: False [0.125]
    [3] <Verticality 11.0 {F#2 A3 C#4 F#4}>: True [0.5]
Subequence:
    [3] <Verticality 12.0 {F#3 C#4 F#4 A4}>: True [0.25]
    [4] <Verticality 13.0 {G#3 B3 F#4 B4}>: False [1.0]
    [4] <Verticality 13.5 {F#3 B3 F#4 B4}>: False [0.125]
    [4] <Verticality 14.0 {G#3 B3 E4 B4}>: True [0.25]
Subequence:
    [4] <Verticality 14.0 {G#3 B3 E4 B4}>: True [0.25]
    [4] <Verticality 14.5 {A3 B3 E4 B4}>: False [0.125]
    [4] <Verticality 15.0 {B3 D#4 F#4}>: True [0.5]
Subequence:
    [4] <Verticality 15.0 {B3 D#4 F#4}>: True [0.5]
    [4] <Verticality 15.5 {B2 A3 D#4 F#4}>: False [0.125]
    [4] <Verticality 16.0 {C#3 G#3 C#4 E4}>: True [0.25]
Subequence:
    [5] <Verticality 17.5 {F#3 D4 F#4 A4}>: True [0.125]
    [5] <Verticality 18.0 {G#3 C#4 E4 B4}>: False [0.25]
    [5] <Verticality 18.5 {G#3 B3 E4 B4}>: True [0.125]
Subequence:
    [6] <Verticality 24.0 {F#3 C#4 F#4 A4}>: True [0.25]
    [7] <Verticality 25.0 {B2 D4 F#4 G#4}>: False [1.0]
    [7] <Verticality 25.5 {C#3 C#4 E#4 G#4}>: True [0.125]
Subequence:
    [7] <Verticality 25.5 {C#3 C#4 E#4 G#4}>: True [0.125]
    [7] <Verticality 26.0 {D3 C#4 F#4}>: False [0.25]
    [7] <Verticality 26.5 {D3 F#3 B3 F#4}>: True [0.125]
Subequence:
    [8] <Verticality 29.0 {A#2 F#3 C#4 F#4}>: True [1.0]
    [8] <Verticality 29.5 {A#2 F#3 D4 F#4}>: False [0.125]
    [8] <Verticality 30.0 {A#2 C#4 E4 F#4}>: False [0.25]
    [8] <Verticality 31.0 {B2 C#4 E4 F#4}>: False [0.5]
    [8] <Verticality 32.0 {C#3 B3 D4 F#4}>: False [0.25]
    [8] <Verticality 32.5 {C#3 A#3 C#4 F#4}>: False [0.125]
    [9] <Verticality 33.0 {D3 B3 F#4}>: True [1.0]
Subequence:
    [9] <Verticality 33.0 {D3 B3 F#4}>: True [1.0]
    [9] <Verticality 33.5 {D3 B3 C#4 F#4}>: False [0.125]
    [9] <Verticality 34.0 {B2 B3 D4 F#4}>: True [0.25]
Subequence:
    [9] <Verticality 34.0 {B2 B3 D4 F#4}>: True [0.25]
    [9] <Verticality 34.5 {B2 B3 D4 E#4}>: False [0.125]
    [9] <Verticality 35.0 {F#3 A#3 C#4 F#4}>: True [0.5]
TimespanCollection.iterateVerticalities(reverse=False)

Iterates all vertical moments in this offset-tree.

Note

The offset-tree can be mutated while its verticalities are iterated over. Each verticality holds a reference back to the offset-tree and will ask for the start-offset after (or before) its own start offset in order to determine the next verticality to yield. If you mutate the tree by adding or deleting timespans, the next verticality will reflect those changes.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> iterator = tree.iterateVerticalities()
>>> for _ in range(10):
...     next(iterator)
...
<Verticality 0.0 {A3 E4 C#5}>
<Verticality 0.5 {G#3 B3 E4 B4}>
<Verticality 1.0 {F#3 C#4 F#4 A4}>
<Verticality 2.0 {G#3 B3 E4 B4}>
<Verticality 3.0 {A3 E4 C#5}>
<Verticality 4.0 {G#3 B3 E4 E5}>
<Verticality 5.0 {A3 E4 C#5}>
<Verticality 5.5 {C#3 E4 A4 C#5}>
<Verticality 6.0 {E3 E4 G#4 B4}>
<Verticality 6.5 {E3 D4 G#4 B4}>

Verticalities can also be iterated in reverse:

>>> iterator = tree.iterateVerticalities(reverse=True)
>>> for _ in range(10):
...     next(iterator)
...
<Verticality 35.0 {F#3 A#3 C#4 F#4}>
<Verticality 34.5 {B2 B3 D4 E#4}>
<Verticality 34.0 {B2 B3 D4 F#4}>
<Verticality 33.5 {D3 B3 C#4 F#4}>
<Verticality 33.0 {D3 B3 F#4}>
<Verticality 32.5 {C#3 A#3 C#4 F#4}>
<Verticality 32.0 {C#3 B3 D4 F#4}>
<Verticality 31.0 {B2 C#4 E4 F#4}>
<Verticality 30.0 {A#2 C#4 E4 F#4}>
<Verticality 29.5 {A#2 F#3 D4 F#4}>
TimespanCollection.iterateVerticalitiesNwise(n=3, reverse=False)

Iterates verticalities in groups of length n.

Note

The offset-tree can be mutated while its verticalities are iterated over. Each verticality holds a reference back to the offset-tree and will ask for the start-offset after (or before) its own start offset in order to determine the next verticality to yield. If you mutate the tree by adding or deleting timespans, the next verticality will reflect those changes.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> iterator = tree.iterateVerticalitiesNwise(n=2)
>>> for _ in range(4):
...     print(next(iterator))
...
<VerticalitySequence: [
    <Verticality 0.0 {A3 E4 C#5}>,
    <Verticality 0.5 {G#3 B3 E4 B4}>
    ]>
<VerticalitySequence: [
    <Verticality 0.5 {G#3 B3 E4 B4}>,
    <Verticality 1.0 {F#3 C#4 F#4 A4}>
    ]>
<VerticalitySequence: [
    <Verticality 1.0 {F#3 C#4 F#4 A4}>,
    <Verticality 2.0 {G#3 B3 E4 B4}>
    ]>
<VerticalitySequence: [
    <Verticality 2.0 {G#3 B3 E4 B4}>,
    <Verticality 3.0 {A3 E4 C#5}>
    ]>

Grouped verticalities can also be iterated in reverse:

>>> iterator = tree.iterateVerticalitiesNwise(n=2, reverse=True)
>>> for _ in range(4):
...     print(next(iterator))
...
<VerticalitySequence: [
    <Verticality 34.5 {B2 B3 D4 E#4}>,
    <Verticality 35.0 {F#3 A#3 C#4 F#4}>
    ]>
<VerticalitySequence: [
    <Verticality 34.0 {B2 B3 D4 F#4}>,
    <Verticality 34.5 {B2 B3 D4 E#4}>
    ]>
<VerticalitySequence: [
    <Verticality 33.5 {D3 B3 C#4 F#4}>,
    <Verticality 34.0 {B2 B3 D4 F#4}>
    ]>
<VerticalitySequence: [
    <Verticality 33.0 {D3 B3 F#4}>,
    <Verticality 33.5 {D3 B3 C#4 F#4}>
    ]>
TimespanCollection.remove(timespans)

Removes timespans from this offset-tree.

TODO: remove assert

TimespanCollection.splitAt(offsets)

Splits all timespans in this offset-tree at offsets, operating in place.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree.findTimespansStartingAt(0.1)
()
>>> for elementTimespan in tree.findTimespansOverlapping(0.1):
...     print("%r, %s" % (elementTimespan, elementTimespan.part.id))
...
<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>, Soprano
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>, Tenor
<ElementTimespan (0.0 to 0.5) <music21.note.Note A>>, Bass
<ElementTimespan (0.0 to 1.0) <music21.note.Note E>>, Alto
>>> tree.splitAt(0.1)
>>> for elementTimespan in tree.findTimespansStartingAt(0.1):
...     print("%r, %s" % (elementTimespan, elementTimespan.part.id))
...
<ElementTimespan (0.1 to 0.5) <music21.note.Note C#>>, Soprano
<ElementTimespan (0.1 to 1.0) <music21.note.Note E>>, Alto
<ElementTimespan (0.1 to 0.5) <music21.note.Note A>>, Tenor
<ElementTimespan (0.1 to 0.5) <music21.note.Note A>>, Bass
>>> tree.findTimespansOverlapping(0.1)
()
TimespanCollection.toPartwiseTimespanCollections()
static TimespanCollection.unwrapVerticalities(verticalities)

Unwraps a sequence of Verticality objects into a dictionary of Part:Horizontality key/value pairs.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> iterator = tree.iterateVerticalitiesNwise()
>>> verticalities = next(iterator)
>>> unwrapped = tree.unwrapVerticalities(verticalities)
>>> for part in sorted(unwrapped,
...     key=lambda x: x.getInstrument().partName,
...     ):
...     print(part)
...     horizontality = unwrapped[part]
...     for timespan in horizontality:
...         print('\t%r' % timespan)
...
<music21.stream.Part Alto>
    <ElementTimespan (0.0 to 1.0) <music21.note.Note E>>
    <ElementTimespan (1.0 to 2.0) <music21.note.Note F#>>
<music21.stream.Part Bass>
    <ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
    <ElementTimespan (0.5 to 1.0) <music21.note.Note G#>>
    <ElementTimespan (1.0 to 2.0) <music21.note.Note F#>>
<music21.stream.Part Soprano>
    <ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>
    <ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
    <ElementTimespan (1.0 to 2.0) <music21.note.Note A>>
<music21.stream.Part Tenor>
    <ElementTimespan (0.0 to 0.5) <music21.note.Note A>>
    <ElementTimespan (0.5 to 1.0) <music21.note.Note B>>
    <ElementTimespan (1.0 to 2.0) <music21.note.Note C#>>

Timespan

class music21.stream.timespans.Timespan(startOffset=-inf, stopOffset=inf)

A span of time, with a start offset and stop offset.

Useful for demonstrating various properties of the timespan-collection class family.

>>> timespan = stream.timespans.Timespan(-1.5, 3.25)
>>> print(timespan)
<Timespan -1.5 3.25>

ElementTimespan

class music21.stream.timespans.ElementTimespan(element=None, beatStrength=None, parentStartOffset=None, parentStopOffset=None, parentage=None, startOffset=None, stopOffset=None)

A span of time anchored to an element in a score. The span of time may be the same length as the element in the score. It may be shorter (a “slice” of an element) or it may be longer (in the case of a timespan that is anchored to a single element but extends over rests or other notes following a note)

ElementTimespans give information about an element (such as a Note). It knows its absolute position with respect to the element passed into TimespanCollection. It contains information about what measure it’s in, what part it’s in, etc.

Example, getting a passing tone from a known location from a Bach chorale.

First we create an Offset tree:

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> tree
<TimespanCollection {165} (0.0 to 36.0) <music21.stream.Score ...>>

Then get the verticality from offset 6.5, which is beat two-and-a-half of measure 2 (the piece is in 4/4 with a quarter-note pickup)

>>> verticality = tree.getVerticalityAt(6.5)
>>> verticality
<Verticality 6.5 {E3 D4 G#4 B4}>

There are four elementTimespans in the verticality – each representing a note. The notes are arranged from lowest to highest.

We can find all the elementTimespans that start exactly at 6.5. There’s one.

>>> verticality.startTimespans
(<ElementTimespan (6.5 to 7.0) <music21.note.Note D>>,)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan
<ElementTimespan (6.5 to 7.0) <music21.note.Note D>>

What can we do with a elementTimespan? We can get its Part object or the Part object name

>>> elementTimespan.part
<music21.stream.Part Tenor>
>>> elementTimespan.partName
u'Tenor'

Find out what measure it’s in:

>>> elementTimespan.measureNumber
2
>>> elementTimespan.parentStartOffset
5.0

The position in the measure is given by subtracting that from the .startOffset:

>>> elementTimespan.startOffset - elementTimespan.parentStartOffset
1.5


>>> elementTimespan.beatStrength
0.125
>>> elementTimespan.element
<music21.note.Note D>

These are not dynamic, so changing the Score object does not change the measureNumber, beatStrength, etc.

ElementTimespan read-only properties

ElementTimespan.beatStrength

The elementTimespan’s element’s beat-strength.

This may be overriden during instantiation by passing in a custom beat-strength. That can be useful when you are generating new elementTimespans based on old ones, and want to maintain pitch information from the old elementTimespan but change the start offset to reflect that of another timespan.

TODO: Tests

ElementTimespan.element

The elementTimespan’s element.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan.element
<music21.note.Note A>
ElementTimespan.measureNumber

The measure number of the measure containing the element.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan.measureNumber
1
ElementTimespan.parentStartOffset
ElementTimespan.parentStopOffset
ElementTimespan.parentage

The Stream hierarchy above the elementTimespan’s element.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> for streamSite in elementTimespan.parentage:
...     streamSite
<music21.stream.Measure 1 offset=1.0>
<music21.stream.Part Soprano>
<music21.stream.Score ...>
ElementTimespan.part

find the object in the parentage that is a Part object:

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[2]
>>> elementTimespan
<ElementTimespan (1.0 to 2.0) <music21.note.Note C#>>
>>> elementTimespan.part
<music21.stream.Part Tenor>
ElementTimespan.partName

The part name of the part containing the elementTimespan’s element.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan.partName
u'Soprano'

TODO: remove and see if something better can be done with elementTimespan.part’s Part object

ElementTimespan.pitches

Gets the pitches of the element wrapped by this elementTimespan.

This treats notes as chords.

TODO: tests, examples of usage.

ElementTimespan.quarterLength

TODO: Tests that show a case where this might be different from the quarterLength of the element.

ElementTimespan.startOffset

The start offset of the elementTimespan’s element, relative to its containing score.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan.startOffset
1.0
ElementTimespan.stopOffset

The stop offset of the elementTimespan’s element, relative to its containing score.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[0]
>>> elementTimespan.stopOffset
2.0

ElementTimespan methods

ElementTimespan.getParentageByClass(classList=None)

returns that is the first parentage that has this classList. default stream.Part

>>> score = corpus.parse('bwv66.6')
>>> score.id = 'bach'
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(1.0)
>>> elementTimespan = verticality.startTimespans[2]
>>> elementTimespan
<ElementTimespan (1.0 to 2.0) <music21.note.Note C#>>
>>> elementTimespan.getParentageByClass(classList=(stream.Part,))
<music21.stream.Part Tenor>
>>> elementTimespan.getParentageByClass(classList=(stream.Measure,))
<music21.stream.Measure 1 offset=1.0>
>>> elementTimespan.getParentageByClass(classList=(stream.Score,))
<music21.stream.Score bach>

The closest parent is returned in case of a multiple list...

>>> searchTuple = (stream.Voice, stream.Measure, stream.Part)
>>> elementTimespan.getParentageByClass(classList=searchTuple)
<music21.stream.Measure 1 offset=1.0>
ElementTimespan.mergeWith(elementTimespan)

Merges two consecutive like-pitched element timespans, keeping score-relevant information from the first of the two, such as its Music21 Element, and any beatstrength information.

This is useful when using timespans to perform score reduction.

Let’s demonstrate merging some contiguous E’s in the alto part of a Bach chorale:

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> timespan_one = tree[12]
>>> print(timespan_one)
<ElementTimespan (2.0 to 3.0) <music21.note.Note E>>
>>> print(timespan_one.part)
<music21.stream.Part Alto>
>>> timespan_two = tree.findNextElementTimespanInSameStreamByClass(
...     timespan_one)
>>> print(timespan_two)
<ElementTimespan (3.0 to 4.0) <music21.note.Note E>>
>>> merged = timespan_one.mergeWith(timespan_two)
>>> print(merged)
<ElementTimespan (2.0 to 4.0) <music21.note.Note E>>
>>> merged.part is timespan_one.part
True
>>> merged.beatStrength == timespan_one.beatStrength
True

Attempting to merge timespans which are not contiguous, or which do not have identical pitches will result in error:

>>> tree[0].mergeWith(tree[50])
Traceback (most recent call last):
...
TimespanException: Cannot merge <ElementTimespan (0.0 to 0.5) <music21.note.Note C#>> with <ElementTimespan (9.5 to 10.0) <music21.note.Note B>>: not contiguous

This is probably not what you want to do: get the next element timespan in the same score:

>>> timespan_twoWrong = tree.findNextElementTimespanInSameStreamByClass(
...     timespan_one, classList=(stream.Score,))
>>> print(timespan_twoWrong)
<ElementTimespan (3.0 to 4.0) <music21.note.Note C#>>
>>> print(timespan_twoWrong.part)
<music21.stream.Part Soprano>
ElementTimespan.new(beatStrength=None, element=None, parentStartOffset=None, parentStopOffset=None, startOffset=None, stopOffset=None)

TODO: Docs and Tests

ElementTimespan.splitAt(offset)

Split elementTimespan at offset.

>>> score = corpus.parse('bwv66.6')
>>> tree = score.asTimespans()
>>> verticality = tree.getVerticalityAt(0)
>>> timespan = verticality.startTimespans[0]
>>> timespan
<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>
>>> for shard in timespan.splitAt(0.25):
...     shard
...
<ElementTimespan (0.0 to 0.25) <music21.note.Note C#>>
<ElementTimespan (0.25 to 0.5) <music21.note.Note C#>>
>>> timespan.splitAt(1000)
(<ElementTimespan (0.0 to 0.5) <music21.note.Note C#>>,)