On to the next project: a cover album of Violent Femmes. My song: Promise. Just need to get started now...
It's February, so I'm writing and recording an album for FAWM and the RPM Challenge. When I'm done, I'll have to post it to Bandcamp or some site like that. For this challenge, I'm using the same drum beat for each of the songs. So far, so good...none of the songs really sounds like the others.
Working on my latest project, BicycleErie. This will have maps of bicycle friendly routes, bike racks, and other such things. Let's hope this one takes off!
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.
}
Ghofulpo.com
Bike to Work Week
Friends
Peter PoP
Sneakatron
Latest Posts
- Coverville - Violent Femmes album
- RPM Challenge - Completed!
- February Album Writing Month / RPM Challenge
- BicycleErie
- Arduino music
- Arduino Bike Computer
- 2011 Bike to Work Roundup
- Tropospheric ducting - part 3
- Ubuntu 11.10
- The yearly playing with the blog
Find Stuff
By Date, tags, etc.
Commands
Home
RSS
Index
Login
