Sunday, January 5, 2025

Key signatures: non-enharmonic versus enharmonic

In musical common practice, using the equal tempered chromatic scale (12-ET), there are 15 possible key signatures. They can be represented as a signed integer ranging from -7 to 7, indicating the key's number of sharps or flats, with positive values for sharps and negative values for flats. This scheme is shown below:

 7 = C#
 …
 2 = D
 1 = G
 0 = C
-1 = F
-2 = Bb
 …
-7 = Cb

This is also the scheme used by standard MIDI files. However, in certain contexts, such as jazz or atonal / twelve-tone music, it can be useful to represent key signature enharmonically, as a number ranging from 0 to 11, like so:

C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B

The non-enharmonic scheme does not map cleanly to the enharmonic scheme, as the former has 15 states whereas the latter has only 12 states. It’s necessary to remove three keys, and in the above example, the keys F sharp, F flat, and C flat are lost. In other words, if we convert from non-enharmonic to enharmonic, and then convert back to non-enharmonic, for those three keys, we will get a different value than the one we started with: F sharp will become G flat, F flat will become E, and C flat will become B.

The function to convert from non-enharmonic to enharmonic is fairly straightforward:

x = Wrap(y * 7, 12)

Where Wrap is a custom modulo function that handles negatives specially, so that there’s no discontinuity for negative inputs.

int Wrap(int nVal, int nModulo)
{
 nVal %= nModulo;
 if (nVal < 0)
  nVal += nModulo;
 return nVal;
}

For example, take -5 as the input (five flats).

-5 * 7 = -35
-35 % 12 = -11

The result of the % operation is negative, so the Wrap function function adds the modulo to the result:

-11 + 12 = 1

In enharmonic notation, 1 = Db, which is the correct answer: the key with five flats is indeed D flat. The inverse transformation, from enharmonic back to non-enharmonic, is less straightforward. It is this function:

x = (y & 1) ? y - 6 : ((y < 6) ? y : y - 12);

Though the most efficient implementation may be the following lookup table:

int arrAccidentals[12] = {
 0, -5, 2, -3, 4, -1, -6, 1, -4, 3, -2, 5
};

Key signatures: non-enharmonic versus enharmonic

In musical common practice, using the equal tempered chromatic scale (12-ET), there are 15 possible key signatures. They can be represented ...