About
Ringtones is an interactive installation piece that generates algorithmic music based on any ten digit number dialed by a participant. Ten digits equates to ten billion unique phone numbers which means the phone is capable of creating ten billion unique pieces of music. In order to achieve this the phone's original circuitry was removed and a microcontroller was grafted into useful parts such as the keypad, hook, indicator light, and speaker. Basically, the microcontroller's program senses the state of the hook, waits for a phone number, then configures and plays the synthesis algorithm.
Ringtones has been exhibited at the 2008 Los Angeles Bent Festival, the 2008 Sound in Space Festival, the 2009 UCSD Spring Music Festival, the 2012 Art of Sound Show at Space4Art Gallery in San Diego, and the San Diego Art Institute Winter 2014 Invitational Show.
Here is a webpage describing the 2014 winter invitational show at the San Diego Art Institute.
Photos
Code
//-----------------------------------------------------------------------------
// Ringtones.cpp
//
// Arduino code to read a telephone keypad and perform algorithmic synthesis
//
// Cooper Baker (c) 2008
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <WProgram.h>
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void setup( void );
void loop( void );
void composer( void );
void song( void );
void poll_keys( void );
void dial_tone( void );
void key_tone( float hertz, byte key_num );
void tone( float hertz, float msec );
void rest( float msec );
void notewrap( int *note );
void freqwrap( float *freq );
void polytonic_scale( int note, int semitones, int iterations, float tone_msec, float pause_msec );
void freq_scale( float freq, float freq_inc, int iterations, float tone_msec );
void rand_freqs( float bot_freq, float top_freq, int iterations, float min_msec, float max_msec );
void bounce_up( float freq, float initial_msec_length, float decrement_coeff );
void bounce_down( float freq, float initial_msec_length, float decrement_coeff );
void dyad_scale( int note, int interval, int scale_interval, int iterations, float msec_length );
void freq_mirror( float freq, float initial_msec_length, float decrement_coeff );
void num_melody( float tone_msec, float rest_msec, int octaves );
//-----------------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------------
// These variables configure the i/o pins
byte key_pin[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte HOOK = 11;
byte SPEAKER = 12;
byte LED = 13;
// This array holds the state of each key
byte key_state[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// More state variables below
byte on_hook = 0;
byte speaker_state = 0;
byte led_state = 0;
// Phone number variables
byte num[] = { 0,0,0, 0,0,0, 0,0,0,0 };
byte num_index = 0;
// These variables are for the oscillator
double half_cyc_uc = 0;
unsigned int cycles_2x = 0;
// This array contains the keypad key frequencies
float dtmf[] = { 569.25, 476.5, 508.25, 536.75, 494.75, 526.5, 561.75, 515.25, 547, 582.25 };
// This array defines a hertz value for each midi note
float midi_note[] = { 8.175799, 8.661957, 9.177024, 9.722718, 10.3, 10.913383, 11.562325, 12.25, 12.978271, 13.75, 14.567617, 15.433853, 16.351599, 17.323914, 18.354048, 19.445436, 20.601723, 21.826765, 23.124651, 24.5, 25.956543, 27.5, 29.135235, 30.867706, 32.703197, 34.647827, 36.708096, 38.890873, 41.203445, 43.65353, 46.249302, 49., 51.913086, 55., 58.27047, 61.735413, 65.406395, 69.295654, 73.416191, 77.781746, 82.406891, 87.30706, 92.498604, 97.998856, 103.826172, 110., 116.540939, 123.470825, 130.81279, 138.591309, 146.832382, 155.563492, 164.813782, 174.61412, 184.997208, 195.997711, 207.652344, 220., 233.081879, 246.94165, 261.62558, 277.182617, 293.664764, 311.126984, 329.627563, 349.228241, 369.994415, 391.995422, 415.304688, 440., 466.163757, 493.883301, 523.25116, 554.365234, 587.329529, 622.253967, 659.255127, 698.456482, 739.988831, 783.990845, 830.609375, 880., 932.327515, 987.766602, 1046.502319, 1108.730469, 1174.659058, 1244.507935, 1318.510254, 1396.912964, 1479.977661, 1567.981689, 1661.21875, 1760., 1864.655029, 1975.533203, 2093.004639, 2217.460938, 2349.318115, 2489.015869, 2637.020508, 2793.825928, 2959.955322, 3135.963379, 3322.4375, 3520., 3729.31, 3951.066406, 4186.009277, 4434.921875, 4698.63623, 4978.031738, 5274.041016, 5587.651855, 5919.910645, 6271.926758, 6644.875, 7040., 7458.620117, 7902.132812, 8372.018555, 8869.84375, 9397.272461, 9956.063477, 10548.082031, 11175.303711, 11839.821289, 12543.853516 };
// These variables keep track of elapsed milliseconds
unsigned long msec[] = { 0, 0 };
int elapsed = 0;
//-----------------------------------------------------------------------------
// setup - sets up the arduino environment
//-----------------------------------------------------------------------------
void setup( void )
{
int i;
// sets up key pins as inputs and turns on pull-up resistors
for( i = 0 ; i < 10 ; i++ )
{
pinMode( key_pin[ i ], INPUT );
digitalWrite( key_pin[ i ], HIGH );
}
// set hook pin to input and turn on pull-up resistor
pinMode( HOOK, INPUT );
digitalWrite( HOOK, HIGH );
// set up and init output pins
pinMode( SPEAKER, OUTPUT );
pinMode( LED, OUTPUT );
digitalWrite( SPEAKER, LOW );
digitalWrite( LED, LOW );
//play a 'ready' tone
for( i = 5000 ; i > 2000 ; i -= 333 )
tone( i, 7 );
for( i = 2000 ; i < 5000 ; i += 333 )
tone( i, 7 );
}
//-----------------------------------------------------------------------------
// loop - the main program loop
//-----------------------------------------------------------------------------
void loop( void )
{
// this keeps track of millisecond values
msec[ 1 ] = msec[ 0 ];
msec[ 0 ] = millis();
// this block blinks the led every five seconds
if( msec[ 0 ] != msec[ 1 ] )
{
elapsed++;
if( elapsed > 3933 )
{
elapsed = 0;
digitalWrite( LED, 1 );
delay( 66 );
digitalWrite( LED, 0 );
}
}
// poll the hook pin
on_hook = digitalRead( HOOK );
while( !on_hook )
{
// reset phone number memory
num_index = 0;
// acquire a ten digit phone number
while( num_index < 10 && !on_hook )
{
poll_keys();
on_hook = digitalRead( HOOK );
if( num_index < 1 )
dial_tone();
}
// look for specific numbers
if( num[0]== 0 &&num[1]== 0 &&num[2]== 0
&&num[3]== 0 &&num[4]== 0 &&num[5]== 0
&&num[6]== 0 &&num[7]== 0 &&num[8]== 0 &&num[9]== 0 )
{
song();
}
else
{
rest( 500 );
composer();
rest( 1500 );
}
// poll the hook pin
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// song - calls all of the sound generating functionss
//-----------------------------------------------------------------------------
void song( void )
{
bounce_up( 100, 100, 0.99);
}
void composer( void )
{
// increment and index variables
byte i, j;
byte a, b, c, d, e;
// this for loop acts as an index that increments through the phone number
for( i = 0 ; i < 10 ; i++ )
{
// this block makes alternate indicies to get different parts of the phone number
a = i + 1; if( a > 9 ) a -= 9;
b = i + 2; if( b > 9 ) b -= 9;
c = i + 3; if( c > 9 ) c -= 9;
d = i + 4; if( d > 9 ) d -= 9;
e = i + 5; if( e > 9 ) e -= 9;
// this for loop plays groups of different sounds based on the phone number
for( j = 0 ; j <= num[ a ] ; j += 3 )
{
// this switch chooses a series of sounds to play based on the current number
switch( num[ i ] )
{
default:
case 0 : num_melody( ( num[ a ] + 4 ), ( num[ b ] + 1 ) * 2, num[ c ] * 0.5 );
break;
case 1 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 12, (float)( num[ c ] + 40 ) / 50 );
bounce_down( ( num[ a ] + 1 ) * 1000, ( num[ b ] + 1 ) * 8, (float)( num[ c ] + 20 ) / 30 );
break;
case 2 : bounce_down( ( num[ b ] + 1 ) * 1000, ( num[ c ] + 1 ) * 8, (float)( num[ d ] + 1 ) / 20 );
polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 3, ( num[ e ] + 1 ) * 20 );
freq_mirror( ( num[ a ] + 1 ) * 150, ( num[ b ] + 1 ) * 7, (float)( num[ c ] + 20 ) / 30 );
break;
case 3 : polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 4, ( num[ e ] + 1 ) * 25 );
freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
break;
case 4 : freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
dyad_scale( ( num[ b ] + 1 ) * 2 + 48, num[ c ] * 2 - 9, num[ d ] * 2 - 7, num[ e ] + 4, ( num[ a ] + 1 ) * 5 );
polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 3, ( num[ e ] + 1 ) * 20 );
break;
case 5 : freq_scale( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 3, ( num[ c ] + 1 ) * 10, ( num[ d ] + 1 ) * 3 );
dyad_scale( ( num[ b ] + 1 ) * 2 + 48, num[ c ] * 2 - 9, num[ d ] * 2 - 7, num[ e ] + 4, ( num[ a ] + 1 ) * 5 );
rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 400, ( num[ c ] + 1 ) * 2, ( num[ c ] + 5 ) * 3, ( num[ d ] + 5 ) * 6 );
break;
case 6 : freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
polytonic_scale( num[ b ] + 32, num[ c ] - 4, num[ d ] + 11, ( num[ e ] + 1 ) * 3, ( num[ a ] + 1 ) * 20 );
dyad_scale( ( num[ a ] + 1 ) * 2 + 24, num[ b ] * 2 - 9, num[ c ] * 2 - 7, num[ d ] + 4, ( num[ e ] + 1 ) * 5 );
break;
case 7 : rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 100, ( num[ c ] + 1 ) * 2, ( num[ c ] + 10 ) * 2, ( num[ d ] + 3 ) * 6 );
rand_freqs( ( num[ b ] + 1 ) * 50, ( num[ c ] + 1 ) * 250, ( num[ d ] + 1 ) * 2, ( num[ e ] + 10 ) * 2, ( num[ a ] + 3 ) * 6 );
break;
case 8 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 3, (float)( num[ c ] + 20 ) / 30 );
freq_mirror( ( num[ a ] + 1 ) * 150, ( num[ b ] + 1 ) * 5, (float)( num[ c ] + 40 ) / 50 );
bounce_down( (num[ a ] + 1 ) * 1000, ( num[ b ] + 1 ) * 3, (float)( num[ c ] + 20 ) / 30 );
break;
case 9 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 7, (float)( num[ c ] + 15 ) / 30 );
rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 300, ( num[ c ] + 1 ) * 2, ( num[ c ] + 10 ) * 2, ( num[ d ] + 3 ) * 6 );
break;
}
}
}
}
//-----------------------------------------------------------------------------
// poll_keys - looks for a pressed key
//-----------------------------------------------------------------------------
void poll_keys( void )
{
byte i = 0;
// this loop looks at each key
for( i = 0 ; i < 10 ; i++ )
{
// if a key is pressed
if( !digitalRead( key_pin[ i ] ) )
{
// debounce the switch
delay( 50 );
// play a key tone
key_tone( dtmf[ i ], i );
// store the key number
num[ num_index ] = i;
num_index++;
break;
}
}
}
//-----------------------------------------------------------------------------
// key_tone - plays a tone while a key is depressed
//-----------------------------------------------------------------------------
void key_tone( float hertz, byte key_num )
{
// calculate microseconds per half wave cycle
half_cyc_uc = ( 1000000. / hertz ) * 0.5;
// turn on the led
digitalWrite( LED, 1 );
// generate a tone while the key is depressed
while( !digitalRead( key_pin[ key_num ] ) )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
}
// turn off the led
digitalWrite( LED, 0 );
}
//-----------------------------------------------------------------------------
// tone - plays a tone of arbitrary length
//-----------------------------------------------------------------------------
void tone( float hertz, float msec )
{
// calculate microseconds per half cycle of waveform
half_cyc_uc = ( 1000000. / hertz ) * 0.5;
// calculate number of half cycles to output
cycles_2x = ( msec * 1000. ) / half_cyc_uc;
// turn on the led
digitalWrite( LED, 1 );
// generate the waveform
while( cycles_2x-- )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
// poll the hook pin
on_hook = digitalRead( HOOK );
}
// turn off the led
digitalWrite( LED, 0 );
}
//-----------------------------------------------------------------------------
// dial_tone - plays the dial_tone
//-----------------------------------------------------------------------------
void dial_tone( void )
{
float frequency = 371.0;
// calculate microseconds per half cycle of waveform
half_cyc_uc = ( 1000000. / frequency ) * 0.5;
// calculate number of half cycles to output
cycles_2x = ( 2 * 1000. ) / half_cyc_uc;
// generate the waveform
while( cycles_2x-- )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
// poll the hook pin
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// rest - plays a rest of arbitrary length
//-----------------------------------------------------------------------------
void rest( float msec )
{
// calculate length of silence
half_cyc_uc = ( 1000000. / 100 ) * 0.5;
cycles_2x = ( msec * 1000. ) / half_cyc_uc;
// rest for a bit
while( cycles_2x-- )
{
if( on_hook )
break;
delayMicroseconds( (unsigned int)half_cyc_uc );
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// notewrap - wraps midi notes into midi range
//-----------------------------------------------------------------------------
void notewrap( int *note )
{
if( *note > 127 ) *note -= 127;
if( *note < 0 ) *note += 127;
}
//-----------------------------------------------------------------------------
// freqwrap - wraps frequencies into human hearing range
//-----------------------------------------------------------------------------
void freqwrap( float *freq )
{
if( *freq > 20000 ) *freq -= 19980;
if( *freq < 20 ) *freq += 19980;
}
//-----------------------------------------------------------------------------
// polytonic_scale - a polytonic scale of intervals
//-----------------------------------------------------------------------------
void polytonic_scale( int note, int semitones, int iterations, float tone_msec, float pause_msec )
{
int i = 0;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( midi_note[ note ], tone_msec );
note += semitones;
notewrap( ¬e );
delayMicroseconds( (unsigned int)(pause_msec * 1000.) );
}
}
//-----------------------------------------------------------------------------
// freq_scale - makes a linear set of incrementing frequencies
//-----------------------------------------------------------------------------
void freq_scale( float freq, float freq_inc, int iterations, float tone_msec )
{
int i = 0;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( freq, tone_msec );
freq += freq_inc;
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// rand_freqs - makes random frequencies in a specified range
//-----------------------------------------------------------------------------
void rand_freqs( float bot_freq, float top_freq, int iterations, float min_msec, float max_msec )
{
int i = 0;
float freq_range = top_freq - bot_freq;
float msec_range = max_msec - min_msec;
float duration;
long int factorial = 1;
for( i = 0 ; i < 10 ; i++ )
{
factorial *= num[i];
}
randomSeed( factorial );
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
duration = random( 999 ) * 0.001 * msec_range + min_msec;
tone( random( (int)freq_range ) + bot_freq , duration );
duration = random( 999 ) * 0.001 * msec_range + min_msec;
rest( duration );
}
}
//-----------------------------------------------------------------------------
// dyad_scale - plays an ascending or descending scale of alternading dyads
//-----------------------------------------------------------------------------
void dyad_scale( int note, int interval, int scale_interval, int iterations, float msec_length )
{
int i;
int dyad;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( midi_note[ note ], msec_length );
rest( msec_length * 0.5 );
dyad = note + interval;
notewrap( &dyad );
tone( midi_note[ dyad ], msec_length );
rest( msec_length * 0.5 );
note += scale_interval;
notewrap( ¬e );
}
}
//-----------------------------------------------------------------------------
// bounce_up - increasingly shorter ascending tones tones with rests
//-----------------------------------------------------------------------------
void bounce_up( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= 1 + ( 1 - decrement_coeff );
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// boune_down - increasingly shorter descending tones tones with rests
//-----------------------------------------------------------------------------
void bounce_down( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= decrement_coeff;
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// freq_mirror - frequencies moving apart from the initial frequency
//-----------------------------------------------------------------------------
void freq_mirror( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
float mirror_freq = freq;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= 1 + ( 1 - decrement_coeff );
mirror_freq *= decrement_coeff;
freqwrap( &freq );
freqwrap( &mirror_freq );
tone( mirror_freq, i );
rest( i );
}
}
//-----------------------------------------------------------------------------
// num_melody - plays a melody based on the dialed phone number
//-----------------------------------------------------------------------------
void num_melody( float tone_msec, float rest_msec, int octaves )
{
byte i, j;
byte duration[] = { 12, 8, 16, 6, 12, 4, 16, 6, 12, 8 };
int note;
for( i = 0 ; i < octaves ; i++ )
{
for( j = 0 ; j < 10 ; j++ )
{
if( on_hook )
return;
note = num[ j ] + 40 + ( 12 * i );
notewrap( ¬e );
tone( midi_note[ note ], tone_msec * duration[ num[ j ] ] );
rest( rest_msec * duration[ 9 - num[ j ] ] );
}
}
}
//-----------------------------------------------------------------------------
// main - entry point for this program
//-----------------------------------------------------------------------------
int main( void )
{
// init the arduino environment
init();
// program-specific setup
setup();
// infinite loop
spin:
loop();
goto spin;
return 0;
}
//-----------------------------------------------------------------------------
// EOF
//-----------------------------------------------------------------------------
// Ringtones.cpp
//
// Arduino code to read a telephone keypad and perform algorithmic synthesis
//
// Cooper Baker (c) 2008
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <WProgram.h>
//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
void setup( void );
void loop( void );
void composer( void );
void song( void );
void poll_keys( void );
void dial_tone( void );
void key_tone( float hertz, byte key_num );
void tone( float hertz, float msec );
void rest( float msec );
void notewrap( int *note );
void freqwrap( float *freq );
void polytonic_scale( int note, int semitones, int iterations, float tone_msec, float pause_msec );
void freq_scale( float freq, float freq_inc, int iterations, float tone_msec );
void rand_freqs( float bot_freq, float top_freq, int iterations, float min_msec, float max_msec );
void bounce_up( float freq, float initial_msec_length, float decrement_coeff );
void bounce_down( float freq, float initial_msec_length, float decrement_coeff );
void dyad_scale( int note, int interval, int scale_interval, int iterations, float msec_length );
void freq_mirror( float freq, float initial_msec_length, float decrement_coeff );
void num_melody( float tone_msec, float rest_msec, int octaves );
//-----------------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------------
// These variables configure the i/o pins
byte key_pin[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
byte HOOK = 11;
byte SPEAKER = 12;
byte LED = 13;
// This array holds the state of each key
byte key_state[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// More state variables below
byte on_hook = 0;
byte speaker_state = 0;
byte led_state = 0;
// Phone number variables
byte num[] = { 0,0,0, 0,0,0, 0,0,0,0 };
byte num_index = 0;
// These variables are for the oscillator
double half_cyc_uc = 0;
unsigned int cycles_2x = 0;
// This array contains the keypad key frequencies
float dtmf[] = { 569.25, 476.5, 508.25, 536.75, 494.75, 526.5, 561.75, 515.25, 547, 582.25 };
// This array defines a hertz value for each midi note
float midi_note[] = { 8.175799, 8.661957, 9.177024, 9.722718, 10.3, 10.913383, 11.562325, 12.25, 12.978271, 13.75, 14.567617, 15.433853, 16.351599, 17.323914, 18.354048, 19.445436, 20.601723, 21.826765, 23.124651, 24.5, 25.956543, 27.5, 29.135235, 30.867706, 32.703197, 34.647827, 36.708096, 38.890873, 41.203445, 43.65353, 46.249302, 49., 51.913086, 55., 58.27047, 61.735413, 65.406395, 69.295654, 73.416191, 77.781746, 82.406891, 87.30706, 92.498604, 97.998856, 103.826172, 110., 116.540939, 123.470825, 130.81279, 138.591309, 146.832382, 155.563492, 164.813782, 174.61412, 184.997208, 195.997711, 207.652344, 220., 233.081879, 246.94165, 261.62558, 277.182617, 293.664764, 311.126984, 329.627563, 349.228241, 369.994415, 391.995422, 415.304688, 440., 466.163757, 493.883301, 523.25116, 554.365234, 587.329529, 622.253967, 659.255127, 698.456482, 739.988831, 783.990845, 830.609375, 880., 932.327515, 987.766602, 1046.502319, 1108.730469, 1174.659058, 1244.507935, 1318.510254, 1396.912964, 1479.977661, 1567.981689, 1661.21875, 1760., 1864.655029, 1975.533203, 2093.004639, 2217.460938, 2349.318115, 2489.015869, 2637.020508, 2793.825928, 2959.955322, 3135.963379, 3322.4375, 3520., 3729.31, 3951.066406, 4186.009277, 4434.921875, 4698.63623, 4978.031738, 5274.041016, 5587.651855, 5919.910645, 6271.926758, 6644.875, 7040., 7458.620117, 7902.132812, 8372.018555, 8869.84375, 9397.272461, 9956.063477, 10548.082031, 11175.303711, 11839.821289, 12543.853516 };
// These variables keep track of elapsed milliseconds
unsigned long msec[] = { 0, 0 };
int elapsed = 0;
//-----------------------------------------------------------------------------
// setup - sets up the arduino environment
//-----------------------------------------------------------------------------
void setup( void )
{
int i;
// sets up key pins as inputs and turns on pull-up resistors
for( i = 0 ; i < 10 ; i++ )
{
pinMode( key_pin[ i ], INPUT );
digitalWrite( key_pin[ i ], HIGH );
}
// set hook pin to input and turn on pull-up resistor
pinMode( HOOK, INPUT );
digitalWrite( HOOK, HIGH );
// set up and init output pins
pinMode( SPEAKER, OUTPUT );
pinMode( LED, OUTPUT );
digitalWrite( SPEAKER, LOW );
digitalWrite( LED, LOW );
//play a 'ready' tone
for( i = 5000 ; i > 2000 ; i -= 333 )
tone( i, 7 );
for( i = 2000 ; i < 5000 ; i += 333 )
tone( i, 7 );
}
//-----------------------------------------------------------------------------
// loop - the main program loop
//-----------------------------------------------------------------------------
void loop( void )
{
// this keeps track of millisecond values
msec[ 1 ] = msec[ 0 ];
msec[ 0 ] = millis();
// this block blinks the led every five seconds
if( msec[ 0 ] != msec[ 1 ] )
{
elapsed++;
if( elapsed > 3933 )
{
elapsed = 0;
digitalWrite( LED, 1 );
delay( 66 );
digitalWrite( LED, 0 );
}
}
// poll the hook pin
on_hook = digitalRead( HOOK );
while( !on_hook )
{
// reset phone number memory
num_index = 0;
// acquire a ten digit phone number
while( num_index < 10 && !on_hook )
{
poll_keys();
on_hook = digitalRead( HOOK );
if( num_index < 1 )
dial_tone();
}
// look for specific numbers
if( num[0]== 0 &&num[1]== 0 &&num[2]== 0
&&num[3]== 0 &&num[4]== 0 &&num[5]== 0
&&num[6]== 0 &&num[7]== 0 &&num[8]== 0 &&num[9]== 0 )
{
song();
}
else
{
rest( 500 );
composer();
rest( 1500 );
}
// poll the hook pin
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// song - calls all of the sound generating functionss
//-----------------------------------------------------------------------------
void song( void )
{
bounce_up( 100, 100, 0.99);
}
void composer( void )
{
// increment and index variables
byte i, j;
byte a, b, c, d, e;
// this for loop acts as an index that increments through the phone number
for( i = 0 ; i < 10 ; i++ )
{
// this block makes alternate indicies to get different parts of the phone number
a = i + 1; if( a > 9 ) a -= 9;
b = i + 2; if( b > 9 ) b -= 9;
c = i + 3; if( c > 9 ) c -= 9;
d = i + 4; if( d > 9 ) d -= 9;
e = i + 5; if( e > 9 ) e -= 9;
// this for loop plays groups of different sounds based on the phone number
for( j = 0 ; j <= num[ a ] ; j += 3 )
{
// this switch chooses a series of sounds to play based on the current number
switch( num[ i ] )
{
default:
case 0 : num_melody( ( num[ a ] + 4 ), ( num[ b ] + 1 ) * 2, num[ c ] * 0.5 );
break;
case 1 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 12, (float)( num[ c ] + 40 ) / 50 );
bounce_down( ( num[ a ] + 1 ) * 1000, ( num[ b ] + 1 ) * 8, (float)( num[ c ] + 20 ) / 30 );
break;
case 2 : bounce_down( ( num[ b ] + 1 ) * 1000, ( num[ c ] + 1 ) * 8, (float)( num[ d ] + 1 ) / 20 );
polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 3, ( num[ e ] + 1 ) * 20 );
freq_mirror( ( num[ a ] + 1 ) * 150, ( num[ b ] + 1 ) * 7, (float)( num[ c ] + 20 ) / 30 );
break;
case 3 : polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 4, ( num[ e ] + 1 ) * 25 );
freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
break;
case 4 : freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
dyad_scale( ( num[ b ] + 1 ) * 2 + 48, num[ c ] * 2 - 9, num[ d ] * 2 - 7, num[ e ] + 4, ( num[ a ] + 1 ) * 5 );
polytonic_scale( num[ a ] + 48, num[ b ] - 4, num[ c ] + 10, ( num[ d ] + 1 ) * 3, ( num[ e ] + 1 ) * 20 );
break;
case 5 : freq_scale( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 3, ( num[ c ] + 1 ) * 10, ( num[ d ] + 1 ) * 3 );
dyad_scale( ( num[ b ] + 1 ) * 2 + 48, num[ c ] * 2 - 9, num[ d ] * 2 - 7, num[ e ] + 4, ( num[ a ] + 1 ) * 5 );
rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 400, ( num[ c ] + 1 ) * 2, ( num[ c ] + 5 ) * 3, ( num[ d ] + 5 ) * 6 );
break;
case 6 : freq_mirror( ( num[ b ] + 1 ) * 150, ( num[ c ] + 1 ) * 5, (float)( num[ d ] + 20 ) / 30 );
polytonic_scale( num[ b ] + 32, num[ c ] - 4, num[ d ] + 11, ( num[ e ] + 1 ) * 3, ( num[ a ] + 1 ) * 20 );
dyad_scale( ( num[ a ] + 1 ) * 2 + 24, num[ b ] * 2 - 9, num[ c ] * 2 - 7, num[ d ] + 4, ( num[ e ] + 1 ) * 5 );
break;
case 7 : rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 100, ( num[ c ] + 1 ) * 2, ( num[ c ] + 10 ) * 2, ( num[ d ] + 3 ) * 6 );
rand_freqs( ( num[ b ] + 1 ) * 50, ( num[ c ] + 1 ) * 250, ( num[ d ] + 1 ) * 2, ( num[ e ] + 10 ) * 2, ( num[ a ] + 3 ) * 6 );
break;
case 8 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 3, (float)( num[ c ] + 20 ) / 30 );
freq_mirror( ( num[ a ] + 1 ) * 150, ( num[ b ] + 1 ) * 5, (float)( num[ c ] + 40 ) / 50 );
bounce_down( (num[ a ] + 1 ) * 1000, ( num[ b ] + 1 ) * 3, (float)( num[ c ] + 20 ) / 30 );
break;
case 9 : bounce_up( ( num[ a ] + 1 ) * 100, ( num[ b ] + 1 ) * 7, (float)( num[ c ] + 15 ) / 30 );
rand_freqs( ( num[ a ] + 1 ) * 50, ( num[ b ] + 1 ) * 300, ( num[ c ] + 1 ) * 2, ( num[ c ] + 10 ) * 2, ( num[ d ] + 3 ) * 6 );
break;
}
}
}
}
//-----------------------------------------------------------------------------
// poll_keys - looks for a pressed key
//-----------------------------------------------------------------------------
void poll_keys( void )
{
byte i = 0;
// this loop looks at each key
for( i = 0 ; i < 10 ; i++ )
{
// if a key is pressed
if( !digitalRead( key_pin[ i ] ) )
{
// debounce the switch
delay( 50 );
// play a key tone
key_tone( dtmf[ i ], i );
// store the key number
num[ num_index ] = i;
num_index++;
break;
}
}
}
//-----------------------------------------------------------------------------
// key_tone - plays a tone while a key is depressed
//-----------------------------------------------------------------------------
void key_tone( float hertz, byte key_num )
{
// calculate microseconds per half wave cycle
half_cyc_uc = ( 1000000. / hertz ) * 0.5;
// turn on the led
digitalWrite( LED, 1 );
// generate a tone while the key is depressed
while( !digitalRead( key_pin[ key_num ] ) )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
}
// turn off the led
digitalWrite( LED, 0 );
}
//-----------------------------------------------------------------------------
// tone - plays a tone of arbitrary length
//-----------------------------------------------------------------------------
void tone( float hertz, float msec )
{
// calculate microseconds per half cycle of waveform
half_cyc_uc = ( 1000000. / hertz ) * 0.5;
// calculate number of half cycles to output
cycles_2x = ( msec * 1000. ) / half_cyc_uc;
// turn on the led
digitalWrite( LED, 1 );
// generate the waveform
while( cycles_2x-- )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
// poll the hook pin
on_hook = digitalRead( HOOK );
}
// turn off the led
digitalWrite( LED, 0 );
}
//-----------------------------------------------------------------------------
// dial_tone - plays the dial_tone
//-----------------------------------------------------------------------------
void dial_tone( void )
{
float frequency = 371.0;
// calculate microseconds per half cycle of waveform
half_cyc_uc = ( 1000000. / frequency ) * 0.5;
// calculate number of half cycles to output
cycles_2x = ( 2 * 1000. ) / half_cyc_uc;
// generate the waveform
while( cycles_2x-- )
{
digitalWrite( SPEAKER, speaker_state );
speaker_state = !speaker_state;
delayMicroseconds( (unsigned int)half_cyc_uc );
// poll the hook pin
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// rest - plays a rest of arbitrary length
//-----------------------------------------------------------------------------
void rest( float msec )
{
// calculate length of silence
half_cyc_uc = ( 1000000. / 100 ) * 0.5;
cycles_2x = ( msec * 1000. ) / half_cyc_uc;
// rest for a bit
while( cycles_2x-- )
{
if( on_hook )
break;
delayMicroseconds( (unsigned int)half_cyc_uc );
on_hook = digitalRead( HOOK );
}
}
//-----------------------------------------------------------------------------
// notewrap - wraps midi notes into midi range
//-----------------------------------------------------------------------------
void notewrap( int *note )
{
if( *note > 127 ) *note -= 127;
if( *note < 0 ) *note += 127;
}
//-----------------------------------------------------------------------------
// freqwrap - wraps frequencies into human hearing range
//-----------------------------------------------------------------------------
void freqwrap( float *freq )
{
if( *freq > 20000 ) *freq -= 19980;
if( *freq < 20 ) *freq += 19980;
}
//-----------------------------------------------------------------------------
// polytonic_scale - a polytonic scale of intervals
//-----------------------------------------------------------------------------
void polytonic_scale( int note, int semitones, int iterations, float tone_msec, float pause_msec )
{
int i = 0;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( midi_note[ note ], tone_msec );
note += semitones;
notewrap( ¬e );
delayMicroseconds( (unsigned int)(pause_msec * 1000.) );
}
}
//-----------------------------------------------------------------------------
// freq_scale - makes a linear set of incrementing frequencies
//-----------------------------------------------------------------------------
void freq_scale( float freq, float freq_inc, int iterations, float tone_msec )
{
int i = 0;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( freq, tone_msec );
freq += freq_inc;
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// rand_freqs - makes random frequencies in a specified range
//-----------------------------------------------------------------------------
void rand_freqs( float bot_freq, float top_freq, int iterations, float min_msec, float max_msec )
{
int i = 0;
float freq_range = top_freq - bot_freq;
float msec_range = max_msec - min_msec;
float duration;
long int factorial = 1;
for( i = 0 ; i < 10 ; i++ )
{
factorial *= num[i];
}
randomSeed( factorial );
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
duration = random( 999 ) * 0.001 * msec_range + min_msec;
tone( random( (int)freq_range ) + bot_freq , duration );
duration = random( 999 ) * 0.001 * msec_range + min_msec;
rest( duration );
}
}
//-----------------------------------------------------------------------------
// dyad_scale - plays an ascending or descending scale of alternading dyads
//-----------------------------------------------------------------------------
void dyad_scale( int note, int interval, int scale_interval, int iterations, float msec_length )
{
int i;
int dyad;
for( i = 0 ; i < iterations ; i++ )
{
if( on_hook )
return;
tone( midi_note[ note ], msec_length );
rest( msec_length * 0.5 );
dyad = note + interval;
notewrap( &dyad );
tone( midi_note[ dyad ], msec_length );
rest( msec_length * 0.5 );
note += scale_interval;
notewrap( ¬e );
}
}
//-----------------------------------------------------------------------------
// bounce_up - increasingly shorter ascending tones tones with rests
//-----------------------------------------------------------------------------
void bounce_up( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= 1 + ( 1 - decrement_coeff );
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// boune_down - increasingly shorter descending tones tones with rests
//-----------------------------------------------------------------------------
void bounce_down( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= decrement_coeff;
freqwrap( &freq );
}
}
//-----------------------------------------------------------------------------
// freq_mirror - frequencies moving apart from the initial frequency
//-----------------------------------------------------------------------------
void freq_mirror( float freq, float initial_msec_length, float decrement_coeff )
{
float i;
float mirror_freq = freq;
for( i = initial_msec_length ; i > 1 ; i = i * decrement_coeff )
{
if( on_hook )
return;
tone( freq, i );
rest( i );
freq *= 1 + ( 1 - decrement_coeff );
mirror_freq *= decrement_coeff;
freqwrap( &freq );
freqwrap( &mirror_freq );
tone( mirror_freq, i );
rest( i );
}
}
//-----------------------------------------------------------------------------
// num_melody - plays a melody based on the dialed phone number
//-----------------------------------------------------------------------------
void num_melody( float tone_msec, float rest_msec, int octaves )
{
byte i, j;
byte duration[] = { 12, 8, 16, 6, 12, 4, 16, 6, 12, 8 };
int note;
for( i = 0 ; i < octaves ; i++ )
{
for( j = 0 ; j < 10 ; j++ )
{
if( on_hook )
return;
note = num[ j ] + 40 + ( 12 * i );
notewrap( ¬e );
tone( midi_note[ note ], tone_msec * duration[ num[ j ] ] );
rest( rest_msec * duration[ 9 - num[ j ] ] );
}
}
}
//-----------------------------------------------------------------------------
// main - entry point for this program
//-----------------------------------------------------------------------------
int main( void )
{
// init the arduino environment
init();
// program-specific setup
setup();
// infinite loop
spin:
loop();
goto spin;
return 0;
}
//-----------------------------------------------------------------------------
// EOF
//-----------------------------------------------------------------------------