# Consonance calculations (2): tunings

It’s time to combine two of the previous posts, namely consonance calculations and tunings. The idea is that through consonance calculations, we can immediately get the consonance of any chord (and in particular the major and minor ones) for a given tuning. For that, we are going to use the framework of Plomp and Levelt that we already introduced. Now, this theory has been criticized recently for not correctly taking into account the interaction between three or more sinusoids, as it primarily based on dyads. Nevertheless, it gives reasonable predictions for major and minor chords, on which we will concentrate for tunings.

Remember that the pure fifth corresponds to a ratio of 3/2, the pure major third to a ratio of 5/4, and the pure minor third to a ratio of 6/5. So, the pure major chord would be obtained by combining a harmonic sound having a base frequency of $f_0$, with another one of base frequency $5f_0/4$, and another one of base frequency $6f_0/5$. If we perform a Plomp-Levelt calculation on such a sound, we will find the consonance value $c_{\text{pure major}}$ of a pure major chord at frequency $f_0$. We can do the same to find the consonance value $c_{\text{pure minor}}$ of a pure minor chord at frequency $f_0$. In the rest of this post, I chose a harmonic sound with 6 partials of identical intensity, but you may choose differently.

Now for a given tuning, say the Pythagorean one,

 C C# D Eb E F F# G G# A Bb B 0 114 204 294 408 498 612 702 816 906 996 1110

we are going to calculate the difference $c-c_{\text{pure major/minor}}$ between the dissonance value of a major or minor chord and that of its pure counterpart, for any degree of the scale. Since thedissonance value depends on the base frequency, we compensate by always calculating at the same base frequency. For example, for a pure major chord on C, I will consider the subset $\{0, 408, 702\}$, while for a pure major chord on Eb, I will consider the subset $\{0, 702-294, 996-294\}$.

I considered 96 different tunings, extracted from two excellent books:

• Mathématiques des systèmes acoustiques, F. Jedrzejewski, L’Harmattan
• Tuning and Temperament: A Historical Survey, J. Murray Barbour, Dover Books on Music

The result of these calculations is presented on the following graph:

Obviously, the consonance values for the equal temperament are all the same. For the temperaments we studied previously (Pythagorean, 1/4-Meantone), we can clearly see where are the chords affected by a wolf interval, with a very high dissonance value. For the 1/4-Meantone temperament, and for many others, we can see that some chords are actually more consonant than in equal temperament (which was expected of course).

Edit (October 12th, 2015): I realized that, in addition to the above visualization, it is also interesting to compare all of these systems with the equal temperament. So, for each row of the table, I substracted the first row. The relative dissonance values can now be either positive (a more dissonant chord as compared to the same chord in equal temperament), or negative (a more consonant chord as compared to the same chord in equal temperament). Here is a heatmap of this new table (I have clipped the values to -0.20/0.20. Some systems can actually give much higher or lower values, but differences as small as 0.05 can already be heard (see the Pythagorean tuning)).

Just as a quick and dirty experiment, one can consider the 24 values calculated in the first table as the features of each tuning, and then perform PCA to cluster all of them. This is the global result in two dimensions:

And zoomed versions in the big left cluster:

Remember that this is just a table-top experiment: the distance used in the PCA is the euclidean one, which is not very adequate for this problem (I suspect that one should take instead the min of the distance between cyclically permutated columns, as one can build the tuning on any note). Nevertheless, we can see those temperaments and tunings which clearly stand out such as the Pythagorean one, or the Rousseau or Marpurg I ones. And on the other hand, we have this big cluster of temperaments which gravitate around the equal temperament.

And that will be all for today. Of course, any comment is welcome on this topic !

Edit

At the request below, here is the Python code for doing all these calculations. Now, I should warn you, this code is really dirty ! And non-Pythonic on top of that… I just wrote it very rapidly one afternoon, and didn’t want to spend too much time on it. Feel free to modify it as you want (with a little credit ?). Also, you will need the table of tunings which follows.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA

#### This is the Plomp & Levelt consonance formula for two sinusoids of frequencies f1 and f2 of the same loudness

def plomp(f1,f2):
fmin=min(f1,f2)
fmax=max(f1,f2)
s=0.24/(0.021*fmin+19.)
return (np.exp(-3.5*s*(fmax-fmin))-np.exp(-5.75*s*(fmax-fmin)))

