User’s Guide, Chapter 25: Post-Tonal Tools (1)¶
The music21 toolkit features many tools for analyzing and creating music
within a post-tonal context. A Chord
can be
identified as a Forte class, a list of pitch classes can be used to
create a 12-tone matrix, and many other analytical tools are available.
This overview will illustrate key features of music21’s post tonal
tools. For complete documentation on post-tonal tools, see the many
methods in Chord
as well as the objects in
music21.serial, such as
TwelveToneMatrix
and
TwelveToneRow
.
Pitches as Pitch Classes¶
Any music21 Pitch
, or a
Note
containing Pitch, can be expressed as
pitch class integers using the
pitchClass
and
pitchClassString
properties.
In the following example, the parse()
function
is used to create a Score
object. The
id
attribute of each contained
Part
is presented in a list.
We’ll start our look at post-tonal music with a most-decidedly tonal piece, Beethoven’s Opus 59.
from music21 import *
aScore = corpus.parse('beethoven/opus59no2', 3)
[e.id for e in aScore.parts]
['Violin I.', 'Violin II.', 'Viola.', 'Violoncello.']
We can view the fourth and fifth measures of the violin Part by
obtaining the Part from the Stream with
getElementById()
method. Next, we can
extract the desired measures with the
measures()
method. Calling the
show()
method will, assuming correct
setup of your environment, open a display of the extracted measures.
vlnPart = aScore.getElementById('Violin I.')
mRange = vlnPart.measures(4, 7)
mRange.show()
If we want to gather all Pitch
objects from
this measure range, we can use the
pitches
property. This returns
a list of all Pitch objects. All pitch objects have
pitchClass
and
pitchClassString
properties, providing either integer or string representations,
respectively.
', '.join([str(p) for p in mRange.pitches])
'A4, F#4, G4, G4, B4, E5, G5, G5, G5, C#6, E6, E6, E6, G6, C#5'
[p.pitchClass for p in mRange.pitches]
[9, 6, 7, 7, 11, 4, 7, 7, 7, 1, 4, 4, 4, 7, 1]
If we want to label the notes in our measure range with the Note’s pitch
class representation, we can iterate over the notes and assign the pitch
class representation to the Note’s lyric. This is a common way to
annotate Note and Chord objects in music21. The results can be displayed
with the show()
method.
Here is the image from when we were using a Beethoven Quartet as an example:
for n in mRange.recurse().notes:
if n.tie is None or n.tie.type == 'start':
n.lyric = n.pitch.pitchClassString
mRange.show()
Chords as Forte Set Classes¶
Any music21 Chord can be interpreted as a Forte set class. Additional, a wide variety of analytical features, derived from the Forte set class, are available as methods of the chord.
For an example, lets create a sequence of generated aggregate-completing trichords stored on a Stream. That is, we will construct chords with pitch classes, drawing them from a list of all pitch classes. These pitches will be supplied to a Chord object and stored on a Stream.
aStream = stream.Stream()
src = list(range(12)) # cheate a list of integers 0 through 11
src = src[2:4] + src[0:2] + src[8:9] + src[4:8] + src[9:12] # recombine
for i in range(0, 12, 3):
aStream.append(chord.Chord(src[i:i + 3]))
aStream.show()
These Chords, like all Chords in music21, can be interpreted as Forte set classes. The Chord object offers numerous methods that retrieve data from the set class representation of the Chord. The following is just a sampling of some of the many relevant methods.
for c in aStream:
print(c.orderedPitchClassesString)
<023>
<148>
<567>
<9AB>
for c in aStream:
print(c.forteClass)
3-2B
3-11A
3-1
3-1
for c in aStream:
print(c.forteClassTnI)
3-2
3-11
3-1
3-1
for c in aStream:
print(c.normalOrder)
[0, 2, 3]
[1, 4, 8]
[5, 6, 7]
[9, 10, 11]
for c in aStream:
print(c.primeFormString)
<013>
<037>
<012>
<012>
for c in aStream:
print(c.intervalVector)
[1, 1, 1, 0, 0, 0]
[0, 0, 1, 1, 1, 0]
[2, 1, 0, 0, 0, 0]
[2, 1, 0, 0, 0, 0]
To annotate the Chords stored on the Stream with their Forte name, we
can iterate over the Stream and assign the Forte name to each Chord’s
lyric
attribute.
for c in aStream:
c.lyric = c.forteClass
aStream.show()
We can use the itertools.combinations
function to find all possible
subsets of a chord for a given cardinality. (Hat tip to John Chittum for
the hint). Here is the all tri-chord hexachord
which contains within
it all possible three-note chords under inversion and transposition:
import itertools
c = chord.Chord([0, 1, 2, 4, 7, 8])
c.commonName
'all tri-chord hexachord'
Now we’ll test this create a set to hold the Forte number for each trichord and see that all twelve of them are there:
cc = set()
for i in itertools.combinations(c.pitches, 3):
cc.add(chord.Chord(i).forteClassNumber)
cc
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
Creating and Processing Twelve-Tone Matrices¶
The music21 music21.serial module provides a Stream-based representation of a 12-Tone row, as well as the ability to view these rows as a matrix. Additionally, numerous 12-tone rows from works are available as classes.
For example, we can create an instance of the row from Alban Berg’s
Violin Concerto, use the show()
method to display its contents as
text, and then create and print a
TwelveToneMatrix
object.
aRow = serial.getHistoricalRowByName('RowBergViolinConcerto')
aRow.show('text')
{0.0} <music21.note.Note G>
{1.0} <music21.note.Note B->
{2.0} <music21.note.Note D>
{3.0} <music21.note.Note F#>
{4.0} <music21.note.Note A>
{5.0} <music21.note.Note C>
{6.0} <music21.note.Note E>
{7.0} <music21.note.Note G#>
{8.0} <music21.note.Note B>
{9.0} <music21.note.Note C#>
{10.0} <music21.note.Note E->
{11.0} <music21.note.Note F>
aMatrix = aRow.matrix()
print(aMatrix)
0 3 7 B 2 5 9 1 4 6 8 A
9 0 4 8 B 2 6 A 1 3 5 7
5 8 0 4 7 A 2 6 9 B 1 3
1 4 8 0 3 6 A 2 5 7 9 B
A 1 5 9 0 3 7 B 2 4 6 8
7 A 2 6 9 0 4 8 B 1 3 5
3 6 A 2 5 8 0 4 7 9 B 1
B 2 6 A 1 4 8 0 3 5 7 9
8 B 3 7 A 1 5 9 0 2 4 6
6 9 1 5 8 B 3 7 A 0 2 4
4 7 B 3 6 9 1 5 8 A 0 2
2 5 9 1 4 7 B 3 6 8 A 0
We might divide this row into trichords, present each of those trichords
as Chords, and label the resulting pitch classes and Forte set class. As
shown above, we can set the lyric
attribute to assign a single line
of text. If we need to assign multiple lines of text, the Note and Chord
method addLyric()
can be used to add
successive lines.
bStream = stream.Stream()
for i in range(0, 12, 3):
c = chord.Chord(aRow[i:i + 3])
c.addLyric(c.primeFormString)
c.addLyric(c.forteClass)
bStream.append(c)
bStream.show()
This should give a decent introduction to working with post-tonal music – we’ll return to this when we discuss searching for post-tonal structures, but there’s still a lot to find in a Stream, and we want to take it one step at a time – the best way to do so is to understand Chapter 26, Stream Iteration and Filtering