Music014/102 Lab2: Tonality and Rhythm

Prof. Michael Casey

Dartmouth College


In [1]:
# import libraries that we will use
from pylab import * 
from bregman.suite import *
import tonality as ton # The Well-Tempered Clavier Tools
from pylab import rcParams
%matplotlib inline
rcParams['figure.figsize'] = 14, 8


Warning: music21 not installed, only loading .ascii files supported

In [3]:
# %cd /Path/to/WTCexample, for example:
# %cd /home/mkc/src/BregmanToolkit/bregman/examples/WTCexamples

In [4]:
help(ton)


Help on module tonality:

NAME
    tonality - tonality.py - Matrix representations of musical scores, corpara, and their tonality

FILE
    /Users/alexandrarieger/WTCexamples/tonality.py

DESCRIPTION
    Example: J. S. Bach's "Well Tempered Clavier" Books 1 and 2
    
    2015, Michael A. Casey, Dartmouth College, Bregman Media Labs
    
    License: 
    Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
    http://creativecommons.org/licenses/by-nc/4.0/

FUNCTIONS
    center_mtx(D)
        Given a dissimilarity or dissonance matrix, center the matrix by subtracting the mean of 
        the rows and columns. For a dissimilarity matrix this operation yields the "scatter matrix".
    
    dissimilarity_mtx(A)
        Given a piano-roll indicator matrix, construct self-dissimilarity matrix
    
    dissonance_fun(A)
        Given a piano-roll indicator matrix representation of a musical work (128 pitches x beats),
        return the dissonance as a function of beats.
        Input:
            A  - 128 x beats indicator matrix of MIDI pitch number
    
    dissonance_mtx(A)
        Given a piano-roll indicator matrix, construct pair-wise dissonance matrix
    
    extract_audio_chroma(flist, nSecs=10, nSamps=6)
        Given a list of WAV files, extract chromagram features and stack
        Options:
            nSecs - how many seconds each sample lasts
            nSamps - how many samples to take from each file
    
    fold_mtx(a)
        Fold piano-roll matrix into single octave beginning with 'C'.
    
    hist_mtx(mtx, tstr='')
        Given a piano-roll matrix, 128 MIDI piches x beats, plot the pitch class histogram
    
    load_corpus(corpus=None, idx=None, win_len=1, sample_len=0)
        Load items from a corpus, use given idx slice argument to select subsets
        Inputs:
             corpus - list of symbolic music files (xml, mid, krn, etc...) 
                idx - slice argument giving range of works [None] (all)
            win_len - num tactus beats to integrate [1] (no integration)
         sample_len - number of sampled windows per work [0] (all)
    
    load_wtc(idx=None, win_len=1, sample_len=0)
        Load scores in matrix form in the entire WTC dataset.
        Inputs:
                idx - slice argument giving range of works [None] (all)
            win_len - num tactus beats to integrate [1] (no integration)
         sample_len - number of sampled windows per work [0] (all)
    
    plot_mtx(mtx=None, title=None, newfig=False, cbar=True, **kwargs)
        ::
        
            static method for plotting a matrix as a time-frequency distribution (audio features)
    
    scale_mtx(M, normalize=False, dbscale=False, norm=False, bels=False)
        ::
        
            Perform mutually-orthogonal scaling operations, otherwise return identity:
              normalize [False]
              dbscale  [False]
              norm      [False]
    
    win_mtx(a, win_len=2)
        Options:
            win_len  - window length [2]

