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( &note );
        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( &note );
    }  
}


//-----------------------------------------------------------------------------
// 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( &note );
            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
//-----------------------------------------------------------------------------