Project 4.04 Piezo Greensleaves
We developed the Arno’s ability for playing music in Project 4.03. In this project, we give it a tune to play.
Concepts: arrays, functions
Circuits:
Concepts: arrays, functions
Circuits:
To play music we need two pieces of information: the notes to play and the duration of each note. There aren’t any shortcuts here. We need to write out each note. The music array contains the notes and the beats array contains the duration of each note (1 beat = a quarter note).
We created a system in Project 4.03 to link the names of each note with its frequency (e.g., C = 16.352). This works within an octave, but our music goes across several octaves. We include that additional information in the music array. The character ‘+’ means to move up an octave and ‘-‘ moves down an octave:
char music[] = { 'E','G','A','B','+','d','-','B','A','F','D' (the array keeps going…)
Since the music array contains elements that aren’t actually notes, we need to make a decision: do we keep the length of the beats array the same as the music array (which makes it easier to read) or do we skip the beats for the elements in music that move up or down an octave (which makes it harder to match the note and beat later)? All of this information can push the limit of the Arnos memory, so we decided to only provide a beat when we play a note, which makes the beats array shorter. We also use the byte variable type to save on space (an int array would take up twice the space since each int value needs two bytes):
byte beats[] = {2,4,2,3, (and so on)
We begin the loop() block by setting the initial octave and setting the variable skips. We use skips to keep track of non-note elements in music so that we can keep the music and beats arrays coordinated:
octave = 4;
skips = 0;
Next, we begin a loop to go through the 83 characters in the music array:
for(int i=0;i<83;i++){
As we read each element of music we need to identify those elements that are not played but change the octave. When we encounter one of these elements, we use the continue statement that tells the sketch to move back to the top of the for loop and read the next element of music. This saves a little time since we don’t run through the rest of the loop. The variable skips keeps track of these changes, too:
//check if octave is changed
if(music[i] == '+'){
octave = octave + 1;
skips++;
continue;
}
if(music[i] == '-'){
octave = octave - 1;
skips++;
continue;
}
If we make it to this point in the loop, then it’s time to play a note. First, we need to look up the frequency of the note. We loop through the notes array until we find a match with the current element of music. Once a match is found, the break statement ends the loop. There’s no need to keep looking and it saves a little time (and time is important in music!):
//otherwise find the note;
for(int k=0;k<12;k++){
if(music[i]==notes[k]){
thisNote = (long) freqs[k]*pow(2,octave);
break;
}
}
Next, we find the beat for the current note. The octave changes mean the the elements of music and beats don’t directly correspond. But we kept track of this with skips. The second line sets the tempo (if you replace 200 with 100, the music will play twice the speed):
thisBeat = (long) beats[i-skips];
thisBeat = thisBeat * 200;
Finally, we call piezoTone to play the note:
piezoTone(thisNote,thisBeat);
After playing the entire piece, we wait a second and then start again:
} //end loop through notes
delay(1000);
We created a system in Project 4.03 to link the names of each note with its frequency (e.g., C = 16.352). This works within an octave, but our music goes across several octaves. We include that additional information in the music array. The character ‘+’ means to move up an octave and ‘-‘ moves down an octave:
char music[] = { 'E','G','A','B','+','d','-','B','A','F','D' (the array keeps going…)
Since the music array contains elements that aren’t actually notes, we need to make a decision: do we keep the length of the beats array the same as the music array (which makes it easier to read) or do we skip the beats for the elements in music that move up or down an octave (which makes it harder to match the note and beat later)? All of this information can push the limit of the Arnos memory, so we decided to only provide a beat when we play a note, which makes the beats array shorter. We also use the byte variable type to save on space (an int array would take up twice the space since each int value needs two bytes):
byte beats[] = {2,4,2,3, (and so on)
We begin the loop() block by setting the initial octave and setting the variable skips. We use skips to keep track of non-note elements in music so that we can keep the music and beats arrays coordinated:
octave = 4;
skips = 0;
Next, we begin a loop to go through the 83 characters in the music array:
for(int i=0;i<83;i++){
As we read each element of music we need to identify those elements that are not played but change the octave. When we encounter one of these elements, we use the continue statement that tells the sketch to move back to the top of the for loop and read the next element of music. This saves a little time since we don’t run through the rest of the loop. The variable skips keeps track of these changes, too:
//check if octave is changed
if(music[i] == '+'){
octave = octave + 1;
skips++;
continue;
}
if(music[i] == '-'){
octave = octave - 1;
skips++;
continue;
}
If we make it to this point in the loop, then it’s time to play a note. First, we need to look up the frequency of the note. We loop through the notes array until we find a match with the current element of music. Once a match is found, the break statement ends the loop. There’s no need to keep looking and it saves a little time (and time is important in music!):
//otherwise find the note;
for(int k=0;k<12;k++){
if(music[i]==notes[k]){
thisNote = (long) freqs[k]*pow(2,octave);
break;
}
}
Next, we find the beat for the current note. The octave changes mean the the elements of music and beats don’t directly correspond. But we kept track of this with skips. The second line sets the tempo (if you replace 200 with 100, the music will play twice the speed):
thisBeat = (long) beats[i-skips];
thisBeat = thisBeat * 200;
Finally, we call piezoTone to play the note:
piezoTone(thisNote,thisBeat);
After playing the entire piece, we wait a second and then start again:
} //end loop through notes
delay(1000);