Skip to content


Arduino Sound Part 3. Playing a Melody

ArduinoSoundPart3BBThis is the third in a series of articles about generating sound with an Arduino. In this article we bit-bang our way through Jingle Bells . It’s almost Christmas so why not make a festive microcontroller project?

If your not up to speed on making sound with the Arduino read the first two articles in this series.
Arduino Sound Part 1 – Sound Generation Methods
Arduino Sound Part 2 – Hello World

In the last article (part2) we got ready to create sound. Now we will give the Arduino just enough voice to play Jingle Bells. It will be easy for you to adapt this code to play other music and I hope you will share your code with us in the comments.

At the end of Part 2 I suggested that you create a function which generates the sound once given a frequency and duration. I even mentioned that most of the work we done for you by Paul Badger with his freqout function. The first step in this article will be to create this function. I chose to create a new function based on Paul’s code called ArduinoSound.

The ArduinoSound function uses bit-banging to generate sound as described in the previous articles. It’s a very simple function and differs from Paul’s freqout function only in that it works with floating point numbers. Paul’s function is more efficient but might be harder to follow.

Bit-Banging Sound
To bit-bang sound we toggle an output pin at the desired frequency. To play a musical note, we need to use specific frequencies and play the sound for a specific amount of time.

Generating the desired frequency big-bang style means figuring out what the period or cycle time of the desired frequency is. This is easy to calculate, it’s the reciprocal (1/F) of the frequency. As we found in part 2, 1KHz is 1000 cycles per second. So, one cycle takes 1 one-thousandth (1/1000) of a second or 0.001 seconds (1 millisecond). We need to toggle the pin at this rate so we want to keep it low for 1/2 the time then high for 1/2 the time. Therefore we want to set the I/O pin low for 500uS, then high for 500uS and do this over and over for as long as the duration requires.

With this information and Paul’s fregout function as an example, I created the following function. The code has comments but we will cover it in depth below.

void Sound(float freq, int durationMS, int outputPin) {
  int halfPeriod;
  float period;
  int durationCycles;
  //Check for rest, 0 frequency is a rest for durationMS.
  if(freq==0.0) {
    //0 frequency so we stay quiet for duration
    delay (durationMS);
  }
  else { //Frequency is not zero so we have work to do
    // turn on output pin
    pinMode(outputPin, OUTPUT);
    //calculate the period or cycle time for the given frequency
    period=1/freq; //Take the reciprocal to get time in seconds
    period=period*1.0E6; //to covert seconds to uS.
    //divide that by 2 to get the 1/2 cycle time. convert to int at the same time
    halfPeriod = (int)(period/2.0) - 7; // subtract 7 us to make up for digitalWrite overhead

    // calculate cycles for duration.
    durationCycles = (int)(((float)durationMS*1000.0)/period); // play note for duration ms
    for (int i=0; i<durationCycles; i++){
      digitalWrite(outputPin, HIGH);
      delayMicroseconds(halfPeriod);
      digitalWrite(outputPin, LOW);
      delayMicroseconds(halfPeriod - 1); // - 1 to make up for fractional microsecond                                              in digitaWrite overhead
    }
    // shut off pin to avoid noise from other operations
    pinMode(outputPin, INPUT);
  }
}

I named the function “Sound” so that it does not get confused with Paul’s freqout function. I’m hoping that it’s OK to use such a generic name. If there is a conflict then I don’t mind changing.

The function takes three arguments. Frequency, duration, and the output pin. Frequency is the desired output frequency in Hertz or cycles per second. Duration is the time to play the note for in milliseconds, and the output pin is which pin to play the note on. The pin is set for output by the function and returned to input mode when complete. If you give a zero frequency the function does nothing for the duration. This makes the function a bit easier to use when playing music as you can use this function both for the notes and rests.

The first step is to check for zero frequency. If the frequency is zero, we need to stay quiet for the duration time. We just delay for duration then return. If the frequency is not zero, we have a note to play. We start by setting the desired I/O pin to output mode.

Calculate the period then convert the result to microseconds. We need microseconds for the delayMicroseconds function we will be calling soon. We divide that by 2 for the half period value which is the time we need to hold the pin low then high to toggle it at the desired frequency.

The next calculation is how many cycles of the frequency we need to generate to get our desired duration. This is a nice trick which I got from Paul’s freqout function . The calculation is easy, we convert the duration value to seconds then divided it by the period or cycle time of our frequency.

