Analyzing musical pieces on the Tonnetz
using the pitchplots Python library

20. Jahreskongress der Gesellschaft für Musiktheorie (GMTH)

Fabian C. Moss (fabian.moss@epfl.ch) & Martin Rohrmeier

Overview

  1. Background
  2. Installation and usage of pitchplots
  3. Examples

1. Background

  • In musical corpus studies, pieces are often viewed as collections of pitch classes
  • Different goals:
    • key or mode finding
    • composer or style classification
    • tracing historical developments
  • Here: Focus not on results but on their display
  • Feedback loop from visualization to interpretation and analysis

Running example

We use Franz Liszt's "Bénédiction de Dieu dans la Solitude", S. 173 (1847) throughout.

Mediantic key relations in this piece: F$\sharp$ major - D major - B$\flat$ major - F$\sharp$ major

Pitch-class counts of "Bénédiction"

Liszt_profile

"Bénédiction" as tone profile

Liszt_profile

More sophisticated tonal spaces

"Tonnetz" from Hostinský's Die Lehre von den musikalischen Klängen: ein Beitrag zur aesthetischen Begründung der Harmonielehre (1879)

The Neo-Riemannian "Tonnetz"

Cohn, R. (1998). Introduction to Neo-Riemannian Theory: A Survey and Historical Perspective. Journal of Music Theory 42(2), 167-180.

Goal

The pitchplots library was developed to use the spatial representation of the Tonnetz to display the tonal content of pieces without relying on the assumption that the music is fundamentally triadic.

Rather, it gives a more fine-grained overview of tones in pieces by displaying their distribution in a piece or a segment.

3. Installation and usage of pitchplots

The code and many examples how to use the library are hosted on https://github.com/DCMLab/pitchplots.

  1. Install the Python programming language (python.org), preferably using the Anaconda distribution (anaconda.com).
  2. Open a terminal and type pip install pitchplots
  3. That's it!

The library is now accessible in your Python coding environment.

4. Examples

Where do I find the data?

  • Several research projects provide access to symbolic encodings
  • A large community-based resource is the MuseScore platform

musescore.com

It is possible to download scores from this website in the MusicXML format that can be read by pitchplots.

Data transformation

In order to read the file we have to "parse" it into a list of all notes that are in the piece. We load a parser (file converter) that reads the MusicXML file and returns a table of all notes:

from pitchplots.parser import xml_to_csv

We can then call the function on the exported MusicXML file and store it in a variable, e.g. df (for DataFrame)

df = xml_to_csv("data/liszt.mxl")

Let's inspect df.

In [163]:
df.head(10)
Out[163]:
filepath qpm time_sign_num time_sign_den measure_no no_accs mode key_area type note_name tpc step acc octave pitch pitch_class duration onset onset_seconds
3 data/liszt.mxl 70.0002 4 4 1 6 major 0 note E#3 E# E 1.0 3 53.0 5.0 0.166667 0 0
4 data/liszt.mxl 70.0002 4 4 1 6 major 0 note D#3 D# D 1.0 3 51.0 3.0 0.250000 0.333333 1.14285
5 data/liszt.mxl 70.0002 4 4 1 6 major 0 note C#3 C# C 1.0 3 49.0 1.0 0.083333 0.833333 2.85713
10 data/liszt.mxl 70.0002 4 4 2 6 major 0 note F#4 F# F 1.0 4 66.0 6.0 0.125000 1 3.42856
11 data/liszt.mxl 70.0002 4 4 2 6 major 0 note C#5 C# C 1.0 5 73.0 1.0 0.125000 1 3.42856
12 data/liszt.mxl 70.0002 4 4 2 6 major 0 note D#5 D# D 1.0 5 75.0 3.0 0.125000 1.125 3.85713
13 data/liszt.mxl 70.0002 4 4 2 6 major 0 note C#5 C# C 1.0 5 73.0 1.0 0.125000 1.25 4.2857
14 data/liszt.mxl 70.0002 4 4 2 6 major 0 note D#5 D# D 1.0 5 75.0 3.0 0.125000 1.375 4.71427
15 data/liszt.mxl 70.0002 4 4 2 6 major 0 note C#5 C# C 1.0 5 73.0 1.0 0.125000 1.5 5.14284
16 data/liszt.mxl 70.0002 4 4 2 6 major 0 note D#5 D# D 1.0 5 75.0 3.0 0.125000 1.625 5.57141
In [105]:
from pitchplots.static import tonnetz
In [106]:
tonnetz(df)
Out[106]:
In [137]:
tonnetz(
    df,
    figsize=(8,8), 
    fontsize=2, 
    colorbar=False, 
    radius=4, 
    duplicate=True, 
    duration=False, 
    nan_color="white"
)
Out[137]:

Using a dictionary

It is possible to pass a dictionary of keywords to Python functions.

If, for example, the dictionary of keywords is

In [138]:
kws = {
    "figsize" : (8,8),
    "cmap" : "Reds",
    "duration" : True
}

and then calling the plotting function like this

In [ ]:
tonnetz(**kws)

is equivalent to

In [ ]:
tonnetz(figsize=(8,8), cmap="Reds", duration=True)

This is very convenient when one wants to share the keywords between different pieces or sections.

Let's try it out!

In [134]:
kws = {
    "figsize" : (8,8),
    "cmap" : "Reds",
    "duration" : True,
    "colorbar" : False,
    "fontsize" : 1.5,
    "nan_color" : "white",
    "show" : True,
    "radius" : 4,
    "edgecolor" : "black"
}
In [135]:
fig = tonnetz(df, **kws)

Sometimes we do only want to analyze specific sections of a piece. This can be achieved with the measures keyword.

Liszt's piece consists of four large sections with the following keys

  • F$\sharp$ major (mm. 1-179)
  • D major (mm. 180-226)
  • B$\flat$ major (mm. 227-256)
  • and F$\sharp$ major (mm. 257-367)
In [136]:
fig = tonnetz(df, **kws, measures=[1,179], center="F#")
fig.savefig("img/liszt_1-179.png")
In [130]:
fig = tonnetz(df, **kws, measures=[180,226], center="D")
plt.savefig("img/liszt_180-226.png")
In [131]:
fig = tonnetz(df, **kws, measures=[227,256], center="Bb")
plt.savefig("img/liszt_227-256.png")

Compare pieces and different styles

Lieck, R., Moss, F. C., & Rohrmeier, M. (in press) The Tonal Diffusion Model. Transactions of the International Society for Music Information Retrieval.

Summary

  • Music visualization can be an aid for music analysis
  • Provides a "fingerprint" of the harmonic makeup of a piece or a section
  • Can inspire further analytical inquiry
  • Interpretation of plots crucially depends on music-theoretical framework
  • Looks nice in publications
  • Important: The library is in constant development. If you have feedback and suggestions, let me know!

Thank you very much!