Tuning and Temperament (4): Tempering the cycle of fifths

We have seen that 12 justly tuned fifths will overshoot 7 octaves, so in order for them to match up, we need to narrow at least one of those fifths. Pythagorean temperament narrows just one fifth and keeps all the others pure; equal temperament narrows them all by the same amount.

But what if we narrow just some of the fifths? Could we obtain a system with the key range of equal temperament, and some of the precision of the home key of Pythagorean temperament?

Vallotti temperament

Francesco Antonio Vallotti (1697 – 1780) was a composer, organist and theorist who developed a number of different tuning systems. One of the most popular narrows six fifths and keeps the rest pure. If we start our cycle at C, then Vallotti has the fifths

D, A, E, B, F#, C#, G#

all flattened by one sixth of a Pythagorean comma.

Here’s how we might generate tuning of a scale tuned by Vallotti’s temperament, in Python. First define the scale, the Pythagorean comma, and a perfect fifth, using the “rat2cents” function developed earlier:

[In ]: scale = ['C','C#','D','Eb','E','F','F#','G','G#','A','Bb','B']
[In ]: pcomma = rat2cents(1.5**12/2.0**7)
[In ]: perf5th = rat2cents(1.5)

Now we turn our scale into a cycle of fifth, and generate the sequence of Vallotti fifths:

[In ]: val5ths = [perf5th]*12
[In ]: val5ths[2:8] -= pcomma/6

To turn these tempered fifths back into a scale, recall that if

f_0,f_1,f_2,\ldots f_{11},f_{12}

are the fifths, then the scale can be obtained by adding enough fifths to get from C to the required note. Since the function

x\to (7x)\bmod{12}

turns a cycle of fifths into a scale and back again, the values of the scale s_i can be obtained as

    \[ s_i=\sum_{k=0}^{(7i)\bmod 12}f_k. \]

[In ]: valscale = [sum(val5ths[:(7*i)%12])%1200 for i in range(12)]+[1200]
[In ]: for v in valscale:
            print round(x,2)

0.0
94.13
203.91
294.13
400.0
498.04
596.09
701.96
792.18
901.96
996.09
1098.04
1200.0

We see that note values are quite close to equal temperament. To see if this closeness provides good thirds, fourths, and fifths, we can determine the differences between the just intonation values.

[In ]: just = [rat2cents(x) for x in [6.0/5, 5.0/4, 4.0/3, 3.0/2]]
[In ]: D = [3,4,5,7]
[In ]: intervals = numpy.zeros((12,4))
[In ]: for i in range(12):
  ...:     intervals[i,:] = [round((valscale[(i+x)%12]-valscale[i])%1200,2) for x in D]
  ...:

So here just is a list of the cent values of the justly tuned intervals, and D is the number of semitones in each interval. The array intervals determines the cents in each interval with x semitones by the standard method of determining the value of

    \[ \mbox{cents}((i+x)\bmod{12})-\mbox{cents}(i)\bmod{1200} \]

over all values i=0,1,2,\ldots,11. Determining the differences now is straightforward:

In : diffs = np.zeros((12,4))
In : for i in range(4):
...:     diffs[:,i] = [round(x,2) for x in inv[:,i]-just[i]]
...:     
In : print diffs

[[-21.51  13.69   0.     0.  ]
 [ -9.78  17.6    3.91  -3.91]
 [-21.51   5.87   0.    -3.91]
 [-13.69  21.51  -0.     0.  ]
 [-13.69   5.87   3.91  -3.91]
 [-21.51  17.6   -0.    -0.  ]
 [ -9.78  13.69   3.91  -3.91]
 [-21.51   9.78   0.     0.  ]
 [ -9.78  21.51   3.91   0.  ]
 [-17.6    5.87   3.91  -3.91]
 [-17.6   21.51  -0.     0.  ]
 [ -9.78   9.78   3.91  -3.91]]

We see immediately that the fifths (last column) and fourths (third column) are very close to pure, but the thirds are mostly way off, with the minor thirds (first column) very flat, and the major thirds (second column) very sharp.

Similar to the Vallotti temperament is the second temperament of Thomas Young (1773 – 1829), which also has six consecutive fifths all tempered by one sixth of a Pythagorean comma. Young tempered the first six fifths, rather than Vallotti who left the first two fifths pure. Young’s temperament has the same disadvantages (poor thirds) as Vallotti’s.

Before we go exploring other temperaments, it will be convenient to have a program to call on to produce all the output we need. For example:

def intervals(fifths,rounding=True):
    """
    The parameter 'fifths' is a list or numpy vector consisting of a circle
    of fifths.
    The output is a dictionary indexed by:
    
    'S': the corresponding scale
    'I': the minor thirds, major thirds, fourths, and fifths
    'A': all intervals (except unison and octave)
    'D': differences between I and their justly tuned values

    The parameter 'rounding' may be set to False, in which case all outputs are
    given in double precision.  The default is to produce outputs rounded to
    two decimal places
    """
    scale = [sum(fifths[:(7*i)%12])%1200 for i in range(12)]+[1200]
    semitones = [3,4,5,7]
    just = [rat2cents(x) for x in [6.0/5,5.0/4,4.0/3,3.0/2]]
    just_ratios = \
    [0,16.0/15,9.0/8,6.0/5,5.0/4,4.0/3,45.0/32,3.0/2,8.0/5,5.0/3,\
    16.0/9,15.0/8,2.0]
    alljust = [rat2cents(x) for x in just_ratios]
    intvls = np.zeros((12,4))
    for i in range(12):
        intvls[i,:] = [(scale[(i+x)%12]-scale[i])%1200 for x in semitones]
    diffs = np.zeros((12,4))
    for i in range(4):
        diffs[:,i] = intvls[:,i]-just[i]
    all_intervals = np.zeros((12,11))
    for i in range(12):
        all_intervals[i,:] = [(scale[(i+x)%12]-scale[i])%1200 for x in range(1,12)]
    if rounding:
        scale = [round(x,2) for x in scale]
        for i in range(12):
            intvls[i,:] = [round(x,2) for x in intvls[i,:]]
            all_intervals[i,:] = [round(x,2) for x in all_intervals[i,:]]
            diffs[i,:] = [round(x,2) for x in diffs[i,:]]
    return {'S':scale, 'I':intvls, 'A':all_intervals, 'D':diffs}

