An algorithm for dynamically choosing
the best 12 notes from an extended meantone -
Part 1

by David C Keenan, 24-Mar-1995
last updated 1-Sep-2000

http://users.bigpond.net.au/d.keenan

The problem

The 12 note equal tempered scale was a relatively recent compromise solution to a problem; that of having good harmonies while being able to modulate between keys. Modulation became unlimited, but the harmonies suffered, most notably the major and minor thirds (and their inversions the minor and major sixths). For more detail on this, see my Harmonic errors in equal tempered musical scales. We now have the possibility of a better solution, one which involves much less compromise of the harmonies. The solution is, retuning the instrument "on the fly".

The key to understanding the following algorithm is the chain of fifths.

    ... Gbb Dbb Abb Ebb Bbb 
Fb  Cb  Gb  Db  Ab  Eb  Bb 
F   C   G   D   A   E   B 
F#  C#  G#  D#  A#  E#  B# 
F## C## G## D## A## ...

The standard equal tempered scale considers this to be a closed cycle of 12 notes where G# is just another name for Ab etc. The cycle of 31 treats the 31 notes actually listed above as distinct but closes the cycle after 31 so that E## is just another name for Gbb etc. The cycle of 31 has much better thirds and minor thirds and the fifths are only very slightly worse. There are other suitable tunings for this chain of fifths that close after 50 notes or do not close at all. These are all called extended meantones. The algorithm could be used with any of these, but we will assume, below, the cycle of 31, also called 31 tone equal temperament (31-tET).

By choosing a fixed sub-sequence of 12 consecutive notes from the 31 above we can modulate between 6 major keys and 3 minor keys while obtaining much better third and minor third harmonies. This was the meantone system used for keyboard instruments for centuries, until composers (notably Bach) decided that the ability to modulate to widely different keys was more important than having good thirds. The disadvantage of a fixed meantone is that once the boundary of those nine keys is overstepped, the harmonies disappear like stepping off a cliff! When this occurred with the organs of Bach’s day it was referred to as a wolf. For example, choosing the 12 notes Eb thru G# is a good default choice (and the one used traditionally). It allows playing in the keys Bb F C G D A Gm Dm Am. But if for example we try to play a B major triad, we will have an Eb where we need a D# and discord results. Similarly if we try to play an Ab major triad we will get the C and Eb but the Ab will actually be a G#. Again, serious discord.

Llewellyn Lloyd, in his book 'Music and Sound' (1937), said that meantone tuning gives harmonies superior to equal-temperament but the limitation of only 3 minor keys is too restrictive. And all attempts to change the keyboard, to allow for more than 12 notes per octave, have failed and been relegated to museums. He said that meantone tuning will have to wait for the day when one can generate all the required notes from a standard keyboard with no extra effort on the part of the player. The computer was only a glint in John Von Neumann's eye when Lloyd wrote that.

Toward a solution

The basic trick is to slide our choice of 12 notes up or down the sequence of fifths as required. Since the difference between say Eb and D# is quite audible at 39 cents (39/100ths of a semitone) it is very important to avoid the need to retune a note that is already sounding. To this end, the algorithm must be constantly trying to determine (in some sense) the key being used, and the most likely tuning of the notes outside the key, to allow for seamless modulations.

Consider the following algorithm. It might be called the "two down and three up" algorithm because whatever key you are playing in, it maintains a reserve of two keys down and three keys up (by fifths) from the current key. This is not the final algorithm, but I believe it is best to start by explaining this simple precursor.

Number the positions in our chosen sequence of fifths from 0 to 11. Ignore chords for now and consider only melodies, or consider a chord as a very rapid arpeggio. Whenever the note at position 0 or 1 is played, slide the sequence down so that the note becomes position 2 of the new sequence. Likewise if a note at position 9 or 10 is played, slide the sequence up so that the note becomes position 8 of the new sequence. In deference to minor keys, if the note at position 11 is played we should not panic and shift up 3 keys, but only one, so that the note becomes position 10 of the new sequence.

For example, given our default sequence of Eb to G# we can play any of the "white" notes without affecting the tuning. So we say we are centered on the key of C.

C Major
 Db  Ab  Eb  Bb  F   C   G   D   A   E   B   F#  C#  G#  D#  A#
        [0   1 | 2   3   4   5   6   7   8 | 9   10  11]

If we play an F#, the Eb would immediately be lowered 39 cents to become a D# and we would then be centered on the key of G.

G Major
 Db  Ab  Eb  Bb  F   C   G   D   A   E   B   F#  C#  G#  D#  A#
            [0   1 | 2   3   4   5   6   7   8 | 9   10  11]

If instead we play an Eb then the C# and G# will be raised 39 cents to become Db and Ab and we will be centered on the key of Bb.

Bb Major
 Db  Ab  Eb  Bb  F   C   G   D   A   E   B   F#  C#  G#  D#  A#
[0   1 | 2   3   4   5   6   7   8 | 9   10  11]

Perhaps we should only ever move one key up or down, and perhaps, in consideration of minor keys, we should maintain a reserve of only one key down and four up. Note that the "|"s might be moved independently of the "[ ]"s as suggested below. This might be how we recognise that we are in a minor key.

A minor
Db  Ab  Eb  Bb  F   C   G   D   A   E   B   F#  C#  G#  D#  A#
           [0 | 1   2   3   4   5   6   7 | 8   9   10  11]