#### Given a matrix as input, where the rows represent the different sounds,
#### and the columns represent the frequencies of the spectrum of each sound,
#### this function calculate the total consonance value

def plompSpectrum(spectrum):
theShape=spectrum.shape
nSpectr=theShape[0]
nFreq=theShape[1]

c=0.0
for i in range(nSpectr):
for j in range(i+1,nSpectr):
for k in range(nFreq):
for l in range(nFreq):
c=c+plomp(spectrum[i][k],spectrum[j][l])
return c

#### Given a column matrix of 3 frequency ratio as input, plus the basis frequency
#### this function returns the harmonic spectrum matrix with n harmonics

def spectrumFromFundamentals(tablF,f,n):
spectrum=np.zeros((3,n))

for i in range(3):
for j in range(n):
spectrum[i,j]=tablF[i]*f*(j+1)
return spectrum

#### Given a tuning as a 12 columns vector matrix
#### this function returns a 24 columns vector matrix containing the frequency ratios covering 2 octaves

def scaleFrequenciesFromTuning(tuning):
frequencies=np.zeros((24,1))

for i in range(12):
frequencies[i]=np.power(2.0,tuning[i]/1200.0)
for i in range(12,24):
frequencies[i]=frequencies[i-12]*2.0

return frequencies

#### Given a tuning as a 12 columns vector matrix
#### this function returns the 3 fundamental frequency ratios corresponding to a major chord on degree 'index'

def majorFundamentalsFromTuning(tuning,index):
tableF=np.zeros((3,1))
frequencies=scaleFrequenciesFromTuning(tuning)

tableF[0]=frequencies[index]/frequencies[index]
tableF[1]=frequencies[index+4]/frequencies[index]
tableF[2]=frequencies[index+7]/frequencies[index]

return tableF

#### Given a tuning as a 12 columns vector matrix
#### this function returns the 3 fundamental frequency ratios corresponding to a minor chord on degree 'index'

def minorFundamentalsFromTuning(tuning,index):
tableF=np.zeros((3,1))
frequencies=scaleFrequenciesFromTuning(tuning)

tableF[0]=frequencies[index]/frequencies[index]
tableF[1]=frequencies[index+3]/frequencies[index]
tableF[2]=frequencies[index+7]/frequencies[index]

return tableF

###### MAIN ######

chordNames=['$C_M$','$C\sharp_M$','$D_M$','$Eb_M$','$E_M$','$F_M$','$F\sharp_M$','$G_M$','$G\sharp_M$','$A_M$','$Bb_M$','$B_M$','$C_m$','$C\sharp_m$','$D_m$','$Eb_m$','$E_m$','$F_m$','$F\sharp_m$','$G_m$','$G\sharp_m$','$A_m$','$Bb_m$','$B_m$']

## Reading file

theFile = open('tuning.txt','r')
Ntunings=int(theFile.readline())
print Ntunings
tuningNames=[]
tunings=[]

for i in range(Ntunings):
tuningNames.append(theFile.readline())
tunings.append([float(x) for x in theFile.readline().split(",")])

## Calculating the consonance of each chord

pureReferenceMajor=plompSpectrum(spectrumFromFundamentals([1.,1.25,1.5],260.,6))
consonanceTableMajor=np.array([[plompSpectrum(spectrumFromFundamentals(majorFundamentalsFromTuning(theTuning,i),260.,6))-pureReferenceMajor for i in range(12)] for theTuning in tunings])
pureReferenceMinor=plompSpectrum(spectrumFromFundamentals([1.,1.2,1.5],260.,6))
consonanceTableMinor=np.array([[plompSpectrum(spectrumFromFundamentals(minorFundamentalsFromTuning(theTuning,i),260.,6))-pureReferenceMinor for i in range(12)] for theTuning in tunings])

consonanceTable = np.concatenate((consonanceTableMajor,consonanceTableMinor),1)

## Displaying

theFig1 = plt.figure()
plt.imshow(consonanceTable,interpolation='none',aspect='auto')
plt.yticks(range(96),tuningNames,fontsize=10)
plt.xticks(range(24),chordNames,fontsize=8)

