Let's start with a really nasty scale on the piano. Here's a C minor blues scale. It's like the C minor penatonic but with an added "blue note". This means a mix of lots of black and white notes. Super confusing!
You can see that there are six notes in the scale, and they repeat over and over on each octave of the keyboard. We're currently describing one octave as twelve intervals in binary. This is basically a byte and a half. So we can store two octaves over three bytes. The third repeating octave is simply a repeat of the first and half of the second byte, and so on.
So our twelve-interval representation of the blues scale is
And a two octave span for the C minor blues scale is
And splitting this up into bytes, we get
10010111 00101001 01110010
Where our scale is split over the middle byte:
[10010111 0010][1001 01110010]
If we wanted to play this same scale in D, we simply bit-shift the entire pattern two places to the right:
00100101 11001010 01011100 10
and we're now spanning four bytes.
However, it's worth noting that the final byte is simply the first two notes of the scale as it repeats.
If we continued the sequence, we'd get
00100101 11001010 01011100 10100101 11001010 10100101 11001010
So other than the very first byte, our fourth, seventh, tenth (and so on) bytes are all the same. If truth be told, our first byte should be the same too. We pushed everything up two places, to start on the D instead of the C and simply padded the gaps with "blank" notes. But if we played down the scale instead of upwards, some of these blanks would be over notes in the scale. So there's no reason why we can't "merge" the first and fourth bytes together, to complete the scale in both directions.
Simply put, as we push our scale "out of" the third byte and into the fourth, we can then bitwise OR the first and fourth bytes together, to "fill in the blanks".
In musical notation, you can "push" a scale six places up (this is often referred to as making the notes "sharp") or push a scale down up to six places (referred to as "flattening" the notes). Once you push the notes more than six places in any direction, because of the repeating nature of the notes in a scale (or on a keyboard) you'll find it's the same as pushing them from the other direction. So, for example, you can sharpen all the notes 8 places, but this is the same as flattening all the notes 4 places.
To keep things simple, we're just going to "push upwards" from a basic C scale for everything. So all our scales and chords will be based around C, and if we want to transpose them into another key, simply push them up the appropriate number of spaces, then use the repeating nature to fill in the blanks immediately before the root note.
Here are the binary patterns we'll start with (padded to 16 bits by repeating):
C Major chord (C-E-G)
1000100100001000 = 0x89 0x08
C Minor chord (C-Eb- G)
1001000100001001 = 0x91 0x09
C Major 7th chord (C-E-G-B)
1000100100011000 = 0x89 0x18
C Minor 7th chord (C-Eb-G-B)
1001000100011001 = 0x91 0x19
1010110101011010 = 0xAD 0x5A
C Minor scale (C-D-Eb-F-G-G#-Bb)
1011010110101011 = 0xB5 0xAB
C Minor pentatonic (C-Eb-F-G-Bb)
1001010100101001 = 0x95 0x29
C Minor blues scale (C-Eb-F-F#-G-Bb)
1001011100101001 = 0x97 0x29
There are probably a million and one other scales we could include, but for most pop, rock and blues type songs, these will probably get us through (maybe in future we might include 6th, 9th and 11th versions of some chords, but we've plenty to be getting started with here!). Once we've got these working, adding more chords/scales is as simple as converting into a two byte value and adding them to a look-up table.
So how do we make use of these? At the minute all we can do is play a load of stuff in C!
Well, we simply bit-shift (we're going to always bitshift to the right) a certain number of places to start the pattern on the correct (root) note, then fill in the blanks we've created immediately before the root note.
Let's say we want to play an E Major chord.
To get to E from C, we need to raise all the notes 4 places (from C to C#, from C# to D, from D to Eb, from Eb to E). So let's just take our C Major chord and bit-shift the pattern four places to the right
10001001 00001000 00000000 - CMaj
00001000 10010000 10000000 - EMaj
We created a third byte, to "catch" the last four bits of the last byte in the (repeating) chord
If we translate these bytes back into notes, we can see:
So far, our crazy bit-shifting seems to be hold up.
Let's go crazy and really push those bits around. Let's see if we can't make a B Major chord, using exactly the same technique:
To get from C to B, we need to raise all of the notes in our major chord by 11 intervals/steps. Doing this in binary gets us:
10001001 00001000 00000000 - CMaj
00000000 00010001 00100001 - BMaj
And mapping these back to our keyboard diagram:
So now let's try applying the same technique to our "blues scale".
We're looking to repeat the scale over the entire keyboard, so we'll take the 3-byte repeating pattern of a C minor blues scale, from the top of this article.
Let's turn this into a G minor pentatonic by shifting the entire pattern 7 steps to the right.
If we split this pattern up into bytes, we get
00000001 00101110 01010010 11100100
But we've already discussed how the fourth byte is a repetition of the first.
So we should bit-wise OR the first and fourth bytes together:
So our final pattern looks like this:
10100101 00101010 01010010
And mapping this pattern back to the piano keyboard, we get this:
The paler notes in the diagram above show the "additional" notes added, when we merged the fourth and first bytes together. Despite performing a simple bitwise operation, the "missing" notes do fit in with the rest of the scale.
Having demonstrated that the theory holds up, it's time to get coding......