We will ignore this possibility for now. The full algorithm copes with this sort of thing in a much more general way, not limited only to major and minor modes.

Crunching the numbers

Considered as a numerical algorithm, its input is a note number between 0 and 11 (octave ignored) and its output is possibly a new tuning table with a maximum of 3 notes changed. Its internal state consists only of a number between 0 and 18 representing the central key of the current tuning. The full algorithm effectively records the mode as well as the key.

We shall call the key of C (the default tuning) key 9. The key of F is 8 and so on down by fifths to Bbb which is key 0. The key of G is 10 and so on up to D# which is key 18.

Assume the note numbers are 0 for C, 1 for C#, and so on up to 11 for B. To convert a note number to a number representing its position in the current sequence of 12 fifths:

pos := (note * 7 - key) mod 12

For example if we are at the default tuning (key 9) and we play an Eb (note 3) this is position (3 * 7 - 9) mod 12 = 0. The tonic note, in this case C, is always at position 3. In key 9, note G# is at position (8 * 7 - 9) mod 12 = 11, the last position.

To convert a position back to a note number:

note := (pos + key) * 7 mod 12.

[In mod 12 arithmetic, *7 is the inverse of *7 because 7*7 mod 12 = 49 mod 12 = 1]

No note will ever be offset from the standard 12 tone equal tempered tuning by more than ±50 cents (half a semitone). If we ever managed to get all the way up to G## or down to Dbb there would be a discontinuity as every note would have to be raised or lowered 39 cents. For this reason, whenever there has been no note sounding for a second or two then any key outside of the range 3 to 15 (Gb to F#) should be brought back into that range by adding or subtracting 12 from its key number and subtracting or adding 39 cents to every note. After a longer timeout (e.g. 30 seconds) we may wish to automatically return to the default C tuning.

In pseudo-Pascal the algorithm is:

var
    key: 0..18;
    centsOffset: Array [0..11] of -50..50;
function PosFromNote (note: 0..11; key: 0..18): 0..11;
    PosFromNote := (note * 7 - key) mod 12;
function NoteFromPos (pos: 0..11; key: 0..18): 0..11;
    NoteFromPos := (pos + key) * 7 mod 12;
procedure SetDefaultTuning;
    var
        pos: 0..11;
    begin
    key := 9; { C or Am }
    for pos := 0 to 11 do
        centsOffset[NoteFromPos(pos, key)] := ((6 - pos) * 100 + 15) div 31;
    { The note A has zero offset so we still obey the A = 440Hz tuning standard.
     The largest offsets in the key of C are +13 and -6 cents }
    end;
procedure AcceptNote(note: 0..11);
    var
        pos, n, noteToRetune: 0..11;
    procedure Up12Keys;
        begin
        key := key + 12;
        for n := 0 to 11 do
            centsOffset [n] := centsOffset [n] - 39;
        end;
    procedure Down12Keys;
        begin
        key := key - 12;
        for n := 0 to 11 do
            centsOffset [n] := centsOffset [n] + 39;
        end;
    begin { AcceptNote }
    if longTimeout then SetDefaultTuning
    else if shortTimeout then
        if key < 3 then Up12Keys;
        if key > 15 then Down12Keys;
    pos := PosFromNote(note, key);
    while pos < 2 do
        if key = 0 then Up12Keys;
        key := key - 1;
        noteToRetune := NoteFromPos(11, key);
        centsOffset[noteToRetune] := centsOffset[noteToRetune] + 39;
        pos := pos + 1;
    while pos > 8 do
        if key = 18 then Down12Keys;
        key := key + 1;
        noteToRetune := NoteFromPos(0, key);
        centsOffset[noteToRetune] := centsOffset[noteToRetune] - 39;
        pos := pos - 1;
    end;

A few consequences of this algorithm

Here are the shortest sequences of notes that will take us directly to a particular key from the default C tuning.

Key   Shortest sequences
Gb    Eb,Db,Cb
Db    Eb,Ab,Gb    or    Eb,Db,Gb
Ab    Eb,Db
Eb    Bb,Ab       or    Eb,Ab
Bb    Eb
F     Bb
C
G     F#
D     C#
A     G#
E     F#,D#       or    C#,D#        or    G#, D#
B     C#,A#       or    G#,A#
F#    G#,E#

The shortest sequences aren’t necessarily the easiest to remember. It may be easier just to play the key signature, i.e. all the sharps or flats of the key in their order of introduction. The order is F#,C#,G#,D#,A#,E# or Bb,Eb,Ab,Db,Gb,Cb.

Truly chromatic music is just not going to work. For example attempting to play a chromatic scale up from C will result in the sequence C,C#,D,D#,E,E#,F#,F##,G#,G##,Bb,B,C. Note that every second note causes us to go up two keys until we play the G## which causes us to wrap back to the key of Bb. At least we end up in the key of C again at the end. Playing the scale in reverse would give the totally different result, C,B,Bb,A,Ab,G,Gb,F,Fb,Eb,Ebb,Db,Dbb, leaving us in the key of G.

Conclusion

Part 2 (if I ever get around to writing it) will describe the more complex algorithm which, I hope, overcomes most of the shortcomings of the simple algorithm described above. In the meantime, not for the faint-hearted, here is the full algorithm written in C for an Apple Macintosh, and again in AVR assembler for an Atmel AT90S2313 microcontroller connected to a 5x7 dot LED display and an opto-isolator for MIDI interface. Here's a link to my AVR microcontroller page.