User’s Guide, Chapter 4: Lists, Streams (I) and Output

In the last two chapters, I introduced the concept of Note objects which are made up of Pitch and Duration objects, and we even displayed a Note on a staff and played it via MIDI. But unless you’re challenging Cage and Webern for the status of least musical material, you will probably want to analyze, manipulate, or create more than one Note.

Python has ways of working with multiple objects and music21 extends those ways to be more musical. Let’s look at how Python does it first and then see how music21 extends these ways. (If you’ve been programming for a bit, or especially if you have Python experience, skip to the section on Streams below after creating the objects note1, note2 and note3 described below).

Say you have two notes, a C and an F# in the middle of the treble staff. (If the concept of working with a tritone bothers you, go ahead and make the second note a G; we won’t mind; we’ll just call you Pope Gregory from now on). Lets create those notes:

Working with multiple objects via Lists

from music21 import *
note1 = note.Note("C4")
note2 = note.Note("F#4")

Let’s make the first note a half note by modifying its duration (by default all Note objects are quarter notes):

note1.duration.type = 'half'
note1.duration.quarterLength
 2.0
note2.duration.quarterLength
 1.0

To print the step (that is, the name without any octave or accidental information) of each of these notes, you could do something like this:

print(note1.step)
 C
print(note2.step)
 F

But suppose you had thirty notes? Then it’d be a pain to type “print(noteX.step)” thirty times. Fortunately, there’s a solution: we can put each of the note objects into a List which is a built in Python object that stores multiple other objects (like Notes or Chords, or even things like numbers). To create a list in Python, put square brackets ([]) around the things that you want to put in the list, separated by commas. Let’s create a list called noteList that contains note1 and note2:

noteList = [note1, note2]

We can check that noteList contains our Notes by printing it:

