Speakers generate sounds by vibrating.
You can generate those sound vibrations with the same PWM feature you used in Chapter 6 to adjust the intensity of LEDs. By changing the period and length of pulses, you can change the frequency of the sounds. And by using pulses that correspond to musical notes, you can play songs.
You can also use the length of pulses as a signal to a servo (motor), setting the servo to certain angular positions through specific pulse lengths.
To play a song, you’ll need a speaker. For this example, use a common and inexpensive piezo speaker.
Wire up the piezo’s positive wire to pin D5 on your Netduino. Wire up the piezo’s negative (ground) wire to the GND pin on your Netduino. Figure 7-1 shows the connections.
Some piezos have legs rather than wires. In most cases, one leg is longer than the other, and the long leg is positive. There may also be faint markings on the piezo indicating which is positive and which is negative. If in doubt, check your piezo’s instructions or datasheet for proper wiring.
Create a new Netduino project. Add the following code to the top of your Main routine:
// store the notes on the music scale and their associated pulse lengths System.Collections.Hashtable scale = new System.Collections.Hashtable(); // low octave scale.Add("c", 1915u); scale.Add("d", 1700u); scale.Add("e", 1519u); scale.Add("f", 1432u); scale.Add("g", 1275u); scale.Add("a", 1136u); scale.Add("b", 1014u); // high octave scale.Add("C", 956u); scale.Add("D", 851u); scale.Add("E", 758u); // silence ("hold note") scale.Add("h", 0u);
I’m introducing a new concept here called a
Hashtable
. A Hashtable
is a
collection of keys and values. The keys here are the notes, and the
numbers are the pulse length values. Hashtable
s
make it easy to look up values by specifying their keys. You’ll do
this later by looking up notes and getting back their pulse length
values.
The “u” after each value signifies that the values should be stored as unsigned integers (whole numbers). You could store this data in other ways, but this makes the sample simpler to understand later on.
These note values are calculated by the equation 1 / (2 * toneFrequency). toneFrequency is the number of cycles per second for a specific note. You can extend this scale further if desired.
Next, specify the speed of the song. You’ll specify this in beats per minute, a common measurement for music.
int beatsPerMinute = 90; int beatTimeInMilliseconds = 60000 / beatsPerMinute; // 60,000 milliseconds per minute int pauseTimeInMilliseconds = (int)(beatTimeInMilliseconds * 0.1);
To calculate the beatTime (length of a beat) in milliseconds, divide 60,000 (the number of milliseconds in a minute) by the number of beats per minute.
And since you want a tiny bit of pause in between each note (so that the notes don’t all blur together), multiply the beat length by 10% to create a pause length. Later on, you’ll subtract 10% from the beat length and insert this pause instead, keeping a steady rhythm but making the song more pleasant.
You casted the pause time to an integer value. Integers are positive or negative whole numbers. Since 0.1 (10%) is a fractional number and not a whole number, you need to do this explicitly. The programming language is smart and realizes that you might lose a little bit of accuracy by ending up with a few tenths of a number that can’t be stored in an integer. So it requires that you explicitly cast the product into an integer to show you know that you might lose some precision.
Now, define the song. This song involves animals on a farm and the sounds that they make:
// define the song (letter of note followed by length of note) string song = "C1C1C1g1a1a1g2E1E1D1D1C2";
The song is defined as a series of notes (upper-case for higher octave, lower-case for lower octave) followed by the number of beats for each note. For the musicians’ reference, one beat is a quarter note in this instance and two beats is a half note, in 4/4 time.
Now that you have a musical scale and a song, you need a speaker to play them. You’ll add one here:
// define the speaker PWM speaker = new PWM(Pins.GPIO_PIN_D5);
You’re all set to play the song. Create a loop that reads in each of the note/length entries in the song string, and then plays them by generating specific pulses for the piezo using PWM.
Create a loop:
// interpret and play the song for (int i = 0; i < song.Length; i += 2) { // song loop }
The first part of the for statement, int i = 0
, establishes the variable i
as a position counter in the encoded
song’s string.
The middle part of the statement, i
< song.Length
, repeats the loop until you’ve finished
playing the song.
The third part of the statement, i +=
2
, moves the position forward by two places (the size of a
note/length pair) each time through the loop.
Inside this loop, you’ll extract the musical notes and then play them. Inside the pair of curly braces, add the music interpreter/player code:
// extract each note and its length in beats string note = song.Substring(i, 1); int beatCount = int.Parse(song.Substring(i + 1, 1));
The first line reads the note out of the song string at the
current position, i
. Song.Subtring(i, 1)
means “the string data
starting at position i
, length of 1
character.”
The second line reads the beat count of the song string. It
reads the beat count at one position beyond the note. Then, it uses
int.Parse()
to translate this
letter into an integer number that you can use. This is needed because
you can’t do math with letters, and you’ll need to do a bit of math
with the beat count shortly.
Now that you have the note, look up its duration:
// look up the note duration (in microseconds) uint noteDuration = (uint)scale[note];
By passing in the letter (the variable note
), you get back the noteDuration
. Lookup is a powerful feature of C#
that can be used for many purposes.
Whenever you retrieve a value from a collection (a
Hashtable
in this instance), you need to explicitly
cast it since the programming language wants to be sure it knows what it’s
getting.
Now, play the note:
// play the note for the desired number of beats speaker.SetPulse(noteDuration * 2, noteDuration); Thread.Sleep( beatTimeInMilliseconds * beatCount - pauseTimeInMilliseconds);
SetPulse
is another PWM feature
(in addition to SetDutyCycle
, which you saw in Chapter 6). It sets a period of twice the
noteDuration
, followed by a duration of
noteDuration
. This will generate a pulse at the
frequency required to sound the desired note.
You use Thread.Sleep
to keep the note playing for
the specified number of beats. You subtracted
pauseTimeInMilliseconds
from the note’s sounding time
so that you can pause a tenth of a beat and make the music more
pleasant.
Now that you’ve played the note, go ahead and insert that one-tenth beat pause:
// pause for 1/10th of a beat in between every note. speaker.SetDutyCycle(0); Thread.Sleep(pauseTimeInMilliseconds);
By setting the duty cycle to zero, you turn the piezo off momentarily. This creates the nice pause.
Finally, skip past the curly brace that ends the for loop and add one more line of code:
Thread.Sleep(Timeout.Infinite);
This will pause the program after playing the tune.
The final code should look like this:
public static void Main() { // write your code here // store the notes on the music scale and their associated pulse lengths System.Collections.Hashtable scale = new System.Collections.Hashtable(); // low octave scale.Add("c", 1915u); scale.Add("d", 1700u); scale.Add("e", 1519u); scale.Add("f", 1432u); scale.Add("g", 1275u); scale.Add("a", 1136u); scale.Add("b", 1014u); // high octave scale.Add("C", 956u); scale.Add("D", 851u); scale.Add("E", 758u); // silence ("hold note") scale.Add("h", 0u); int beatsPerMinute = 90; int beatTimeInMilliseconds = 60000 / beatsPerMinute; // 60,000 milliseconds per minute int pauseTimeInMilliseconds = (int)(beatTimeInMilliseconds * 0.1); // define the song (letter of note followed by length of note) string song = "C1C1C1g1a1a1g2E1E1D1D1C2"; // define the speaker PWM speaker = new PWM(Pins.GPIO_PIN_D5); // interpret and play the song for (int i = 0; i < song.Length; i += 2) { // extract each note and its length in beats string note = song.Substring(i, 1); int beatCount = int.Parse(song.Substring(i + 1, 1)); // look up the note duration (in microseconds) uint noteDuration = (uint)scale[note]; // play the note for the desired number of beats speaker.SetPulse(noteDuration * 2, noteDuration); Thread.Sleep( beatTimeInMilliseconds * beatCount - pauseTimeInMilliseconds); // pause for 1/10th of a beat in between every note. speaker.SetDutyCycle(0); Thread.Sleep(pauseTimeInMilliseconds); } Thread.Sleep(Timeout.Infinite); }
Now, run your app. Sing along with the song if you’d like. Look up the full song online and add the rest if desired.
18.191.168.8