DATA
    pc_labels = array(['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G'..., 'G', ...



In [77]:
execfile('tonality.py') # Calling the module as a script runs a demo

In this lab you will:

1. Analyze music using both audio features and symbolic (score-based) features
2. Analyze a musical corpus by sampling from multiple works
3. Extract the latent geometry in tonal music 

Part A

First, let's load a single piece of music: the Prelude in C Major from J. S. Bach's Well Tempered Clavier Book 1


In [7]:
prelude1 = loadtxt('01.ascii') # Load the score in matrix form, 128 MIDI notes x time (tactus)

In [13]:
ton.plot_mtx(prelude1, 'Prelude No. 1 in C Major')


Dissonance Overlay Plot


In [14]:
# Overlay dissonance plot, scaled to range of pitch values
ton.plot_mtx(prelude1, 'Prelude No. 1 in C Major') 
d = ton.dissonance_fun(prelude1)
plot(d*30,'r')
title('Dissonance',fontsize=16)
ax = axis('tight')


tonality.py:226: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future.
  if amps == None: amps = [1]*len(freqs)

Pitch Usage Histogram


In [15]:
# Summarize the work as a histogram, showing only range of active pitches
ton.hist_mtx(prelude1, 'J. S. Bach, Prelude No. 1 in C Major')


Dissimilarity Matrix


In [16]:
prelude1.shape


Out[16]:
(128, 560)

In [17]:
x2 = prelude1[:,2]
x3 = prelude1[:,3]
print sqrt(sum((x3 - x2)**2))


1.41421356237

In [18]:
prelude1b = ton.fold_mtx(prelude1)
D = ton.dissimilarity_mtx(prelude1b)
imshow(D)
colorbar()
title('Dissimilarity Matrix: WTC 1: Prelude No. 1 in C Major')
xlabel('Tactus', fontsize=14); ylabel('Tactus', fontsize=14)


Out[18]:
<matplotlib.text.Text at 0x109f53050>

Part B: Working with a Corpus

So far we have considered only one work at a time. Here we will investigate working with a corpus of works: the Well Tempered Clavier (WTC) Books 1 and 2 by J. S. Bach

WTC is organized into two books with 48 works each: a prelude and a fugue for every major and minor key in pitch class order: ['C','C#','D',...].

The WTC folder contains MIDI piano roll matrix representations of each work.

BOOK 1:

  • 1.ascii = Prelude No. 1 in C Major
  • 2.ascii = Fugue No. 1 in C Major
  • 3.ascii = Prelude No. 2 in C Minor
  • 4.ascii = Fugue No. 2 in C Minor
  • 5.ascii = Prelude No. 3 in C# Major
  • 6.ascii = Fugue No. 3 in C# Major
  • 7.ascii = Prelude No. 4 in C# Minor
  • 8.ascii = Fugue No. 4 in C# Minor
  • ...
  • 47.ascii - Prelude No. 24 in B Minor
  • 48.ascii = Fugue No. 24 in B Minor

BOOK 2:

  • 49.ascii = Prelude No. 25 in C Major
  • 59.ascii = Fugue No. 25 in C Major
  • ...
  • 95.ascii = Prelude No. 48 in B Minor
  • 96.ascii = Fugue No. 48 in B Minor

Loading and Displaying Multiple Works


In [19]:
# Learn about the load_wtc function
help(ton.load_wtc)


Help on function load_wtc in module tonality:

load_wtc(idx=None, win_len=1, sample_len=0)
    Load scores in matrix form in the entire WTC dataset.
    Inputs:
            idx - slice argument giving range of works [None] (all)
        win_len - num tactus beats to integrate [1] (no integration)
     sample_len - number of sampled windows per work [0] (all)


In [20]:
# Load the first two works in WTC
A = ton.load_wtc(slice(0,10,2)) # sample_len=0
A = hstack(A) # Stack (concatenate) all works into a single matrix
ton.plot_mtx(A, 'WTC Preludes 1-5')



In [21]:
# Load the first five Fugues in WTC
A = ton.load_wtc(slice(1,10,2)) # sample_len=0
A = hstack(A) # Stack (concatenate)
ton.plot_mtx(A, 'WTC Fugues 1-5')



In [22]:
# Load the 24 Preludes in WTC Book 1
A = ton.load_wtc(slice(0,48,2)) # sample_len=0
A = hstack(A) # Stack (concatenate) all works into a single matrix
#A = ton.scale_mtx(A.T, norm=True).T
ton.plot_mtx(A, 'WTC Preludes 1-24')
print "Matrix Size is:", A.shape


Matrix Size is: (128, 21671)

In [23]:
# Make a plot of pitch usage in all Preludes in book 1
ton.hist_mtx(ton.fold_mtx(A), 'WTC Preludes 1-24')


QUESTION: What do you conclude about pitch usage in Fugues 1-24 in Book 1 of WTC?

WRITE YOUR ANSWER HERE

Finding Implicit Geometry in Tonality


In [24]:
# Loading whole works results in very large matrices.
# We need to restrict the size of matrices to have <=5000 time points
# We have two choices:
#  1) Average Adjacent Tactus into pitch profiles spanning Beats, Bars or Phrases
#  2) Randomly sample subsets of Beats, Bars, or Phrases in each work
A = ton.load_wtc(win_len=16, sample_len=20) # Integrate 16 adjacent frames into beat-level
A = hstack(A) # Stack (concatenate) all works into a single matrix
ton.plot_mtx(A)


Making a Corpus-Level Dissimilarity Matrix


In [25]:
# Now that we have fewer time-points due to integration, we can form the pair-wise dissimilarity matrix
D = ton.dissimilarity_mtx(A)
figure(figsize=(12,12))
imshow(D**2)
colorbar()


Out[25]:
<matplotlib.colorbar.Colorbar instance at 0x1068d4b90>

Eigen Pitches

Principal Component Analysis (PCA) using the Singular Value Decomposition (SVD)


In [26]:
# Center the Dissimilarity Matrix
B = ton.center_mtx(D**2) # Use squared dissimilarity

# Singular Value Decomposition (Eigenvector Analysis)
u,s,v = svd(B) # 
figure(figsize=(12,8))
stem(arange(32),s[:32])
grid()



In [27]:
# Now make a plot of each 'beat' projected onto the first two eigenvector dimensions
figure(figsize=(10,10))
plot(u[:,0], u[:,1],'.') # u are the projection of the data onto the principal axes 
grid()


QUESTION: What do you see. What do you think this represents ?

3D Plot of projection to first three eigenvectors

What shape do you see below? You can interact with the window to expore the 3D space


In [28]:
# Setup matplotlib for external window (deafult mode)
%matplotlib
rcParams['figure.figsize'] = 12, 12
plot3(u[:,0],u[:,1],u[:,2],linestyle='none',marker='.')
title('J. S. Bach, Well Tempered Clavier: 96 Works, first 3 Principal Components')


Using matplotlib backend: MacOSX
Out[28]:
<matplotlib.text.Text at 0x107294b50>

In [29]:
# Reset matplotlib for inline drawing
%matplotlib inline
rcParams['figure.figsize'] = 14, 8

In [29]:


In [52]:


In [ ]: