⚖️Lesson 7 - Arrays and Scales

How can we play different musical notes with the oscillator?

Overview

This lesson continues building a mouse controlled theremin which plays specific pitches by dividing the canvas into sections with each section triggering a different pitch. This lesson will incorporate the sound element to this project by using arrays as a data structure to hold the notes in the scale that the instrument will play.

Lesson Objectives

Students will be able to:

  • Create an array to store and access different values.

  • Use midiToFreq( ) function to pass MIDI note values as arguments to play specific musical notes

  • Access the number of elements in an array as a value which can be stored in a variable and used in their program

Suggested Duration

1 period (45 minutes)

NYS Standards

9-12.CT.7 Design or remix a program that utilizes a data structure to maintain changes to related pieces of data.

9-12.DL.1 Type proficiently on a keyboard.

9-12.DL.2 Communicate and work collaboratively with others using digital tools to support individual learning and contribute to the learning of others.

Vocabulary

Scale - Any set of notes ordered by pitch, going from lowest to highest

Pentatonic - A scale which contains 5 different notes.

Octave - The interval between one musical pitch and another with double or half its frequency. In Western notation, notes that are an octave apart will have the same note name, ie 'C', but different sounding pitches, one being higher and the other lower.

Array - an ordered series of data

Index - a position within an array

MIDI (Musical Instrument Digital Interface) - A widely used standard for interconnecting electronic musical instruments and computers. MIDI notes are referred to as numbers.

Planning Notes

This lesson picks up from the previous lesson which has divided the canvas into sections (rectangles) using a for loop and highlighting each separate rectangle when the mouse is within the sides of a specific rectangle. These rectangles will be used to designate different pitches that can be played by our theremin.

The pitches played in this project will be stored in an array. Students are expected to have some experience with this data structure, specifically the syntax needed to create an array containing multiple elements and how to refer to a specific element in the array by using the index of that element. If you feel your students may require some review on these concepts, try to plan for some extra time to review, either before going into this lesson or when these concepts come up during the lesson.

This lesson does include some musical concepts and vocabulary. It is not necessary for the students (or teacher) to have more than a surface level understanding of these concepts in order to get their projects to work. Also keep in mind that you might have students in your class who do have a musical background and this could be an opportunity to allow them to take the lead on trying to explain these concepts to the rest of the class. Since the assessments for this lesson are based on the coding concepts, you do not need to be concerned about how well the musical concepts are taught or needing to assess how well students understand them. Just allowing students to take on the teaching role to share their knowledge with their peers can be beneficial to the overall learning environment.

Note: The Pentatonic Scale reference list provide is nowhere near a comprehensive list. There can be several instances where a scale with one name can also have a different name in another country. It is also worth noting that different places in the world use different types of instruments, different ways of tuning and playing these instruments, different systems for naming notes and even different notes themselves. This is way beyond the scope of this lesson but it is important for students to be aware that there is not one "right" way to make music nor is one way better than another.

To get a sense of the scope of how diverse the use of pentatonic scales can be, you could have students take a look at the Wikipedia page entry.

Materials

Pentatonic Scale Reference sheet

Resources

ICM Lesson - Intro to Arrays

MIDI Notes/Frequency Conversion Chart

midiToFreq( ) p5.js reference

Assessments

Formative Assessment -

  • Check for student understanding of how to access specific values of an array within a for loop and conditional statement.

  • Explain how the argument passed to the .freq( ) method affects its output to demonstrate why our code may work but does not behave in the way we want it to by

New Code in this lesson

//Converts MIDI note 60 into a frequency in hertz
let note = midiToFreq(60);


let a = [3, 4, 1, 8, 10];
console.log(a.length); // prints 5

Do Now/Warm Up

Have students open their Mouse Instrument sketch from last class. The code from the last class did not include any sound, so students should not have an oscillator object yet. Instruct students to create an oscillator object in their projects. This is a good opportunity to review the steps to creating an instance of an object or check to see if students still remember how to do this.

Students should also include the ability for the user to start and stop the oscillator with the start( ) and stop( ) methods for the oscillator used inside the mousePressed( ) and mouseReleased( ) functions respectively. Students may already be familiar with implementing this feature as it was covered in Lesson 5. If this is not something students have experience with, the code is provided here:

function mousePressed(){
    osc.start();
}

function mouseReleased(){
    osc.stop();
}

Keep in mind that this code assumes the variable used for the osciilator object is called osc. Remind students they need to include the name for their oscillator object if it is not the same as this example.

Scales are Data Structures

In music, a scale is any set of notes ordered by pitch, going from lowest to highest. To think of it in computer science terms, a scale is just individual values (notes) in a specific order that need to stored together in a group (scale). Since we are working with scales for this project, the best way to organize the notes in our scale is by using a data structure, specifically an array.

When making music using scales, musicians will choose specific notes from the scale to play. If we are storing our notes in an array, this means that we need to be able to access specific values from our array. This is a good opportunity to review the index system of referring to and accessing values from an array.

There are many different types of scales throughout the world. One of the differences in scales can depend on the amount of notes. For example, the Major scale, which some students may be familiar with, has 7 different notes. To start this project, we will be looking at a type of scale called Pentatonic scales. These are scales that have 5 notes (Penta == five). We are using these scales for a few reasons: It is one of the least amount of notes you can have in a scale and it can make the math easier if we stick with nice, round numbers in our project. It is also a common type of scale used in many different types of music from many different countries throughout the world. Students will be provided with a list of different pentatonic scales used throughout the world and are certainly encouraged to choose a scale that might have some cultural significance to them.

Introduction to MIDI Notes

The reference sheet includes musical notes using their letter names, C E G etc. (Note - The system of using letters as notes is not universal) However, p5.js will not be able to recognize these letters as notes that can be used to make specific pitches. We will need to convert these into a format called MIDI notes. MIDI stands for Musical Instrument Digital Interface. MIDI is a standardize communication protocol used by a variety of electronic device to play, edit and record music. MIDI information is essentially just numeric values that correspond to different musical characteristics (volume, pitch, duration and many other expressive elements). A computer can interpret this information and convert it into audio output.

We will be using numerical values to represent pitches in the musical scales that we want to play with our instrument. These will be referred to as MIDI notes. Each musical note has a corresponding numeric MIDI note that we can use to tell our program what notes we want to play. We also need to be aware that even though there are only 12 musical note names (A, A#/Bb, B, C, C#/Db, D, D#/Eb, E, F, F#/Gb, G, G#/Ab) Some notes have two names, for example A# (sharp) and Bb (flat) but the sounding note would be the same. This is called enharmonic. You can also have two notes with the same name that have different pitches. This is referred to in music as an octave. For example, you could play the note 'C' in two different places on the piano keyboard. One would have a higher pitch than the other. We would say they are different octaves. When looking to convert musical notes to MIDI notes, the musical note provided will usually be followed by a number which indicates its octave - C2, C3, C4.

Understanding all of this is not necessary to make this project work, but it can provide useful context for what is happening as well as make the vocabulary used make more sense.

Creating an Array with MIDI Notes

Have students choose a pentatonic scale from the list provided. Students will need to convert the musical notes into MIDI notes using the chart provided. The scale list will not specify which octave to use (for example C3 or C4) Students can decide which octave they want to use but should keep in mind that numbers too high or low may be difficult/irritating to hear.

Example: Hirojoshi Scale

Musical Notes: C, E, F#, G, B

MIDI Notes: 60, 64, 66, 67, 71

Once they have converted their scale to MIDI notes, they can create an array to hold these values.

At the top of the sketch, declare a variable that will store the array and then assign the values to the array. Remember the syntax for an array is to use square brackets [ ] at the beginning and end. Each element in the array should be separated with a comma.

let notes = [60, 64, 66, 67, 71];

Accessing Array Elements for Frequency Values

Students will now take the values from this array and use them as arguments to pass to the Oscillator object. Each note in the array will correspond to one of the rectangles in the divided canvas. Explain to students that just as we are able to highlight a specific rectangle by using the conditional statement inside of the for loop, we can use this same method to assign a specific note to play when we are over a specific rectangle.

Have students write the Oscillator object frequency method inside of the conditional statement which is inside of a for loop. They do not need to provide an argument for this method yet.

for(let i = 0; i < numRects; i++) {
    fill('white');
    if(mouseX > i*rectWidth && mouseX < i*rectWidth + rectWidth){
        fill('red');
        osc.freq( ); // oscillator frequency object w/o argument
    }
    rect(i * rectWidth, 0, i * rectWidth + rectWidth, height);
}

As an argument for this method, students will put the name of their array followed by square brackets with i inside.

osc.freq(notes[i]);

While students should be familiar with using this syntax to access an element in an array, it would be beneficial to walk through what is happening with this single line of code in the context of the for loop, the conditional statement and even within the draw loop.

Note: The degree to which you want to go through this part of the lesson can be determined by the ability level of your class. You could also use this as an assessment opportunity/check for understanding by having students provide their own explanation for what is happening:

  • We have a for loop which is looping the number of time that the numRects variable value is (in this case: 5) and executing the code inside of it

  • We have a conditional statement checking to see if the mouse is in between the side of each rectangle. If the mouse is between one of the rectangles, it will turn red.

  • We are using the i counter variable as part of our conditional statement.

    So it is actually as if there are multiple conditional statements being checked, one for each time the for loop executes (in this case 5 times) and each conditional is different based on the value of i.

  • All of this code is inside of the draw loop which means it is constantly updating and therefore constantly checking and rechecking the conditional statement to see if any of the conditional statements are being met.

  • Because of this, when the conditions evaluate as true, we can consider i to have one specific value, whichever value caused the conditions to be true.

    Example: If the mouse is inside of the second rectangle, in order for the conditions of our conditional statement to be true, i must be equal to 1.

    Therefore, if we refer to the i variable inside of the conditional statement, it can only contain the value that is causing the conditions to be true.

  • If we continue this example, if i is equal to 1, then the code notes[ i ] would read as notes[ 1 ] which would be referring to the element in the array at index 1. For our notes array, the element in index 1 is 64.

  • The end result of osc.freq(notes[ i ]) is osc.freq(64)

midiToFreq( ) method

Have students run this code and click through each rectangle to listen. Point out that the sound coming from the oscillator seems low and it is difficult to tell the difference between one note to the next.

Explain that there technically isn’t anything wrong with our code and it is working exactly as it is written to do but it isn’t doing what we want it to do. You could choose to frame this as a bug in our code. However, it is not a bug in the sense of needing to find what is wrong with the actual code. It is more a bug in our own understanding. We need to figure out what we are missing in our understanding in order to fix this bug.

Ask students to take a moment to try and figure out what the issues is.

Note: This could be treated as an assessment opportunity/check for understanding. You could have students try to explain what is happening and provide a series of clues to scaffold the thought process and help to differentiate depending on how well students may understand it.

Remind students that the .freq method accepts a number which represents the hertz of a frequency. Right now, all the values we are passing as arguments are intended to be MIDI note values but are being interpreted as hertz, which is why all the notes sound so low (remember our range of hearing starts at around 20-40 hertz) and why we don't hear much difference between one note to the next (each note is only separated by 2-3 hertz)

When we refer to our conversion chart, we can see that each MIDI note has a corresponding frequency value in hertz. So one way to fix this would be to make an array using the frequency values instead of the MIDI values. However some of these values have several more digits and decimal points which would make our array much longer to write.

We could incorporate a formula into our code to do the conversions for us. Luckily there is already a function that will do just that.

Introduce midiToFreq( ) function.

This function will take a MIDI note value and return the equivalent hertz value for that note pitch. We can then pass that returned value as an argument to the .freq( ) method of our oscillator object.

We can look at our conversion chart to know what the result will be.

Example - midiToFreq(60) will return 261.626

We can now create a variable to store the returned value from this function and then pass that variable into the .freq method

let noteFreq = midiToFreq(notes[i]);
osc.freq(noteFreq);

Have students run this code and listen to the results. They will hear that this time, they can hear each specific note from the scale clearly.

Note: You can inform students (or have them try to figure out) that this code can be refactored into one line of code by passing the midiToFreq function directly as an argument to the .freq method

osc.freq(midiToFreq(notes[ i ]));

However, this code can be a bit more difficult to read and there are more chances for errors when using multiple parentheses in a single line of code.

Changing the Size of the Array with .length

Tell students that often times with a scale, the first note will be repeated at the end of the scale but one octave higher. For example if you start with C4, one octave higher would be C5. With MIDI notes, one octave higher would be 12 notes higher. For example, If you start with 60, one octave higher would be 72.

Ask students to add one more note to the end of their notes array. It should be the first note, one octave higher. It is recommended to double check to make students have the correct note. After students have done that, have them to run their code.

Ask what they observe - Their sketch still only shows/plays 5 notes.

Ask what they need to do to fix this - We would need to change the numRects variable to 6

Remind students the whole reason we made the numRects variable was so we could change the number of rectangles on the canvas without having to change any other parts of our code. Now our goal is to change the number of elements in our array and have all the other parts of our code change without having to do anything else to our code.

Introduce .length property.

If we add .length to the end of the name of our array, it will return the number of elements in the array.

Example - notes.length would return the value of 6.

Therefore we can use this property to assign the value to the numRects variable and if we change the number of elements in our array, it will automatically change all other parts of our code.

Have students change their code to reflect this.

let numRects = notes.length;
let rectWidth = width / numRects;
for(let i = 0; i < numRects; i++) {
    fill('white');
    if(mouseX > i*rectWidth && mouseX < i*rectWidth + rectWidth){
        fill('red');
        let noteFreq = midiToFreq(notes[i]);
        osc.freq(noteFreq);
    }
    rect(i * rectWidth, 0, i * rectWidth + rectWidth, height);
}

Have students now run the code and they will see that there are now 6 rectangles which will play all 6 notes in the array. Students can also experiment with adding or removing elements in the array to see that the sketch will still have the correct number of rectangles and notes.

Final Sketch Code

Wrap Up

At the end of this lesson, students should have a working instrument which they could use to play music. If time permits, you can even allow students to play around a bit and try to make some music for themselves or in small groups.

For the next and final lesson of this unit, they will be asked to make modifications on this instrument based on some hypothetical user feedback. Consider that the user is a musician who wants to use this as a performance tool. Having already had experience working with user feedback, students can be asked to start visualizing what some possible modifications to this instrument could look like.

You could choose to make this into a short homework assignment where students need to bring in a couple of ideas for how they could modify this instrument or just plant the seed that this will be the next step so they come into the next class having at least been prepared for what to expect.

Extensions

Students can research other types of scales that have more than 5 notes and try to input those scale notes into their array. Some students may already have some musical background and can try to incorporate that knowledge into this project. For example, students could input a scale they already know and try to play a simple song using their instrument. They could also use this experience to consider how this project functions as an instrument which will be helpful for them as they move to the final project.

Last updated