Sunday, February 16, 2014

Plotting pitches and durations continuously in music21

With music21, it's not hard to plot discrete data (pitches, durations, etc.) as continuous data. There isn't a built in tool for doing this, but since music21 is written in Python, it is easy to take advantage of the tools from matplotlib, numpy, and scipy to create "cubic bezier-curve splines" that show these points in an easily visualized format.

In music21 you can easily plot the position of notes as a piano roll:

from music21 import corpus

bach = corpus.parse('bwv66.6')

bach.plot('pianoroll')



which preserves pitch names, measure numbers, etc.  But the case we're asking for requires a plot more like this:




















The numbers at the left are midi numbers while the bottom is number of quarter notes from the beginning. Here's some code to help you achieve this:

import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
from music21 import corpus

bach = corpus.parse('bwv66.6')

fig = plt.figure()

for i in range(len(bach.parts)):
    top = bach.parts[i].flat.notes
    y = [n.ps for n in top]
    x = [n.offset + n.quarterLength/2.0 for n in top]

    tck = interpolate.splrep(x,y,s=0)
    xnew = np.arange(0,max(x),0.01)
    ynew = interpolate.splev(xnew,tck,der=0)
    
    subplot = fig.add_subplot(111) # semi-arbitrary three-digit number
    subplot.plot(xnew,ynew)
plt.title('Bach motion')

plt.show()

With this sort of graph it's easy to isolate each voice (not much overlap of voices in this chorale) and to see the preponderance of similar motion among the Soprano, Alto, and Tenor, but lack of coordination with the Bass (which would create forbidden parallels if it coordinated).  More sophisticated examples with better labels are easily created by those with knowledge of matplotlib, but this simple demonstration will suffice to get things started.