For example if we wanted to generate 1KHz for 250mS. We know that the cycle time for 1KHz is 0.001 seconds from the descriptions above. 0.001 seconds is 1mS so we have a duration of 250mS divided by 1ms which gives us 250. We need to generate 250 cycles of 1KHz to use up 250mS of time. The result is stored in durationCycles which is used by the for loop to control how many cycles we generate.

Now comes the easy part, we have a for loop which will cycle us though the code the correct number of times for our duration, we only need to toggle the pin correctly. The half period value has already been calculated so we just set the pin High for the half period, then low for the same amount of time.

When the cycles are complete, we set the I/O pin to input mode and return.

This function works should be very useful in many applications. If you only need to make a beep-beep sound or maybe generate sounds with a frequency based on some inputs then you are all set.

I wanted to show off my new function by playing a melody. I did some searching for a simple melody and learned that there are a lot of music file formats. None seemed extra easy to decode for my purpose. I wanted a simple, single voice melody, ideally something everyone would recognize and would be available as a set of frequencies and durations. I did not find what I was looking for.

I did find music music in a form that was easy to read, if you knew how to read sheet music. I found several web sites that helped me get started reading it. Eventually I was able to learn enough of the mysterious language to translate Jingle Bells into a form I could use. Here are a few links in case you want/need to learn.

With the notes and durations in hand, I still needed to know the frequencies for each note. The table of note frequencies in Paul Badgers freqout did not look like the correct values to me. So I hunted around online and found a nice list here . Later I figured out that Paul’s frequencies are multiplied up by 64 so that they can fit into an int and still have sufficient resolution. I created a C header file with defines for each of the notes. This will make it easy to generate any note in the future. A small piece of that header file is shown below.

#define FREQ_A3 220.00
#define FREQ_AS3 233.08
#define FREQ_BF3 233.08
#define FREQ_B3 246.94
#define FREQ_C4 261.63 //Middle C
#define FREQ_CS4 277.18
#define FREQ_DF4 277.18
#define FREQ_D4 293.66
#define FREQ_DS4 311.13

Now I need to create the code to play Jingle Bells. There are 108 notes and rest in the version of Jingle Bells so I could have just coded up all these calls to my new Sound function. I did not feel that that would be a good solution. Instead I needed to create a way to store the music as data that could be processed.

I remember playing with a BASIC sound function in the old Color Computer and Commodore 64 days. This function took a string of music as input. I did a quick search but ultimately decided to reinvented the wheel. I did ultimately find some information on the QBasic Play function but not until after I had created my lesser version. If you have a suggestion for a better encoding or perhaps a good site to learn about the Play function please comment.

My implementation is very simple and barely enough to work for Jingle Bells. It will keep me from coding up 108 Sound calls. We ultimately need a function very much like the older BASIC Play function for the Arduino . Perhaps we can work toward that goal.

My PlayMusicString function is shown below. The function takes two inputs, a character string encoded with music and the output pin to play the sound on. The string is made up of note/duration pairs. Notes can be one of “abcdefgD”. For example ‘c’ is FREQ_C4 from the notes table. The second number is the note duration and can be 1, 2, 4, or 8 for whole, half, quarter, or eight note. For example, ‘c4′ means plays a ‘c’ note for a quarter beat. Use a space followed by the note duration to encode a rest. For example ‘ 4′ is a quarter rest.

void PlayMusicString(char* music, int outputPin){
  int noteCount=strlen(music);
  float freq;
  int duration;
  for (inti=0;i<noteCount;i+=2) {
    switch(music[i]){
      case ' ' :
        freq=0;
        break;
      case 'D' :
        freq=FREQ_D4;
        break;
      case 'e' :
        freq=FREQ_E4;
        break;
      case 'f' :
        freq=FREQ_F4;
        break;
      case 'g' :
        freq=FREQ_G4;
        break;
      case 'a' :
        freq=FREQ_A4;
        break;
      case 'b' :
        freq=FREQ_B4;
        break;
      case 'c' :
        freq=FREQ_C5;
        break;
      case 'd' :
        freq=FREQ_D5;
        break;
    }
    //Note Timing
    switch(music[i+1]){
      case '1' :
        duration=WHOLE_NOTE_TIME;
        break;
      case '2' :
        duration=WHOLE_NOTE_TIME/2;
        break;
      case '4' :
        duration=WHOLE_NOTE_TIME/4;
        break;
      case '8' :
        duration=WHOLE_NOTE_TIME/8;
        break;
    }
    Sound(freq,duration,outputPin);
    delay(WHOLE_NOTE_TIME/32);
  }
}