print(noteList)
 [<music21.note.Note C>, <music21.note.Note F#>]

The list is represented by the square brackets around the end with the comma in between them, just like how they were created originally. The act of creation is mirrored in the representation. That’s nice. Medieval philosophers would approve.

Now we can write a two-line program that will print the step of each note in noteList. Most modern languages have a way of doing some action for each member (“element”) in a list (also called an “array” or sometimes “row”). In Python this is the “for” command. When you type these lines, make sure to type the spaces at the start of the second line. (When you’re done typing print(thisNote.step), you’ll probably have to hit enter twice to see the results.)

for thisNote in noteList:
    print(thisNote.step)
 C
 F

What’s happening here? What for thisNote in noteList: says is that Python should take each note in noteList in order and temporarily call that note “thisNote” (you could have it called anything you want; myNote, n, currentNote are all good names, but note is not because note is the name of a module). Then the “:” at the end of the line indicates that everything that happens for a bit will apply to every Note in noteList one at a time. How does Python know when “a bit” is over? Simple: every line that is a part of the loop needs to be indented by putting in some spaces. (I usually use four spaces or hit tab. Some people use two spaces. Just be consistent.)

Loops don’t save much time here, but imagine if noteList had dozens or hundreds of Notes in it? Then the ability to do something to each object becomes more and more important.

Let’s add another note to noteList. First let’s create another note, a low B-flat:

note3 = note.Note("B-2")

Then we’ll append that note to the end of noteList:

noteList.append(note3)

Lists can be manipulated or changed. They are called “mutable” objects (we’ll learn about immutable objects later). Streams, as we will see, can be manipulated the same way through .append().

We can see that the length of noteList is now 3 using the len() function:

len(noteList)
 3

And if we write our looping function again, we will get a third note:

for thisNote in noteList:
    print(thisNote.step)
 C
 F
 B

We can find out what the first note of noteList is by writing:

noteList[0]
 <music21.note.Note C>

Notice that in a list, the first element is [0], not [1]. There are all sorts of historical reasons why computers start counting lists with zero rather than one–some good, some obsolete–but we need to live with this if we’re going to get any work done. Think of it like how floors are numbered in European buildings compared to American buildings. If we go forward one note, to the second note, we write:

noteList[1]
 <music21.note.Note F#>

We can also ask noteList where is note2 within it, using the index() method:

noteList.index(note2)
 1

If we want to get the last element of a list, we can write:

noteList[-1]
 <music21.note.Note B->

Which is how basements are numbered in Europe as well. This is the same element as noteList[2] (our third Note), as we can have Python prove:`

noteList[-1] is noteList[2]
 True

Lists will become important tools in your programming, but they don’t know anything about music. To get some intelligence into our music we’ll need to know about a music21 object similar to lists, called a Stream.

Introduction to Streams

The Stream object and its subclasses (Score, Part, Measure) are the fundamental containers for music21 objects such as Note, Chord, Clef, TimeSignature objects.

A container is like a Python list (or an array in some languages).

Objects stored in a Stream are generally spaced in time; each stored object has an offset usually representing how many quarter notes it lies from the beginning of the Stream. For instance in a 4/4 measure of two half notes, the first note will be at offset 0.0, and the second at offset 2.0.

Streams, further, can store other Streams, permitting a wide variety of nested, ordered, and timed structures. These stored streams also have offsets. So if we put two 4/4 Measure objects (subclasses of Stream) into a Part (also a type of Stream), then the first measure will be at offset 0.0 and the second measure will be at offset 4.0.

Commonly used subclasses of Streams include the Score, Part, and Measure. It is important to grasp that any time we want to collect and contain a group of music21 objects, we put them into a Stream. Streams can also be used for less conventional organizational structures. We frequently will build and pass around short-lived, temporary Streams, since doing this opens up a wide variety of tools for extracting, processing, and manipulating objects on the Stream. For instance, if you are looking at only notes on beat 2 of any measure, you’ll probably want to put them into a Stream as well.

A critical feature of music21’s design that distinguishes it from other music analysis frameworks is that one music21 object can be simultaneously stored (or, more accurately, referenced) in more than one Stream. For examples, we might have numerous Measure Streams contained in a Part Stream. If we extract a region of this Part (using the measures() method), we get a new Stream containing the specified Measures and the contained notes. We have not actually created new notes within these extracted measures; the output Stream simply has references to the same objects. Changes made to Notes in this output Stream will be simultaneously reflected in Notes in the source Part. There is one limitation though: the same object should not appear twice in one hierarchical structure of Streams. For instance, you should not put a note object in both measure 3 and measure 5 of the same piece – it can appear in measure 3 of one piece and measure 5 of another piece. (For instance, if you wanted to track a particular note’s context in an original version of a score and an arrangement). Most users will never need to worry about these details: just know that this feature lets music21 do some things that no other software package can do.

Creating simple Streams

Objects stored in Streams are called elements and must be some type of Music21Object (don’t worry, almost everything in music21 is a Music21Object, such as Note, Chord, TimeSignature, etc.).

(If you want to put an object that’s not a Music21Object in a Stream, put it in an ElementWrapper.)

Streams are similar to Python lists in that they hold individual elements in order. They’re different in that they can only hold music21 objects such as Notes or Clef objects. But they’re a lot smarter and more powerful.

To create a Stream you’ll need to type stream.Stream() and assign it to a variable using the equal sign. Let’s call our Stream stream1:

stream1 = stream.Stream()
Notice that just like how the (capital) Note object lives in a module called (lowercase) note, the (capital) Stream object lives in a module called (lowercase) stream. Variable names, like stream1 can be either uppercase or lowercase, but I tend to use lowercase variable names (or camelCase like we did with noteList).
The most common use of Streams is as places to store Notes. So let’s do just that: we can add the three Note objects we created above by using the append method of Stream:
stream1.append(note1)
stream1.append(note2)
stream1.append(note3)

Of course, this would be a pain to type for hundreds of Notes, so we could also use the Stream method repeatAppend() to add a number of independent, unique copies of the same Note. This creates independent copies (using Python’s copy.deepcopy function) of the supplied object, not references.

stream2 = stream.Stream()
n3 = note.Note('D#5')  # octave values can be included in creation arguments
stream2.repeatAppend(n3, 4)
stream2.show()
../_images/usersGuide_04_stream1_47_0.png

But let’s worry about that later. Going back to our first stream, we can see that it has three notes using the same len() function that we used before:

len(stream1)
 3

Alternatively, we can use the show() method called as show('text') to see what is in the Stream and what its offset is (here 0.0, since we put it at the end of an empty stream).

stream1.show('text')
 {0.0} <music21.note.Note C>
 {2.0} <music21.note.Note F#>
 {3.0} <music21.note.Note B->

If you’ve setup your environment properly, then calling show with the musicxml argument should open up Finale, or Sibelius, or MuseScore or some music notation software and display the notes below.

stream1.show()
../_images/usersGuide_04_stream1_53_0.png

Accessing Streams

We can also dive deeper into streams. Let’s get the step of each Note using the for thisNote in ...: command. But now we’ll use stream1 instead of noteList:

for thisNote in stream1:
    print(thisNote.step)
 C
 F
 B

And we can get the first and the last Note in a Stream by using the [X] form, just like other Python list-like objects:

stream1[0]
 <music21.note.Note C>
stream1[-1].nameWithOctave
 'B-2'

While full list-like functionality of the Stream isn’t there, some additional methods familiar to users of Python lists are also available. The Stream index() method can be used to get the first-encountered index of a supplied object.

note3Index = stream1.index(note3)
note3Index
 2

Given an index, an element from the Stream can be removed with the pop() method.

stream1.pop(note3Index)
stream1.show()
../_images/usersGuide_04_stream1_63_0.png

Since we removed note3 from stream1 with the the pop() method, let’s add note3 back into stream1 so that we can continue with the examples below using stream1 as we originally created it.

stream1.append(note3)
stream1.show()
../_images/usersGuide_04_stream1_65_0.png

Separating out elements by class with .getElementsByClass()

We can also gather elements based on the class (object type) of the element, by offset range, or by specific identifiers attached to the element. Gathering elements from a Stream based on the class of the element provides a way to filter the Stream for desired types of objects. The getElementsByClass() method iterates over a Stream of elements that are instances or subclasses of the provided classes. The example below gathers all Note objects and then all Rest objects. The easiest way to do this is to use for loops with .getElementsByClass():

for thisNote in stream1.getElementsByClass(note.Note):
    print(thisNote, thisNote.offset)
 <music21.note.Note C> 0.0
 <music21.note.Note F#> 2.0
 <music21.note.Note B-> 3.0

If you want instead of passing the class note.Note you could instead pass the string "Note".

for thisNote in stream1.getElementsByClass('Note'):
    print(thisNote, thisNote.offset)
 <music21.note.Note C> 0.0
 <music21.note.Note F#> 2.0
 <music21.note.Note B-> 3.0

It is also possible to pass in a list of classes or strings of class names to .getElementsByClass() which will return anything that matches any of the classes. Notice the [] marks in the next call, indicating that we are creating a list to pass to .getElementsByClass():

for thisNote in stream1.getElementsByClass(['Note', 'Rest']):
    print(thisNote, thisNote.offset)
 <music21.note.Note C> 0.0
 <music21.note.Note F#> 2.0
 <music21.note.Note B-> 3.0

Since there are no note.Rest objects, it’s the same as above. Oh well…

music21 has a couple of shortcuts that are equivalent to .getElementsByClass. For instance .notes is equivalent to .getElementsByClass(['Note', 'Chord']) (we’ll get to chords soon):

for thisNote in stream1.notes:
    print(thisNote)
 <music21.note.Note C>
 <music21.note.Note F#>
 <music21.note.Note B->

And .notesAndRests is equivalent to .getElementsByClass(['Note', 'Chord', 'Rest']).

for thisNote in stream1.notesAndRests:
    print(thisNote)
 <music21.note.Note C>
 <music21.note.Note F#>
 <music21.note.Note B->

Finally, there’s something slightly different. .pitches begins with a call to .notes, but then returns a list of all the pitches from every Note or Chord in the Stream:

listOut = stream1.pitches
listOut
 [<music21.pitch.Pitch C4>,
  <music21.pitch.Pitch F#4>,
  <music21.pitch.Pitch B-2>]

The result of a .getElementsByClass are not technically streams, but you can convert it to a stream with .stream() and then call .show() on it:

sIterator = stream1.getElementsByClass(note.Note)
sOut = sIterator.stream()
sOut.show('text')
 {0.0} <music21.note.Note C>
 {2.0} <music21.note.Note F#>
 {3.0} <music21.note.Note B->

Separating out elements by offset with .getElementsByOffset()

The getElementsByOffset() method returns a Stream of all elements that fall either at a single offset or within a range of two offsets provided as an argument. In both cases a Stream is returned.

sOut = stream1.getElementsByOffset(3)
len(sOut)
 1
sOut[0]
 <music21.note.Note B->

Like with .getElementsByClass() if you want a Stream from .getElementsByOffset(), add .stream() to the end of it.

sOut = stream1.getElementsByOffset(2, 3).stream()
sOut.show('text')
 {2.0} <music21.note.Note F#>
 {3.0} <music21.note.Note B->

We will do more with .getElementsByOffset() later when we also talk about getElementAtOrBefore() and getElementAfterElement()

More Stream Features

Okay, so far we’ve seen that Streams can do the same things as lists, but can they do more? Let’s call the analyze method on stream to get the ambitus (that is, the range from the lowest note to the highest note) of the Notes in the Stream:

stream1.analyze('ambitus')
 <music21.interval.Interval A12>

Let’s take a second to check this. Our lowest note is note3 (B-flat in octave 2) and our highest note is note2 (F-sharp in octave 4). From B-flat to the F-sharp above it, is an augmented fifth. An augmented fifth plus an octave is an augmented twelfth. So we’re doing well so far. (We’ll get to other things we can analyze in chapter 18 and we’ll see what an Interval object can do in chapter 15).

As we mentioned earlier, when placed in a Stream, Notes and other elements also have an offset (stored in .offset) that describes their position from the beginning of the stream. These offset values are also given in quarter-lengths (QLs).

Once a Note is in a Stream, we can ask for the offset of the Notes (or anything else) in it. The offset is the position of a Note relative to the start of the Stream measured in quarter notes. So note1’s offset will be 0.0, since it’s at the start of the Stream:

note1.offset
 0.0

note2’s offset will be 2.0, since note1 is a half note, worth two quarter notes:

note2.offset
 2.0

And note3, which follows the quarter note note2 will be at offset 3.0:

note3.offset
 3.0

(If we made note2 an eighth note, then note3’s offset would be the floating point [decimal] value 2.5. But we didn’t.) So now when we’re looping we can see the offset of each note. Let’s print the note’s offset followed by its name by putting .offset and .name in the same line, separated by a comma:

for thisNote in stream1:
    print(thisNote.offset, thisNote.name)
 0.0 C
 2.0 F#
 3.0 B-

(Digression: It’s probably not too early to learn that a safer form of .offset is .getOffsetBySite(stream1):

note2.offset
 2.0
note2.getOffsetBySite(stream1)
 2.0

What’s the difference? Remember how I said that .offset refers to the number of quarter notes that the Note is from the front of a Stream? Well, eventually you may put the same Note in different places in multiple Streams, so the .getOffsetBySite(X) command is a safer way that specifies exactly which Stream we are talking about. End of digression…)

As a final note about offsets, the lowestOffset property returns the minimum of all offsets for all elements on the Stream.

stream1.lowestOffset
 0.0

So, what else can we do with Streams? Like Note objects, we can show() them in a couple of different ways. Let’s hear these three Notes as a MIDI file:

stream1.show('midi')

Or let’s see them as a score:

stream1.show()
../_images/usersGuide_04_stream1_105_0.png

You might ask why is the piece in common-time (4/4)? This is just the default for new pieces, which is in the defaults module:

defaults.meterNumerator
 4
defaults.meterDenominator
 'quarter'

(Some of these examples use a system that automatically tries to get an appropriate time signature and appropriate clef; in this case, music21 figured out that that low B-flat would be easier to see in bass clef than treble.)

We’ll learn how to switch the TimeSignature soon enough.

If you don’t have MIDI or MusicXML configured yet (we’ll get to it in a second) and you don’t want to have other programs open up, you can show a Stream in text in your editor:

stream1.show('text')
 {0.0} <music21.note.Note C>
 {2.0} <music21.note.Note F#>
 {3.0} <music21.note.Note B->

This display shows the offset for each element (that is, each object in the Stream) along with what class it is, and a little bit more helpful information. The information is the same as what’s called the __repr__ (representation) of the object, which is what you get if you type its variable name at the prompt:

note1
 <music21.note.Note C>

By the way, Streams have a __repr__ as well:

stream1
 <music21.stream.Stream 0x116b27dc0>

that number at the end is the hex form of the .id of the Stream, which is a way of identifying it. Often the .id of a Stream will be the name of the Part (“Violin II”), but if it’s undefined then a somewhat random number is used (actually the location of the Stream in your computer’s memory). We can change the .id of a Stream:

stream1.id = 'some_notes'
stream1
 <music21.stream.Stream some_notes>

We could have also changed the .id of any of our Note objects, but it doesn’t show up in the Note’s __repr__:

note1.id = 'my_favorite_C'
note1
 <music21.note.Note C>

Now, a Stream is a Music21Object just like a Note is. This is why it has an .id attribute and, more importantly, why you can call .show() on it.

What else makes a Music21Object what it is? It has a .duration attribute which stores a Duration object:

stream1.duration
 <music21.duration.Duration 4.0>
stream1.duration.type
 'whole'
stream1.duration.quarterLength
 4.0

(Notice that the len() of a Stream, which stands for “length”, is not the same as the duration. the len() of a Stream is the number of objects stored in it, so len(stream1) is 3).

A related concept to the .duration of a Stream is its .highestTime, which is the time at which the latest element in the Stream ends. Usually this is the last element of the stream’s .offset plus its .quarterLength.

stream1.highestTime
 4.0

Streams within Streams

And, as a Music21Object, a Stream can be placed inside of another Stream object. Let’s create a stream, called biggerStream (for reasons that will become obvious), that holds a Note D# at the beginning

biggerStream = stream.Stream()
note2 = note.Note("D#5")
biggerStream.insert(0, note2)

Now we use the .append functionality to put stream1 at the end of biggerStream:

biggerStream.append(stream1)

Notice that when we call .show('text') on biggerStream, we see not only the presence of note2 and stream1 but also all the contents of stream1 as well:

biggerStream.show('text')
 {0.0} <music21.note.Note D#>
 {1.0} <music21.stream.Stream some_notes>
     {0.0} <music21.note.Note C>
     {2.0} <music21.note.Note F#>
     {3.0} <music21.note.Note B->

Notice though that the offsets, the little numbers inside curly brackets, for the elements of stream1 (“some notes”) relate only to their positions within stream1, not to their position within biggerStream. This is because each Music21Object knows its offset only in relation to its containing Stream, not necessarily to the Stream containing that Stream.

Also notice that note1 knows that it is in stream1 but doesn’t know that it is somewhere inside biggerStream:

note1 in stream1
 True
note1 in biggerStream
 False

All this might not seem like much of a big deal, until we tell you that in music21, Scores are made up of Streams within Streams within Streams. So if you have an orchestral score, it is a Stream, and the viola part is a Stream in that Stream, and measure 5 of the viola part is a Stream within that Stream, and, if there were a ‘’divisi’‘, then each’‘diviso’’ voice would be a Stream within that Stream. Each of these Streams has a special name and its own class (Score, Part, Measure, and Voice), but they are all types of Streams.

So how do we find note1 inside biggerStream? That’s what the next two chapters are about.

Chapter 5 covers Lists of Lists. Those with programming experience who have familiarity with lists of lists and defining functions might want to skip to Chapter 6 Streams of Streams.