Here is a nice code sample that I put together that plays music on the Arduino. Just hook up a speaker to pin 8, add a resistor if necessary, and it will play a few songs, one with multiple voices (polyphonic music).
// Arduino Music Demo
// James S. Ghofulpo
//
// Plays multi voice music. Documentation for writing your own music is below.
#define TS ( 130 ) // Time for a 1/16th note, in ms
#define STACATTO ( 80 ) // Time for a stacatto note, in ms
#define OUTPUT_PIN ( 8 ) // Which pin on the Arduino are we modulating
#define NUM_VOICES ( 2 ) // Number of voices supported
#define MAX_CYCLES ( 10 ) // On and off cycles before switch
// Data structure for each voice
typedef struct {
float f; // Frequency
long t; // Time left to play this note
long decay_time; // Time left to decay (turn off) note
long on_pt; // On phase time
long off_pt; // Off phase time
int synchronizing; // Set to 1 if we are waiting to synchronize
int current_voice; // which voice is being analyzed in the muisc
const char * music;
const char * start_of_music;
const char * return_pos;
} voice_info_t;
// Function to play a song
//
// Tone Commands:
// a-g - Select low octave note
// A-G - Select high octave note
// + - This note, up one octave
// - - This note, down one octave
// # - Sharp
// & - Flat
// t - Note is a triplet (1/3 of timing)
// . - Note is dotted (timing * 1.5)
// , - Note is stacatto
// r - Rest
// s, e, q, h, w - Timing (1/16, 1/8, 1/4, 1/2, 1) of note. Note plays when time is selected!
//
// Voice / flow commands:
//
// V - This is part of the next voice
// >X - Call part X
// [X yyyyy] - Define part x to be yyyyy
// | - End of measure. Allows for synchronization, in case of misprogrammed music.
// k - The previously mentioned note selects the key signature (i.e. fk selects F major).
void play_song( const char * this_song )
{
int inx = 0;
int iny;
int duration = 0;
int note_offset = -1;
voice_info_t info[NUM_VOICES]; // Each voices' info
int triplet = 0;
int key[12];
long shortest_time = 999999;
int current_phase = 0;
long current_time;
int in_combo;
long time_in_micros;
float on_duty_cycle = 0.1;
int duty_cycle_direction = 0;
int current_voice = 0;
int cycle_count = 0;
int dotted = 0;
int stacatto = 0;
char search_target;
int everybody_is_done = 0;
int accidental = 0;
for ( inx = 0; inx < NUM_VOICES; inx++ )
{
info[inx].music = this_song;
info[inx].start_of_music = this_song;
info[inx].return_pos = this_song;
info[inx].f = 0;
info[inx].t = 0;
info[inx].decay_time = 0;
info[inx].on_pt = 0; // Frequency of this voice
info[inx].off_pt = 0;
info[inx].synchronizing = 0;
info[inx].current_voice = 0;
}
// Initialize key to C major
for ( inx = 0; inx < 12; inx++ )
{
key[inx] = 0;
}
//Serial.println( "Starting loop" );
while ( !everybody_is_done )
{
int everybody_is_synchronizing = 1;
everybody_is_done = 1; // Assume we are all done
for ( inx = 0; inx < NUM_VOICES; inx++ )
{
if ( *info[inx].music != 0 )
{
everybody_is_done = 0;
if ( !info[inx].synchronizing ) // Somebody is still running...
{
everybody_is_synchronizing = 0;
break;
}
}
}
// If everybody is synchronizing, then we can continue.
if ( everybody_is_synchronizing )
{
for ( inx = 0; inx < NUM_VOICES; inx++ )
{
info[inx].synchronizing = 0;
}
}
// Code to check for the next note/sets of notes
for ( inx = 0; inx < NUM_VOICES; inx++ ) // Check all voices
{
while ( ( info[inx].t <= 0 ) // out of time, get next note
&& ( *info[inx].music != 0 ) // Still music left to play
&& ( !info[inx].synchronizing ) ) // And not waiting for everybody to be ready
{
// Serial.print( *info[inx].music );
switch( *info[inx].music )
{
case '[': // Skip section [
while ( ( *info[inx].music != 0 ) && ( *info[inx].music != ']' ) )
{
info[inx].music++;
}
break;
case ']':
info[inx].synchronizing = 1; // Wait for everybody to get done
info[inx].music = info[inx].return_pos;
info[inx].current_voice = 0;
break;
case 'V': // End of this part
info[inx].current_voice++;
break; // Do nothing: End marker
case '>': // Call a subroutine
info[inx].music++; // Get the marker
search_target = *info[inx].music;
info[inx].return_pos = info[inx].music; // The return position will be +1
info[inx].music = info[inx].start_of_music; // Rewind
while ( ( *info[inx].music != '[' ) || ( *(info[inx].music+1) != search_target ) )
{
info[inx].music++;
}
info[inx].music++; // Skip the bracket
info[inx].music++; // Skip the marker
info[inx].current_voice = 0; // Starting with voice 0
info[inx].synchronizing = 1; // Wait for everybody to get done
break;
case '|': // Force synchronize between voices
if ( info[inx].current_voice == inx )
{
info[inx].synchronizing = 1; // Wait for everybody to get done
}
break;
case 'c': note_offset = 15; break;
case 'd': note_offset = 17; break;
case 'e': if ( note_offset == -1 )
note_offset = 19;
else
duration = TS * 2; // Eighth
break;
case 'f': note_offset = 20; break;
case 'g': note_offset = 22; break;
case 'a': note_offset = 24; break;
case 'b': note_offset = 26; break;
case 'C': note_offset = 27; break;
case 'D': note_offset = 29; break;
case 'E': note_offset = 31; break;
case 'F': note_offset = 32; break;
case 'G': note_offset = 34; break;
case 'A': note_offset = 36; break;
case 'B': note_offset = 38; break;
case '+': note_offset += 12; break; // Up an octave
case '-': note_offset -= 12; break; // Down an octave
case '#': accidental = 1; break; // Up a half step
case '&': accidental = -1; break; // Down a half step
case 's': duration = TS; break; // Sixteenth
case 'q': duration = TS * 4; break; // Quarter
case 'h': duration = TS * 8; break; // Half
case 'w': duration = TS * 16; break; // Whole
case 'r': note_offset = -2; break; // Rest, not really necessary, except for eighth notes
case 't': triplet = 1; break; // Duration is a triplet
case '.': dotted = 1; break; // Dotted value
case ',': stacatto = 1; break; // Stacatto
// default: break; // Unhandled command
}
if ( *info[inx].music == 'k' ) // Handle keys
{
int base = note_offset % 12;
for ( iny = 0; iny < 12; iny++ )
{
key[iny] = 0;
}
int num_sharps = 0;
int num_flats = 0;
switch ( base )
{
case 0: key[3] = key[8] = key[10] = 1; break; // A
case 1: key[2] = key[7] = -1; break;
case 2: key[0] = key[3] = key[5] = key[8] = key[10] = 1; break; // B
case 3: break; // C
case 4: key[0] = key[2] = key[5] = key[7] = key[10] = -1; break;
case 5: key[3] = key[8] = 1; break; // D
case 6: key[0] = key[2] = key[7] = -1; break;
case 7: key[3] = key[5] = key[8] = key[10] = 1; break; // E
case 8: key[2] = -1; break; // F
case 9: key[0] = key[2] = key[3] = key[5] = key[7] = key[10] = -1; break;
case 10: key[8] = 1; break; // G
case 11: key[0] = key[2] = key[5] = key[7] = -1; break;
}
note_offset = -1;
}
if ( duration != 0 ) // Note is complete
{
if ( triplet )
{
duration /= 3;
}
if ( dotted )
{
duration = ((float)duration * 1.5);
}
if ( info[inx].current_voice == inx ) // This note is for this voice
{
info[inx].t = millis() + duration;
info[inx].decay_time = info[inx].t - ( stacatto ? duration - STACATTO : 0 );
int base = note_offset % 12;
// Sample code to add phasing effects to music
/*
if ( duty_cycle_direction == 0 )
{
on_duty_cycle += 0.1;
if ( on_duty_cycle > 0.8 )
{
duty_cycle_direction = 1;
}
}
else
{
on_duty_cycle -= 0.1;
if ( on_duty_cycle < 0.2 )
{
duty_cycle_direction = 0;
}
}
*/
if ( note_offset >= 0 )
{
info[inx].f = pow( 1.059460646483, note_offset + key[base] + accidental ) * 220.0;
float pt = 1000000 / ( info[inx].f );
// The on phase time can be set here to add 'phasing' type effects
/*
on_pt[inx] = (long)( pt * on_duty_cycle );
if ( on_pt[inx] < 50 )
{
on_pt[inx] = 50;
}
on_pt[inx] = (long)( pt * on_duty_cycle ); */
info[inx].on_pt = 50;
info[inx].off_pt =(long)( pt - info[inx].on_pt );
}
else // Rest
{
info[inx].f = 0;
}
} // Else, this note is not for us
// Reset parser
note_offset = -1;
duration = 0;
triplet = 0;
dotted = 0;
stacatto = 0;
accidental = 0;
}
info[inx].music++; // Next character
}
} // Checked all voices
// Play the note / set of tones, until end of somebody's note
in_combo = 1;
while ( in_combo )
{
time_in_micros = micros();
current_phase = !current_phase;
if ( info[current_voice].f == 0 ) // Rest all voices
{
digitalWrite( OUTPUT_PIN, LOW );
}
else
{
digitalWrite( OUTPUT_PIN, current_phase ? HIGH : LOW );
}
if ( cycle_count++ > MAX_CYCLES ) // Check if we need to change voices
{
cycle_count = 0;
current_time = millis();
int start_voice = current_voice;
do
{
current_voice = ( current_voice + 1 ) % NUM_VOICES;
}
while ( ( info[current_voice].f == 0 ) && ( current_voice != start_voice ) );
for ( inx = 0; inx < NUM_VOICES; inx++ )
{
if ( current_time > info[inx].t ) // If this note has expired
{
info[inx].t = 0; // Request the next note
info[inx].f = 0; // Turn off the note
in_combo = 0; // Get next note
}
else if ( current_time > info[inx].decay_time ) // stop this note
{
info[inx].f = 0;
}
}
}
if ( current_phase )
{
shortest_time = info[current_voice].on_pt;
}
else
{
shortest_time = info[current_voice].off_pt;
}
time_in_micros = micros() - time_in_micros;
delayMicroseconds( shortest_time - time_in_micros ); // Sleep the time minus time to process this loop
}
} // While looping
}
// Here are some sample pieces of music. All songs, except for 'bourree', are single voice
// Super Mario Bros.
char super_mario_bros[] =
"[A EsEsrsEsrsCsEsrsG,ererq]" // Intro
"[B C,ersg,ers,e,ersa,eb,ea#sasrs gtqEtqGtqA,eFsGsrsE,eCsDsbsre]" // Main music
"[C reGsF#sFsD#srsEsrsg#sasCsrsasCsDs]" // Falling theme
"[D reGsF#sFsD#srsEsrsC+srsC+sC+srsre]" // Falling end 1
"[E reD#sreDsreCersrerq]" // Falling end 2
"[F CsCsrsCsrsCsDsrsEsCsrsasgsrsre CsCsrsCsrsCsDsEsrh CsCsrsCsrsCsDsrsEsCsrsasgsrsre EsEsrsEsrsCsEsrsGsrsrerq]" // Finish
"[G g-tqctqetqCtqEtqGqEq g-#tqctqe&tqg#gCtqE&tqG#qE&q a-#tqdtqftqa#tqDtqFtqA#qA#tqA#tqA#tq C+qqh]" // Finale
"[H EsCsrsg,ersg#,easFsrsFsasrsre btqAtqAtqAtqGtqFtqEsCsrsasgsrsre EsCsrsg,ersg#,easFsrsFsasrsre bsFsrsFsFtqEtqDtqC,ererq]"
">A >B >B >C >D >C >E >F" // Play the intro and the main tune
" >B >B >C >D >C >E >F" // Play the main tune again
">H >H >G"; // Play the second theme, followed by the finale
// char air[] = "ek eegefebegefses febegeCqbqa#e bi"; // Key of E
// Bourree by G.F. Handel, adapted from 'Music' by Software Technology Corp.
char bourree[] = "[A"
"gk Dq | D,qb,qCebea,eg,e | EqGhFeEe | DqC,e,beaebec,ea,e |" // M 1-4
" bqghaq | beC#ed,eb,eC#eDeE,eC#,e | DeEeF,eD,eE,eFeG,eE,e |" // M 5-7
" FeGeA+ereC#e | D.h" // M 8-9
"V bq | a,qg,qfeb-q | gqEqDqgere | fqrqgqfq |" // M1-4
" gqdhfq | gqaqgqeq | aqaqaqC#q | dhaqeq | f.h]" // m5-9
"[B"
"gk A+q | AqFqGeFeEeDe | GqBhEq | D#qEqFqGeAe |" // M17-20
" GqEhDq | DqCebeCqCq | CqbeaebqDq | EeFeGeEeFeGeA+eFe |" // M21-24
" GeAeBeGeAeBeC+eAe | BqAeGeFeGeAeFe | GeAeBeAqCq | bqaegefegeaege |" // M25-28
" geaebegeaebeceae | beCeDqbqaege | g.h" // M29-31
"V Fq | FqDqDererq | dqb-ecedqeq | fqgqEqDq |" // M17-20
" bqEhDq | eqeheq | dqdhgq | ghah |" // M21-24
" DhDh | DqCebeaqDq | DqDqDqCq | dqrqrh |" // M25-28
" derereredererq | dererqdqcq | b-.h]" // M29-31
">A >A >B >B"; // Play the two parts
char deck_the_halls[] = "[A g.qfeeqdq cqdqeqcq] " // Deck the halls with boughs of holly
"[B deeefedee.qde cqb-qch] " // Fa la la la la la la la la
"[C aeaeaeaeg.q fe eqdqch ]" // Last fa la la la
">A >B >A >B "
"d.qeefqdq e.qfegqdq eefegqaebeCq bqaqgh "
">A >C";
char nyan_cat[] =
"bk " // Key of B
"[A D+sE+sF+eB+eD+sE+sF+sB+sC++sD++sC++sA+sB+e |"
" F+ed+se+sF+eB+eC++sA+sB+sC++sE++sD++sE++sC++s]" // Intro music
"[B FeGeDsDsrsbsD&sCsbsrsbeCe | D&eD&sCsbsCsDsFsGsdsfscsdsbscsbs " // Main theme
" DeFeGsDsFsCsDsbsD&sDsD&sCsbscs | D&ebsCsDsFsCsDsCsbsCebece ]"
"[C befsgsbefsgsbsCsDsbsesDsEsFs | bebefsgsbsfsEsDsCsbsfsdsesfs |" // Chorus
" befsgsbefsgsbsbsCsDsbsfsgsfs ]"
"[D bebsasbsfsgsbsEsDsEsFsbeae ]" // Chorus end 1
"[E bebsasbsfsgsbsEsDsEsFsbeCe ]" // Chorus end 2
"[F bebsasbsfsgsbsEsDsEsFsbe ]" // Chorus end 2, without last note
">A >B >B >C >D >C >E "
" >B >B >C >D >C >F";
//char chord0[] = "cw";
//char chord1[] = "rwCwCwCwCwCwCw V cwdwewfwgwAwBwCwDwEwFwGw";
//char chord[] = "CwCwCw V gwgwgw V cedeeefegeAeBeCeDeEeFeGe";
char silence[] = "rwrwrw";
void setup() {
pinMode( OUTPUT_PIN, OUTPUT );
//Serial.begin( 115200 );
play_song( super_mario_bros );
play_song( silence );
play_song( nyan_cat );
play_song( silence );
play_song( deck_the_halls );
play_song( silence );
play_song( bourree );
play_song( silence );
}
void loop() {
// no need to repeat the melody.
}