The PlayMusicString function starts by finding the length of the music string. A for loop steps two characters at a time so we can process the note/duration pairs with two switch statements. The first switch statement decodes note characters and sets the frequency parameter using constants from the header file. The second switch statement decodes note duration and sets the duration value. The Sound function is then called with the frequency, duration and output pin. Finally we delay without generating sound for a short time to create a small gap in the sound. This is to simulate a key being released.

I’m sure the musically inclined readers have a lot of dislikes with the methods used in the PlaySound function. I hope to develop this function into an easy solution for microcontroller hobbyist and I welcome any suggestions for improvement.

I created a separate set of files to keep the new functions in. Functions Sound and PlayMusicString are in ArduinoSound.cpp and the header file, ArduinoSound.h, includes the function prototypes and note definitions. Using separate files (also called modules) makes it easy to reuse these functions in other projects. I don’t have to cut and paste, I can just include the files in my next project.

Here is the main module code for my Jingle Bells project.

#include "ArduinoSound.h">
#define OUTPIN 9
void setup() {
}
void loop() {
  //Jingle Bells translated from sheet music at
  char music[] =
    "D4"             "D4b4a4g4"    "D2 4D4"    "D4b4a4g4"
    "e2 4e4"        "e4c4b4a4"    "f2 4d4"     "d4d4c4a4"
    "b2g4D4"       "D4b4a4g4"    "D2 4D4"    "D4b4a4g4"
    "e2 4e4"        "e4c4b4a4"    "d4d4d4d4" "e4d4c4a4"
    "g2g4 4"        "b4b4b2"       "b4b4b2"    "b4d4g4a8" "b2"
    "c4c4c4c8"    "c4b4b4b8b8" "b4a4a4b4" "a2d2"
    "b4b4b2"       "b4b4b2"       "b4d4g4a8" "b2 4"        "c4c4"
    "c4b4b4b8b8" "d4d4c4a4"    "g2";

  delay(1000);
  PlayMusicString(music,OUTPIN);
}

The first line includes a header file for the new functions. Next we define the pin we want sound to output on. There is nothing to do in the setup function which leaves the loop function to do all the work.

There are only two lines of code in the loop function. The first one is “delay(1000)” which causes the program to pause for 1 second. Its only purpose is so that you know when the song starts as the music repeats. The next line plays the music stored in the string using the new PlayMusicString function.

All the text after the “char music[] = ” is Jingle Bells encoded into a string. I used a trick to help keep things straight with the music. I encoded each measure into a short string and separated them with a space. This space does not effect the data stored in music. This makes it easy to find my place between the string of codes and the actual sheet music. I also put each staff line on a separate line in the code. As with the spaces, the new lines don’t effect what is stored in the music string. It would have been exactly the same to write that string out without the extra quotes and spaces. There are spaces inside the strings to encode rest so those are important, but they are inside the quotes.

Here is a zip file with all the code for this project . Remember that the sound module code is stored in separate source files. I hope you make good use of this code. Let us know how you use it with a comment.

ArduinoSoundPart3BBHere is a picture of my breadboard and a sound file of it playing Jingle Bells using the code above. There are extra parts on the board for the upcoming DAC article and I put a LED to the output pin so I could see it playing. I am using a Modern Devices Arduino compatible Really Bare Bones Board (RBBB) . Really all the extra parts are not necessary, you just need a couple of resistors and a cable to connect so some speakers or your PC. The code will work on any Arduino compatible microcontroller as well.

What’s Next
Last time I said this article would be about generating nice sine waves… I wanted to play a melody so I benched the sin output article. So next week I hope to cover generating clean sin waves using one or more of the DAC methods described in the first article.

Comments Please:

  • We would like to hear your thoughts and suggestions.
  • Do you have an encoding suggestion that would work well and be simple to use?
  • A link for Documentation of the BASIC Play function?
  • Interesting music encoded to play? (I originally wanted to do Daisy from 2001)
  • An interesting project that needs sound?
  • Links to other relevant information?

Posted in Arduino, Discovering, Microcontroller, Projects.

