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 , with another one of base frequency
, and another one of base frequency
. If we perform a Plomp-Levelt calculation on such a sound, we will find the consonance value
of a pure major chord at frequency
. We can do the same to find the consonance value
of a pure minor chord at frequency
. 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 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
, while for a pure major chord on Eb, I will consider the subset
.
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
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?
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.