Saturday, 14 February 2015

Creating repeating musical scales in binary

Inbetween soldering and gluing, we've been adding a bit to the firmware for our light-up MIDI keyboard. We're looking to make a generic system for adding scales and chords using simple three-byte patterns. While we're already well into the actual hardware build, it's time to add a bit more to our music-theory-to-binary idea.

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

100101110010

And a two octave span for the C minor blues scale is

100101110010100101110010

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

C Major scale (C-D-E-F-G-A-B)
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.

100101110010100101110010

Let's turn this into a G minor pentatonic by shifting the entire pattern 7 steps to the right.

0000000100101110010100101110010

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:

00000001
OR
11100100
========
11100101

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......