In this lesson, you’ll replace parts of the sketch for the theremin so that it now produces notes that fall on a musical scale. You can reuse much of the sketch and keep the same setup on your breadboard.
Let’s take a look at the programming elements you’ll need to play notes on a scale. You can find the complete sketch in the Exercise Files for this lesson.
First, you need an array that contains the frequencies of the notes you want to play.
//A Minor Pentatonic
int scale[] = {
147, 165, 196, 220, 262, 294, 330, 392, 440,
523, 587, 659, 784, 880, 1047, 1175, 1319, 1568,
1760, 2093, 2349
};
In the array above, you’ll find notes that belong to the A minor pentatonic scale. These are the notes A – C – D – E – G in different octaves. As a reminder: on GitHub, you can find a more or less complete list of all notes and their frequencies.
Instead of the A minor pentatonic, you can of course also use a C major scale. It would look like this:
//C Major Scale
int scale[] = {
131, 147, 165, 175, 196, 220, 247, 262, 294,
330, 349, 392, 440, 494, 523, 587, 659, 698,
784, 880, 988, 1047
};
___STEADY_PAYWALL___
These arrays contain a fixed number of notes: the first array has a length of 18 and the second has 22 values. You’ll need the number of values later in the map()
function, which you already know from the previous theremin. However, what if you want to add more notes? In this case, you would also have to adjust the length of the array in other parts of the sketch.
It’s more elegant if you let your program determine the length of the array itself. For this, you only need a variable and a for loop:
int lengthOfScale = 0;
for (byte i = 0; i < (sizeof(scale) / sizeof(scale[0])); i++) {
lengthOfScale += 1;
}
In the variable, you store the length of the array. In the for loop, you increase the value in this variable by one in each iteration – as long as the counter i hasn’t reached the end of the array. But where is the end? You determine this with sizeof()
. This command determines the number of bytes in the array. Divided by the number of bytes of the first value of the array (with index 0), this gives you the total number of values in the array.
Now you have the number of notes in your scale. You need this now in the map()
function. Here you map the distance of your hand to a note or frequency again. This time, the range is limited from the first note in the array scale[0]
to the last scale[lengthOfScale - 1]
. Note: You subtract one from the variable lengthOfScale
because arrays start with index 0.
note = map(distance, 100, distanceHigh, scale[0], scale[lengthOfScale - 1]);
The variable note
now contains a frequency – which, however, doesn’t necessarily correspond to a correct note. For your theremin to play the right notes, you need one last query.
for (byte j = 0; j < (lengthOfScale); j++) {
if (note == scale[j]) {
tone(piezo, note);
break;
}
else if (note > scale[j] && note < scale[j + 1]) {
note = scale[j];
tone(piezo, note);
break;
}
}
This for loop runs over every value in your array, so as long as j < (lengthOfScale)
is true. In each iteration, you first check if the value in the variable note
happens to exactly match a note in the array. If that’s the case, you play this note with the tone()
function and exit the loop with break
.
But if that’s not the case, your played note obviously lies between two notes in the array: note > scale[j] && note < scale[j + 1]
Here, you then play the note that is one below the value in the variable note
– so the next lower correct note. Too complicated? An example: Your hand has a distance to the sensor that you have converted to a frequency of 200 Hertz using the map()
function. But this is not a note of the A minor pentatonic scale, but is somewhere between a G and a G sharp. According to your array, the next lower, correct note is a G with 196 Hertz. And that’s what your theremin will play. In this way, your theremin will always play a note that is stored in your array.
And that’s it. Upload the sketch to your Arduino and start playing. 🙂