.. _usersGuide_06_stream2:
.. WARNING: DO NOT EDIT THIS FILE:
AUTOMATICALLY GENERATED.
PLEASE EDIT THE .py FILE DIRECTLY.
.. code:: python
User's Guide, Chapter 6: Streams (II): Hierarchies, Recursion, and Flattening
=============================================================================
We ended Chapter 4 (:ref:`Streams (I) `.) with
a :class:`~music21.stream.Stream` that was contained within another
``Stream`` object. Let's recreate that class:
.. code:: python
from music21 import *
note1 = note.Note("C4")
note1.duration.type = 'half'
note2 = note.Note("F#4")
note3 = note.Note("B-2")
stream1 = stream.Stream()
stream1.id = 'some notes'
stream1.append(note1)
stream1.append(note2)
stream1.append(note3)
biggerStream = stream.Stream()
note2 = note.Note("D#5")
biggerStream.insert(0, note2)
biggerStream.append(stream1)
The only way to find out what was in the contained Stream that we
demonstrated so far was the :meth:`~music21.base.Music21Object.show`
method using the ``('text')`` argument.
.. code:: python
biggerStream.show('text')
.. parsed-literal::
:class: ipython-result
{0.0}
{1.0}
{0.0}
{2.0}
{3.0}
As Chapter 4 noted, there's
Accessing Scores, Parts, Measures, and Notes
--------------------------------------------
Streams provide a way to structure and position music21 objects both
hierarchically and temporally. A Stream, or a Stream subclass such as
:class:`~music21.stream.Measure`, can be placed within another Stream.
A common arrangement of nested Streams is a
:class:`~music21.stream.Score` Stream containing one or more
:class:`~music21.stream.Part` Streams, each Part Stream in turn
containing one or more :class:`~music21.stream.Measure` Streams.
Such an arrangement of Stream objects is the common way musical scores
are represented in music21. For example, importing a four-part chorale
by J. S. Bach will provide a Score object with four Part Streams, each
Part containing multiple Measure objects. Music21 comes with a
:ref:`moduleCorpus` module that provides access to a large collection
of scores, including all the Bach chorales. We can parse the score from
the corpus with the :func:`~music21.corpus.parse` function.
.. code:: python
sBach = corpus.parse('bach/bwv57.8')
| We can access and examine elements at each level of this Score by
using standard Python syntax for lists within lists. Thus, we can see
the length of each component: first the Score which has five elements, a
:class:`~music21.metadata.Metadata` object and four parts. Then we
find the length of first Part at index one which indicates 19 objects
(18 of them are measures).
| Then within that part we find an object (a Measure) at index 1. All of
these subprograms can be accessed from looking within the same score
object ``sBach``.
.. code:: python
len(sBach)
.. parsed-literal::
:class: ipython-result
6
.. code:: python
len(sBach[1])
.. parsed-literal::
:class: ipython-result
19
.. code:: python
len(sBach[1][1])
.. parsed-literal::
:class: ipython-result
6
But how did we know that index [1] would be a Part and index [1][1]
would be a measure? As writers of the tutorial, we know this piece well
enough to know that. But as we noted above, more than just Measures
might be stored in a Part object (such as
:class:`~music21.instrument.Instrument` objects), and more than just
Note and Rest objects might be stored in a Measure (such as
:class:`~music21.meter.TimeSignature` and
:class:`~music21.key.KeySignature` objects). We it's much safer to
filter Stream and Stream subclasses by the class we seek. To repeat the
count and select specific classes, we can use the
:meth:`~music21.stream.Stream.getElementsByClass` method.
Notice how the counts deviate from the examples above.
.. code:: python
len(sBach.getElementsByClass(stream.Part))
.. parsed-literal::
:class: ipython-result
4
.. code:: python
len(sBach.getElementsByClass(stream.Part)[0].getElementsByClass(stream.Measure))
.. parsed-literal::
:class: ipython-result
18
.. code:: python
len(sBach.getElementsByClass(stream.Part)[0].getElementsByClass(stream.Measure)[1].getElementsByClass(note.Note))
.. parsed-literal::
:class: ipython-result
3
The :meth:`~music21.stream.Stream.getElementsByClass` method can also
take a string representation of the last section of the class name. Thus
we could've rewritten the first call above as:
.. code:: python
len(sBach.getElementsByClass('Part'))
.. parsed-literal::
:class: ipython-result
4
This way of doing things is a bit faster to code, but a little less
safe. Suppose, for instance there were objects of type stream.Measure
and tape.Measure; the latter way of writing the code would get both of
them. (But this ambiguity is rare enough that it's safe enough to use
the strings in most code.)
There are some convenience properties you should know about. Calling
``.parts`` is the same as ``.getElementsByClass(stream.Part)`` and
calling ``.notes`` is the same as
``.getElementsByClass([note.Note, note.Chord])``. Notice that the last
example also shows that you can give more than one class to
``getElementsByClass`` by passing in a list of classes. Note also that
when using ``.parts`` or ``.notes``, you do not write the ``()`` after
the name. Also be aware that ``.notes`` does not include rests. For
that, we have a method called ``.notesAndRests``.
The index position of a Measure is often not the same as the Measure
number. For instance, most pieces that don't have pickup measures begin
with measure 1, not zero. Sometimes there are measure discontinuities
within a piece (e.g., some people number first and second endings with
the same measure number). For that reason, gathering Measures is best
accomplished not with ``getElementsByClass(stream.Measure)`` but instead
with either the :meth:`~music21.stream.Stream.measures` method
(returning a Stream of Parts or Measures) or the
:meth:`~music21.stream.Stream.measure` method (returning a single
Measure). What is great about these methods is that they can work on a
whole score and not just a single part.
Recursion in Streams
--------------------
Flattening a Stream
-------------------
While nested Streams offer expressive flexibility, it is often useful to
be able to flatten all Stream and Stream subclasses into a single Stream
containing only the elements that are not Stream subclasses. The
:attr:``~music21.stream.Stream.flat`` property provides immediate access
to such a flat representation of a Stream. For example, doing a similar
count of components, such as that show above, we see that we cannot get
to all of the Note objects of a complete Score until we flatten its Part
and Measure objects by accessing the ``flat`` attribute.
.. code:: python
len(sBach.getElementsByClass(note.Note))
.. parsed-literal::
:class: ipython-result
0
.. code:: python
len(sBach.flat.getElementsByClass(note.Note))
.. parsed-literal::
:class: ipython-result
213
Element offsets are always relative to the Stream that contains them.
For example, a Measure, when placed in a Stream, might have an offset of
16. This offset describes the position of the Measure in the Stream.
Components of this Measure, such as Notes, have offset values relative
only to their container, the Measure. The first Note of this Measure,
then, has an offset of 0. In the following example we find that the
offset of measure eight (using the
:meth:`~music21.base.Music21Object.getOffsetBySite` method) is 21; the
offset of the second Note in this Measure (index 1), however, is 1.
.. code:: python
m = sBach.parts[0].getElementsByClass('Measure')[7]
m.getOffsetBySite(sBach.parts[0])
.. parsed-literal::
:class: ipython-result
21.0
.. code:: python
n = sBach.parts[0].measure(8).notes[1]
n
.. parsed-literal::
:class: ipython-result
.. code:: python
n.getOffsetBySite(m)
.. parsed-literal::
:class: ipython-result
1.0
Flattening a structure of nested Streams will set new, shifted offsets
for each of the elements on the Stream, reflecting their appropriate
position in the context of the Stream from which the ``flat`` property
was accessed. For example, if a flat version of the first part of the
Bach chorale is obtained, the note defined above has the appropriate
offset of 22 (the Measure offset of 21 plus the Note offset within this
Measure of 1).
.. code:: python
pFlat = sBach.parts[0].flat
indexN = pFlat.index(n)
pFlat[indexN]
.. parsed-literal::
:class: ipython-result
.. code:: python
pFlat[indexN].offset
.. parsed-literal::
:class: ipython-result
22.0
As an aside, it is important to recognize that the offset of the Note
has not been edited; instead, a Note, as all Music21Objects, can store
multiple pairs of sites and offsets. Music21Objects retain an offset
relative to all Stream or Stream subclasses they are contained within,
even if just in passing.