Todo
This is completely in draft mode now and is at best in placeholder status. No revisions please.
Music in the western classical tradition uses a twelve-tone chromatic scale. Any of the tones in this scale can be the basis of a major scale. Most musicians (especially pianists) learn the C-major scale in the early days of study, owing to the ability to play this scale entirely with the ivory (white) keys.
The following declaration shows how to initialize an array consisting of the twelve tones of the chromatic scale, starting from the C note.
1 2 | static string[] tones = { "C", "C#", "D", "D#", "E", "F",
"F#", "G", "G#", "A", "A#", "B" };
|
Even if you’re not a musician, learning the basic principles is fairly straightforward.
The well-known C-major scale, which is often sung as:
Do Re Mi Fa So La Ti Do
has the following progression:
C D E F G A B C
This progression is known as the diatonic major scale. If you look at the tones array, you can actually figure out the intervals associated with this array:
C + 2 = D
D + 2 = E
E + 1 = F
F + 2 = G
G + 2 = A
A + 2 = B
B + 1 = C
So given any starting note, the major scale can be generated from the intervals (represented as an array).
So, for example, if you want the F-major scale, you can get it by starting at F and applying the steps of 2, 2, 1, 2, 2, 2, 1:
F + 2 = G
G + 2 = A
A + 1 = B' (flat) a.k.a. A#)
B'+ 2 = C
C + 2 = D
D + 2 = E
E + 1 = F
So this is the F-major scale:
F G A B' C D E F
We begin by creating a helper function, FindTone(), which does a linear search to find the key of the scale we want to compute. The aim is to make it easy for the user to just specify the key of interest. Then we can use this position to compute the scale given the major (or minor, covered shortly) interval array.
1 2 3 4 5 6 7 | static int FindTone(string key) {
for (int i=0; i < tones.GetLength(0); i++) {
if (key == tones[i])
return i;
}
return -1;
}
|
To see what this function does, pick your favorite key (C and G are very common for beginners).
For example, C is the first note in the array of tones, so FindTone("C") would give us 0. FindTone("F") would give us 6.
So let’s take a look at ComputeScale() which does the work of computing a scale, given a key and an array of steps. The scale array is allocated by the Main() method, primarily to allow the same array to be used repeatedly for calculating other scales.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | static void ComputeScale(string key, int[] steps, int[] scale) {
int tonePosition = 0;
int startTone;
startTone = FindTone(key);
if (startTone < 0)
return;
if (steps.GetLength(0)+1 != scale.GetLength(0))
return;
tonePosition = startTone;
for (int i=0; i < steps.GetLength(0); i++) {
scale[i] = tonePosition % tones.GetLength(0);
tonePosition += steps[i];
}
}
|
The following function writes the scale out (rather naively) by just printing the notes from our existing tones array.
1 2 3 4 5 6 | static void WriteScale(int[] scale) {
foreach (int i in scale) {
Console.Write ("{0} ", tones[i]);
}
Console.WriteLine ();
}
|
We say that the output is naive because any musician will tell you that a scale should be printed in a normalized way. For example, the F-major scale (shown above in our earlier explanation) is never written with A# as one of its notes. It is written as B-flat. It’s easy to manage the various cases by consulting the circle of fifths, which gives us guidance on the number of flats/sharps each scale has. We’ll revisit this topic again later during our discussion of the OOP version.
Lastly, we put this all together.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static void Main (string[] args)
{
int[] scale = new int[8];
int[] major = { 2, 2, 1, 2, 2, 2, 1 };
int[] minor = { 2, 1, 2, 2, 1, 2, 2 };
string name = args[0];
Console.WriteLine("{0} major scale", name);
ComputeScale(name, major, scale);
WriteScale(scale);
Console.WriteLine("{0} minor scale", name);
ComputeScale(name, minor, scale);
WriteScale(scale);
}
|
This Main() method shows how to set up the steps for both major and minor scales. We’ve already explained how to express the steps of a major scale. The minor scale basically drops the 3rd and 7th by a semitone (a single step), which gives us a different pattern.
You can run this program to see the major and minor scales. We plan on doing a bit more with this example later when we have the power of classes and objects, which can be greatly helpful for organizing the other major ideas of music besides scales. For example, we may wish to express a song using tablature and perform a transposition to a different scale. For this and many other more advanced ideas, having classes and objects is a must.