This means we can play with any tempered sequence of fifths we like, and check on the resulting intervals.

A couple of Werckmeisters

Andreas Werckmeister (1645 – 1706) developed a number of different temperaments. One kept eight of the fifths pure, and tempered the others by one quarter of a Pythagorean comma. The fifths to be tempered were C – G – D – A and B – F#. Given our program above, this temperament can be easily examined:

[In ]: wkm = [perf5th]*12
[In ]: wkm[:3] -= pcomma/4
[In ]: wkm[5] -= pcomma/4
[In ]: temp = intervals(wkm)
[In ]: print temp['D']

[[-21.51   3.91   0.    -5.87]
 [-15.64  21.51  -0.     0.  ]
 [ -9.78   9.78   5.87  -5.87]
 [-21.51  15.64  -0.     0.  ]
 [ -9.78  15.64   0.    -0.  ]
 [-21.51   3.91  -0.    -0.  ]
 [-15.64  21.51   5.87   0.  ]
 [-15.64   9.78   5.87  -5.87]
 [-15.64  21.51  -0.     0.  ]
 [ -3.91  15.64   5.87  -0.  ]
 [-21.51   9.78  -0.     0.  ]
 [-15.64  15.64   0.    -5.87]]

As with Vallotti, the minor thirds are flat and the major thirds sharp.

Another Werckmeister temperament flattens the fifths by these fractions of a Pythagorean comma:

    \[ \left[-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{8},\,-\frac{1}{12},\,0,\,0,\,0,\,-\frac{1}{8}\right] \]

So:

[In ]: wkm2 = [perf5th]*12
[In ]: wkm2[:7] -= pcomma/8
[In ]: wkm2[7] -= pcomma/12
[In ]: wkm2[11] -= pcomma/8
[In ]: out = intervals(wkm2)
[In ]: print out['D']

[[-20.53   9.78   0.98  -2.93]
 [-12.71  19.55   2.93  -1.96]
 [-14.66   9.78   2.93  -2.93]
 [-16.62  17.6   -0.     0.  ]
 [-12.71  10.75   2.93  -2.93]
 [-21.51  11.73  -0.    -0.98]
 [-12.71  16.62   2.93  -2.93]
 [-17.6    9.78   2.93  -2.93]
 [-13.69  20.53   1.96   0.  ]
 [-12.71   9.78   2.93  -2.93]
 [-19.55  14.66  -0.     0.  ]
 [-12.71  13.69   2.93  -2.93]]

Again, although the fourths and fifths are all quite close to pure, the minor thirds again are flat, and major thirds sharp.

There are plenty of other temperaments which have been published and used over the past few hundred years, and many of them, such as these, sacrifice purity of thirds for purity (or at least closeness) of fourths and fifths.

Find the best temperament

Suppose we temper six of the fifths by one-sixth of a Pythagorean comma. Which group of temper fifths will provide the best thirds? We can measure “best” by the smallest sum of squares of the differences between the major thirds and the just value. And this can be done by iterating over all possible combinations of 6 ones and 6 zeros:

[In ]: import itertools
[In ]: for x in list(it.combinations(range(12),6)):
  ...:     v = [0]*12
  ...:     for i in x:
  ...:         v[i] = 1
  ...:     L += [v]
  ...:
[In ]: mn = 10000
[In ]: z = [0]*12
[In ]: for x in L:
  ...:     tmp = [perf5th -i*pcomma/6 for i in x]
  ...:     out = sd.intervals(tmp)
  ...:     outd = out['D'][:,1]
  ...:     sq = sum([y**2 for y in outd])
  ...:     if sq < mn:
  ...:         z = x
  ...:         mn = sq
  ...: print z
  ...:        
[1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0]

This would seem to indicate that, in terms of least squares of distances, the temperament where alternate pairs of fifths are tempered would give good results. So let’s see:

[In ]: ts = [1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0]
[In ]: temp = [perf5th - i*pcomma/6 for i in ts]
[In ]: out = intervals(temp)
[In ]: print out['D']

[[-17.6   13.69   0.    -3.91]
 [-13.69  13.69  -0.     0.  ]
 [-13.69  13.69   3.91   0.  ]
 [-17.6   13.69   3.91  -3.91]
 [-17.6   13.69   0.    -3.91]
 [-13.69  13.69  -0.    -0.  ]
 [-13.69  13.69   3.91   0.  ]
 [-17.6   13.69   3.91  -3.91]
 [-17.6   13.69  -0.    -3.91]
 [-13.69  13.69   0.    -0.  ]
 [-13.69  13.69   3.91   0.  ]
 [-17.6   13.69   3.91  -3.91]]

The major thirds are false by exactly the same amount as in equal temperament; all the minor thirds are flat, but in general not as badly as in the temperaments discussed above.

I have no idea what this temperament is called, but no doubt it’s been written about, discussed and examined along with all the others.

Leave a Reply

Your email address will not be published. Required fields are marked *