model = PCA(n_components=2)
projections = model.fit_transform(consonanceTable)
plt.figure()
plt.plot(projections[:,0],projections[:,1],'ob')
for i in range(len(tuningNames)):
plt.text(euh[i,0],euh[i,1],tuningNames[i])
plt.xlabel("PCA Component 1")
plt.ylabel("PCA Component 2")
plt.show()

96
Equal
0,100,200,300,400,500,600,700,800,900,1000,1100
Meantone
0,76,193,310,386,503,579,697,773,890,1007,1083
Pythagorean
0,114,204,294,408,498,612,702,816,906,996,1110
Kepler I
0,92,204,316,386,498,590,702,794,906,1018,1110
Kepler II
0,92,204,316,386,498,590,702,814,906,1018,1110
Werckmeister III
0,90,192,294,390,498,588,696,792,888,996,1092
Werckmeister IV
0,82,196,294,392,498,588,694,784,890,1004,1086
Werckmeister V
0,96,204,300,396,504,600,702,792,900,1002,1098
Werckmeister VI
0,90,187,297,394,498,595,699,792,892,999,1096
Kirnberger I
0,90,187,297,394,498,595,699,792,892,999,1096
Kirnberger II
0,90,204,294,386,498,590,702,792,895,996,1088
Kirnberger III
0,90,193,294,386,498,590,697,792,890,996,1088
Bendeler I
0,90,188,294,392,498,588,694,792,890,996,1094
Bendeler II
0,90,196,294,392,498,596,694,792,890,996,1094
Bendeler III
0,96,192,294,396,498,594,696,798,894,996,1094
Neidhart I
0,94,196,296,392,498,592,698,796,894,996,1092
Neidhart II
0,94,196,298,394,500,596,698,796,894,1000,1096
Neidhart III
0,96,196,298,394,498,596,698,796,894,998,1096
Neidhart V
0,102,204,298,400,502,604,698,800,902,1004,1098
Neidhart VII
0,96,198,300,396,498,600,696,798,900,996,1098
Neidhart VIII
0,100,200,298,402,502,600,700,800,898,1002,1102
Neidhart IX
0,100,196,300,400,496,600,700,796,900,1000,1096
Neidhart X
0,94,194,298,400,494,596,696,800,892,996,1098
Neidhart XI
0,96,196,298,394,500,596,698,796,894,1000,1096
Neidhart XII
0,98,196,300,400,498,596,700,800,898,996,1100
Neidhart XIII
0,94,198,298,392,498,596,696,796,894,996,1094
Neidhart XIV
0,96,198,296,394,500,598,700,800,894,996,1098
Neidhart XV
0,100,198,300,396,498,600,700,798,900,996,1098
Neidhart XVI
0,94,198,296,390,498,592,700,794,894,998,1092
Neidhart XVII
0,96,196,298,394,500,596,698,796,894,1000,1096
Neidhart XVIII
0,96,196,296,394,500,598,698,796,896,1002,1092
Neidhart XIX
0,96,196,296,396,498,596,698,796,894,1000,1094
Neidhart XX
0,100,200,300,398,502,598,700,800,900,1000,1098
Neidhart XXI
0,90,194,294,386,496,590,698,792,890,994,1088
Neidhart XXII
0,92,196,296,388,498,592,698,794,892,996,1090
Sorge I
0,90,187,297,394,498,595,699,792,892,999,1096
Sorge II
0,90,187,297,394,498,595,699,792,892,999,1096
Marpurg A
0,102,200,300,402,500,602,700,802,902,1000,1102
Marpurg B
0,98,198,298,400,500,600,698,798,898,1000,1100
Marpurg C
0,98,200,300,400,498,600,700,800,898,1000,1100
Marpurg D
0,98,198,300,398,498,600,698,798,900,998,1098
Marpurg E
0,100,202,302,402,502,602,700,800,900,1000,1100
Marpurg F
0,102,200,302,400,502,600,702,800,902,1000,1102
Marpurg G
0,100,199,299,398,498,602,702,802,901,1001,1100
Marpurg H
0,96,198,300,396,498,600,696,798,900,996,1098
Marpurg I
0,106,204,302,400,502,604,702,800,906,1004,1102
Marpurg 1
0,71,204,316,386,498,590,702,773,884,1018,1088
Marpurg 3
0,71,204,316,386,498,590,702,773,906,996,1088
Marpurg 4
0,71,182,316,386,498,569,702,773,884,1018,1088
von Wiese I
0,102,204,306,408,498,600,702,804,906,1008,1110
von Wiese II
0,102,204,306,396,498,600,702,804,894,1008,1098
von Wiese III
0,90,192,294,396,498,588,702,792,894,996,1098
von Wiese IV
0,90,204,294,408,498,600,702,792,906,996,1110
von Wiese V
0,90,204,294,386,498,590,702,792,895,996,1088
von Wiese VI
0,102,204,306,408,498,600,702,804,906,996,1110
von Wiese VII
0,90,192,294,396,498,588,690,792,894,996,1086
Dowland
0,108,204,284,388,498,597,702,810,906,986,1090
Malcolm I
0,112,204,316,386,498,590,702,814,884,996,1088
Malcolm II
0,105,204,298,386,498,603,702,796,884,989,1088
Young I
0,94,196,298,392,500,592,698,796,894,1000,1092
Young II
0,90,196,294,392,498,588,698,792,894,996,1090
Stanhope
0,91,197,295,386,498,589,702,793,892,996,1088
Herschel I
0,91,204,295,386,498,589,702,793,884,997,1087
Herschel II
0,90,182,294,386,498,589,702,792,884,996,1088
Fogliano I
0,71,182,316,386,498,569,702,773,884,996,1088
Fogliano II
0,71,204,316,386,498,569,702,773,884,1018,1088
Fogliano III
0,71,193,316,386,498,569,702,773,884,1007,1088
Ganassi
0,89,182,281,386,498,597,702,791,884,983,1088
Colonna I
0,71,182,287,386,498,569,702,734,884,989,1088
Colonna II
0,71,204,316,386,498,583,702,814,884,1018,1049
Ricatti
0,91,196,302,393,502,591,698,791,894,1002,1091
Martini
0,97,193,290,386,503,600,697,793,890,986,1083
Vallotti
0,94,196,298,392,502,592,698,796,894,1000,1090
Barca
0,96,197,298,393,502,594,698,798,895,1000,1092
Schiassi
0,92,204,296,386,498,590,702,794,884,996,1088
Sievers
0,90,195,294,389,498,588,697,792,892,996,1086
Ramos de Pareja
0,92,182,294,386,498,590,702,792,884,996,1088
Bermudo
0,103,200,294,401,498,601,702,792,899,996,1099
Varella
0,81,204,305,386,498,585,702,793,884,1001,1088
Arnault de Zwolle
0,90,204,294,408,498,588,702,792,906,996,1110
Salomon de Caus
0,71,182,275,386,498,569,702,773,884,996,1088
Mersenne
0,71,204,275,386,498,569,702,773,884,996,1088
Sauveur
0,71,204,316,386,498,590,702,814,884,1018,1088
Vincent
0,76,193,281,386,494,579,697,773,890,987,1083
Rameau I
0,76,193,283,386,496,579,697,774,890,991,1083
Rameau II
0,90,193,303,386,503,581,697,798,890,1007,1083
Montvallon
0,92,204,316,386,498,590,702,794,884,996,1088
Gallimard I
0,84,193,297,386,503,582,697,789,890,1007,1083
Gallimard II
0,81,193,293,386,503,581,697,784,890,1007,1083
DAlembert
0,87,193,291,386,500,587,697,787,890,995,1086
Corrette
0,76,193,289,386,503,579,697,783,890,996,1083
Romieu
0,87,196,306,393,502,589,698,785,894,1004,1091
Bethisy
0,76,193,289,386,503,579,697,783,890,996,1083
Rousseau
0,71,204,316,386,498,569,702,814,884,1018,1088
Dom Bedos
0,71,182,316,386,498,569,702,773,884,1018,1088
Mercadier
0,76,193,286,386,498,579,697,775,890,993,1083


## 2 comments

1. Super interesting! Can you elaborate a bit on the colours in the first picture? Also, can you make the data files you used for this picture available?

2. Hi ! Thanks for your comment. The colours in the first picture correspond to the difference between the dissonance value calculated for each chord, and the dissonance value for a pure chord. This difference will always be positive, as the pure chord is supposed to be the most consonant. These calculations are derived from the Plomp and Levelt calculations, which I have talked about previously. The values themselves do not mean much, and the colorbar is adjusted by the imshow() function from matplotlib.

I just added the Python code I used to calculate this. I’m not very proud of it, but it does the job.