Monday, November 14, 2011

Sparkfun Inventor's Kit Circuit-06: Music with a Piezo Element

Much like the Servo Tutorial (number 4) the components here are amazingly simple -- just a single Piezo element and 4 wires.  Everything else is in the code.

When you load this one up, the Piezo element cranks out a melody that you could sing the following lyrics to "Twinkle Twinkle Little Tots, how we love Arduino Bots".  

Then it loops and gets annoying.
Okay, where is the off switch?
There, I unplugged it. Phew.
I could have also just unplugged the Piezo element instead of the usb cable and kept the thing powered up. Or disconnected the Ground  wire (disconnecting the 5V wire does nothing).  Lots of ways to turn off that annoying melody!

First the theory. Controlling sound is different than lights, motion and electrons because sound is an analog phenomenon. We are using a digital microcontroller to create it. How?  The book says that we mimic analog behavior by pulsing current through the piezo at an "incredible speed".  Since a piezo element clicks every time a pulse goes through it, if we pulse at 440 Hz (cycles or times per second) then we get the note A440 (the tuning fork pitch, like the middle A on a really icky keyboard).
If you double the number of clicks over the same period of time (880 clicks per second) you get a note an Octave above (also A, but higher in pitch). Halve it and you get a lower A (A220).  In between are all the other notes. The code explains them:

/* Melody
 * (cleft) 2005 D. Cuartielles for K3
 *
 * This example uses a piezo speaker to play melodies.  It sends
 * a square wave of the appropriate frequency to the piezo, generating
 * the corresponding tone.
 *
 * The calculation of the tones is made following the mathematical
 * operation:
 *
 *       timeHigh = period / 2 = 1 / (2 * toneFrequency)
 *
 * where the different tones are described as in the table:
 *
 * note     frequency     period     timeHigh
 * c             261 Hz             3830     1915    
 * d             294 Hz             3400     1700    
 * e             329 Hz             3038     1519    
 * f             349 Hz             2864     1432    
 * g             392 Hz             2550     1275    
 * a             440 Hz             2272     1136    
 * b             493 Hz             2028    1014   
 * C            523 Hz            1912     956
 *
 * http://www.arduino.cc/en/Tutorial/Melody
 */
 
int speakerPin = 9;

int length = 15; // the number of notes
char notes[] = "ccggaagffeeddc "; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}

void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
 
  // play the tone corresponding to the note name
  for (int i = 0; i < 8; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}

void setup() {
  pinMode(speakerPin, OUTPUT);
}

void loop() {
  for (int i = 0; i < length; i++) {
    if (notes[i] == ' ') {
      delay(beats[i] * tempo); // rest
    } else {
      playNote(notes[i], beats[i] * tempo);
    }
   
    // pause between notes
    delay(tempo / 2);
  }
}


 So when you string together notes of the right frequency you play a melody.

To change the speed of the melody the tutorial informs us that we need only change one line, making int tempo = 300 into int tempo = some new number.  I tried int tempo = 3000 just for kicks. It really slowed things down.  Then I sped it up by making int tempo = 30.  Hardly recognizable.

You can tweak the tuning of the notes by changing the values in the tones[] array which I have marked in red above.

To make your own melody they say that it is easy.  "Each song is defined in one int and two arrays. The int length deines the number of notes, the first array called notes[] defines what the notes are and the second, beats[], defines how long each note is played.

To play the first line of that horrible birthday favorite, "Hippo Birdy" (and risk copyright infringement again,  just like every year) you would use
int length = 13;
char notes[] = {"ccdcfeccdcgf "};
int beats[] = {1,1,1,1,2,1, 1, 1, 1, 2,4};

Note that a space after a note equals a rest and that needs timing too. So you see 12 notes but an int length of 13 because the rest, which lasts for 4 beats, must be included.

And here is my attempt to play the first 10 notes of the awesome Blue Oyster Cult song "Godzilla"

int length = 10; // the number of notes
char notes[] = "cfgdeabfc "; // a space represents a rest
int beats[] = {3,1,1,1,3,1,1,1,3,3};
int tempo = 300;

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}

void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int tones[] = { 1915, 1700, 1600, 1432, 1275, 1150, 1100, 956 };
 
Note that I had to change the value of e to something like an Eb (1600) and the values of A and B to 1150 and 1100 respectively. Tweaking it will make it better.

Try it out and have fun!.



No comments:

Post a Comment