16 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Django said

    Thanks for another great article! Really looking forward to the R2R DAC example, which should allow better quality sound output – like sampled audio from .wav files!
    Best Regards,

    Django

  2. Hi…

    Looking at the PlayMusicString function I wondered if you used a switch/case structure rather than something like a look-up table intentionally to keep things simple or not?

    I’d be inclined to make the duration calculation something like this (sans error checking (and testing :-) )):

    duration=WHOLE_NOTE_TIME/(music[i+1] – ’0′);

    Which converts the numeric ASCII character into its value. (Or indeed, you could use a pre-calculated lookup table for that also.)

    –Phil.

  3. Trumpetto said

    Great article again.
    On encoding … there is very much done on that subject and there has always been a big division between codings for analysis and codings for printing music.
    see: http://www.music-notation.info/en/compmus/notationformats.html
    My suggestion for now: keep things simple and use a format that gives you many tools; abc
    http://www.music-notation.info/en/compmus/notationformats.html#abc

    Trumpetto

  4. arcospark said

    Wow! I had a work-study job in college as a programmer for the music department’s “electronic music lab”. Everything they had until 1977 was analog, and I was to write the code for a digital sound program, based on MUSIC V, a FORTRAN program developed in the late 50′s by Max Mathews at Bell Labs (http://en.wikipedia.org/wiki/Max_Mathews). How is that for a history lesson?

    Well, my 5,000 punch cards were never able to produce a toot – the computer center limited the amount of system resources a program could grab. And, even if it did run, the electronic music lab didn’t have a DAC to convert any output (tape) to audio.

    Basically, you (and the assorted referenced sources) have done more than I did in two years back then. I think we all learned a lot in the process, and the job did launch me into a career in computer tech and related fields.

    Thanks for the unexpected journey to my “roots”.

  5. arcospark said

    PS: Found the Jingle Bells audio file and wanted to mention that the ‘F’ at the end of the third line (“And over the fields we go”) should be F-Sharp. I could write a long paragraph about key signatures, but that’s not needed. Sound is very good for such a simple setup.

    Looking forward to future articles. Is there a punch-card reader for the Arduino? :P

  6. dfowler said

    Thanks arcospark,

    I knew that error was there and wondered if anyone would pick up on it. From my short course in sheet music I understand that the # symbol on the ‘f’ line means that all the f notes are sharp. At least that’s what I thought. To fix this I could have just changed the frequency for the ‘f’ note but I figured someone would notice that. I did not create a way to distinguish between the normal, sharp or flats with my simple string encoding method. I am working on a better encoding scheme, probably based on MIDI note numbers which can be converted to frequencies with some simple math.

  7. Quazatron said

    The ZIP file seems to contain only the HelloWorldSound.pde file…

    I tried to copy+paste the bits of code from the article but couldn’t get the code to compile.

    Could you please provide the final version?

    Thanks! :-)

  8. Quazatron,
    Sorry about that… The link and zip file should be fixed now.

  9. Quazatron said

    Thanks, this year there will be an arduino under my christmas tree.

  10. Quazatron said

    Still no luck. The .h file seems to be empty and there’s no sign of the “Jingle Bells”… :-(

  11. Quazatron said

    Finally figured out why it wouldn’t compile: it seems the compiler doesn’t like some UTF8 characters from the web page.
    It’s working great. Nice work!

  12. Peter said

    Great job. It seems easy for someone who knows music, but it is really difficult for the “others”… :)

    I know, I went there, did that, took quite some time to make a sound looks nice, timming, notes, etc.

    Now, another example of music, using an AVR AtTiny45, six channels, wavetable… wonderful result, but of course the author knows about music, it helps a lot.

    http://elm-chan.org/works/mxb/report_e.html

    There is a MP3 sample of it, check it out.

    Peter.

  13. The zip file is still linked to helloworldsound1.zip, which contains the file from your earlier article.

    Will you please link to the correct file? Thanks!

    -Adam

  14. dfowler said

    Adam,

    I will get this sorted very soon. Thanks for reporting the problem.

  15. I wasn’t able to find the code on this page. I reconstructed it from the text. One of the notes seems a bit off. Anyway, here it is:

    http://rob.gubler.net/devel/ArduinoJingleBells/jinglebells.pde

  16. Hey we made an arduino synth http://code.google.com/p/arduinosynth/ inspired by all the people working on making a synth for arduino