This project was our version of a theremin. It was created by using a Leap Motion controller to read in the number of fingers over the sensor. This data was sent to the k64f for translation into musical notes and then sent to a speaker. Features include playing hard coded songs such as Ode to Joy, recording and playing your own music with a SD card, and changing octaves at any time.

Dependencies:   SDFileSystem mbed

Additional project materials:

  • C# code written to read and send sensor inputs
  • Alternate version of our code
  • Project report explaining the entire project
  • Demonstration video

/media/uploads/mvanderpohl/project_materials.zip

main.cpp

Committer:
mvanderpohl
Date:
2016-04-30
Revision:
0:660f0e3ac256

File content as of revision 0:660f0e3ac256:

//libraries
#include "mbed.h"
#include "SDFileSystem.h"                  // SD File System functions

PwmOut tone(PTC2);              //create a PWM pin
Serial pc(USBTX,USBRX);         //create a COM port
SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); //MOSI, MISO, clk, CS
//switches
DigitalIn sw2(SW2);             //playback recording
DigitalIn sw4(PTD1);            //play a song                 
DigitalIn sw5(PTD3);            //start/end recording
DigitalIn sw6(PTD2);            //play a song
//LEDs
DigitalOut led1(LED_RED);
DigitalOut led2(LED_GREEN);
DigitalOut led3(LED_BLUE);
DigitalOut recordLight(PTA2);
//interrupts
InterruptIn Interrupt1(SW3);    //change the octave
//defines
#define MAXRECORDLENGTH 100
#define ODE2JOYLENGTH 62
#define JINGLELENGTH 51

//vars                              
int tonelevel = 4;                          //Default octave 4
int mode = 0;                               //mode=0 play //mode=1 record
int recordLength = 0;                       //keep track of the actual length vs. the max length
float record[MAXRECORDLENGTH];              //array for storing recorded tone

//determine which song is playing
bool jingleSet = false;
bool odeSet = false;

 //####################################################################################################### 
float octave[7][12]={
    {55.0,62.0,33.0,37.0,41.0,44.0,49.0,58.0,35.0,39.0,46.0,52.0},                          //1
    {110.0,123.0,65.0,73.0,82.0,87.0,98.0,117.0,69.0,78.0,93.0,104.0},                      //2
    {220.0,247.0,131.0,147.0,165.0,175.0,196.0,233.0,139.0,156.0,185.0,208.0},              //3
    {440.0,494.0,262.0,294.0,330.0,349.0,392.0,466.0,277.0,311.0,370.0,415.0},              //4
    {880.0,988.0,523.0,587.0,659.0,698.0,784.0,932.0,554.0,622.0,740.0,831.0},              //5
    {1760.0,1976.0,1047.0,1175.0,1319.0,1397.0,1568.0,1865.0,1109.0,1245.0,1480.0,1661.0},  //6
    {3520.0,3951.0,2093.0,2349.0,2637.0,2794.0,3136.0,3729.0,2217.0,2489.0,2960.0,3322.0}   //7    
    };
 //#######################################################################################################    
float starwars[]={octave[2][2],octave[2][6],octave[2][5],octave[2][4],octave[2][3],octave[3][2],
                  octave[2][2],octave[2][6],octave[2][5],octave[2][4],octave[2][3],octave[3][2],
                  octave[2][6],octave[2][5],octave[2][4],octave[2][5],octave[2][3]  
                };               
 //####################################################################################################### 
float ode2joy[]= {octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][5],octave[tonelevel][6],octave[tonelevel][6],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][3],
                   octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][5],octave[tonelevel][6],octave[tonelevel][6],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][2],
                   octave[tonelevel][3],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][2],
                   octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][5],octave[tonelevel][6],octave[tonelevel][6],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][2],octave[tonelevel][2]};
 //####################################################################################################### 
float jingle[]={octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][6],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],
                octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],
                octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][3],octave[tonelevel][4],octave[tonelevel][3],octave[tonelevel][6],
                octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][6],octave[tonelevel][2],octave[tonelevel][3],octave[tonelevel][4],
                octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][5],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],octave[tonelevel][4],
                octave[tonelevel][6],octave[tonelevel][6],octave[tonelevel][5],octave[tonelevel][3],octave[tonelevel][2]};
 //####################################################################################################### 
 int ode2joyNotes[] = {4,4,5,6,6,5,4,3,2,2,3,4,4,3,3,4,4,5,6,6,5,4,3,2,2,3,4,3,2,2,3,3,4,2,3,4,5,4,2,3,4,5,4,3,2,3,2,4,4,5,6,6,5,4,3,2,2,3,4,3,2,2};
 //####################################################################################################### 
 int jingleNotes[] =  {4,4,4,4,4,4,4,6,2,3,4,5,5,5,5,5,4,4,4,4,4,3,3,4,3,6,4,4,4,4,4,4,4,6,2,3,4,5,5,5,5,5,4,4,4,4,6,6,5,3,2};
 //#######################################################################################################  
int      tempo[]={600,400,400,400,500,600,400,400,400,500,600,400,400,400,400,400,1000};
 //####################################################################################################### 
int      tempo2[]={400,400,400,400,400,400,400,400,400,400,400,400,600,200,650,
              400,400,400,400,400,400,400,400,400,400,400,400,600,200,650,
              400,400,400,400,400,200,200,400,400,400,200,200,400,400,400,400,650,
              400,400,400,400,400,400,400,400,400,400,400,400,600,200,1000};   
 //#######################################################################################################             
int         tempo3[]={400,400,500,350,350,500,350,350,350,350,700,
              400,400,400,350,350,350,400,150,600,
              400,400,400,400,450,600,
              400,400,500,350,350,500,350,350,350,350,700,
              400,400,400,350,350,350,400,150,600,
              400,400,400,400,600};       
 //####################################################################################################### 

//function prototypes
void changeOctave(void);
bool writeSD(void);
bool readSD(void);
bool removeFile(void);
 
int main()
{
//INITIALIZATIONS
    tone = 0;                                     //volume (duty cycle)
    int recordCounter = 0;                       //used to count up to max number of notes
    
    led1 = 1;                                      //turn all LEDs off
    led2 = 1;
    led3 = 1;
    recordLight = 0;

    pc.baud(9600);                              //set baud
    
    Interrupt1.fall(&changeOctave);             //detect interrupt on falling edge and call ISR 
    
//BEGIN LOOP 
    while (true) 
    {       
        //CHECK SW4 -- determine if we should play this song
            if(sw4 == 1)
            {
                led3 = !led3;
                odeSet = true;
                for(int i=0;i<ODE2JOYLENGTH;i++)
                {
                    tone=0.5;
                    tone.period(1/ode2joy[i]);
                    wait_ms(tempo2[i]);
                }
                odeSet = false;
                tone=0;
            }//end sw4
            
            //CHECK SW6 --  determine if we should play this song
            if(sw6 == 1)
            {
                led2 = !led2;
                jingleSet = true;
                for(int i=0;i<JINGLELENGTH;i++)
                {
                    tone=0.5;
                    tone.period(1/jingle[i]);
                    wait_ms(tempo2[i]);
                }
                jingleSet = false;
                tone=0;
            }//end sw6
            
            //mode 0 just recieves a character from the COM port, looks up the corresponding node
            //and plays it over the speaker at a 50% duty cycle (loudest volume available)
            if(mode==0 && pc.readable())
            {
                switch(pc.getc())           //Note selection
                {
                    case 'a':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][0]);
                    break;
                    case 'b':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][1]);
                    break;
                    case 'c':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][2]);
                    break;
                    case 'd':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][3]);
                    break;
                    case 'e':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][4]);
                    break;
                    case 'f':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][5]);
                    break;
                    case 'g':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][6]);
                    break;
                    case 'h':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][7]);
                    break;
                    case 'i':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][8]);
                    break;
                    case 'j':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][9]);
                    break;
                    case 'k':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][10]);
                    break;
                    case 'l':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][11]);
                    break;
                    case 'm':
                    tone=0.5; 
                    tone.period(1/octave[tonelevel][11]);
                    break;
                    default:        //in the default case mute the sound
                    tone=0;
                }//end switch
            
            }//end if mode 0
            
            
            //CHECK SW5 -- Go to recording mode
            if(sw5 == 1)
            {
                while(sw5 == 1);
                mode = 1; 
                led1 = !led1;
                //reset length when we record a new song
                recordLength = 0;
                //delete the current recording before storing a new one
                removeFile();
            }
            
            
            //record notes and set mode back to 0
            if(mode==1)
            {
                tone=0.5;
                //play a tone to let user know they can record
                tone.period(1/octave[3][6]);
                wait_ms(500);
                tone.period(1/octave[5][6]);
                wait_ms(500);
                tone.period(1/octave[3][6]);
                wait_ms(500);
                tone.period(1/octave[5][6]);
                wait_ms(500);
                tone=0;
                
                //read until we have reached max record length
                while(recordCounter < MAXRECORDLENGTH )
                {
                    switch(pc.getc())           //Note selection
                    {
                        case 'a':
                        record[recordCounter]=octave[tonelevel][0];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][0]);
                        
                        break;
                        case 'b':
                        record[recordCounter]=octave[tonelevel][1];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][1]);
                        
                        break;
                        case 'c':
                        record[recordCounter]=octave[tonelevel][2];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][2]);
                        
                        break;
                        case 'd':
                        record[recordCounter]=octave[tonelevel][3];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][3]);
                        break;
                        case 'e':
                        record[recordCounter]=octave[tonelevel][4];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][4]);
                        
                        break;
                        case 'f':
                        record[recordCounter]=octave[tonelevel][5];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][5]);
                        
                        break;
                        case 'g':
                        record[recordCounter]=octave[tonelevel][6];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][6]);
                        
                        break;
                        case 'h':
                        record[recordCounter]=octave[tonelevel][7];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][7]);
                        
                        break;
                        case 'i':
                        record[recordCounter]=octave[tonelevel][8];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][8]);
                        
                        break;
                        case 'j':
                        record[recordCounter]=octave[tonelevel][9];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][9]);
                        
                        break;
                        case 'k':
                        record[recordCounter]=octave[tonelevel][10];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][10]);
                        tone=0;
                        break;
                        case 'l':
                        record[recordCounter]=octave[tonelevel][11];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][11]);
                        
                        break;
                        case 'm':
                        record[recordCounter]=octave[tonelevel][11];
                        tone=0.5; 
                        tone.period(1/octave[tonelevel][11]);
                        
                        break;
                        default:            //use a dummy note that will never get played in the default case
                        record[recordCounter]=octave[6][6];
                        tone=0;
                    }//end switch  
                    
                    recordCounter++;
                    recordLight = !recordLight;
                    
                    //CHECK SW5 -- start or end recording
                    if(sw5 == 1)
                    {
                        while(sw5 == 1);
                        mode=1; 
                        led1 = !led1;
                        recordLength = recordCounter;
                        break;
                    }
                    
            }//end while
            recordCounter = 0;
            mode = 0;
            recordLight = 0;
            
            //play a tone to let user know they are done
            tone=0.5;
            tone.period(1/octave[3][6]);
            wait_ms(500);
            tone.period(1/octave[5][6]);
            wait_ms(500);
            tone.period(1/octave[3][6]);
            wait_ms(500);
            tone.period(1/octave[5][6]);
            wait_ms(500);
            tone=0;
    //write to the SD card after recording
            writeSD();
            
        }//end if
            
        //play recored notes
        if(mode==0 && sw2==0)
        {
            if(readSD() == true)
            {
                tone = 0.5;
                for(int i=0;i<recordLength;i++)
                {
                    if(record[i]==octave[6][6])
                        tone=0;
                    else
                        tone=0.5;
                        
                    tone.period(1/record[i]);
                    //play the recording back a little faster than it was recorded
                    wait_ms(450);
                }
                led1 = !led1;
                tone=0;
            }
        }//end play
        led1 = !led1;
        wait_ms(100);
        
    }//end main while loop
        
}//main closes
    
//used to change the current octave even in the middle of playing a preset song but
//has no effect during playback of recorded music
void changeOctave()
{
    tonelevel+=1;
    if(tonelevel > 6)
        tonelevel = 2;
    led2 = !led2;

    //account for change in octave level
    if(odeSet == true)
    {
        for(int i=0; i<ODE2JOYLENGTH; i++)
        {
           ode2joy[i] = octave[tonelevel][ode2joyNotes[i]];
        } 
    }  
    else if(jingleSet == true)
    {
        for(int i=0; i<JINGLELENGTH; i++)
        {
           jingle[i]=      octave[tonelevel][jingleNotes[i]];
        } 
    } 
}//end change octave

//Write to the SD card
bool writeSD()
{
    FILE *fp = fopen("/sd/sdfile.txt", "w");   // open file for writing
    
    if (fp==NULL)
    {
        pc.printf("Could not open file to write\n");
        return false;
    }
    else
    {
        pc.printf("SC card opened\r\n");
        //wait_ms(1000);
    }
    
    if(fprintf(fp, "%d", recordLength))    //first store the size of the array
        pc.printf("Write of length of sucessful and it is %d\r\n\n", recordLength);
        
        //wait_ms(1000);
        fprintf(fp, " "); //delimit the values
    
    for(int i=0; i<recordLength; i++)   //copy all values over
    {
        if(fprintf(fp, "%f", record[i]))
        {
            fprintf(fp," ");    //delimit the values
            pc.printf("Number of tones stored is : %d\r\n\n", i+1);
            pc.printf("The tone is %f \r\n", record[i]);
        }
    } 
    //wait_ms(1000);
    fclose(fp);
    
    return true;
}//end write

//read from a SD card
bool readSD()
{
    FILE *fp = fopen("/sd/sdfile.txt", "r");   // open file for writing
    
    if (fp==NULL)
    {
        pc.printf("Could not open file to write\n");
        return false;
    }
    else
    {
        pc.printf("SC card opened\r\n");
        //wait_ms(1000);
    }
    
    
    
    if(fscanf(fp, "%d", &recordLength))    //first read the size of the array
    {
        pc.printf("Write of length of sucessful and its value is %d\r\n\n", recordLength);
        //wait_ms(1000);
    }
    else
        return false;
    
    for(int i=0; i<recordLength; i++)   //copy all values over
    {
        if(fscanf(fp, "%f", &record[i]))
            pc.printf("Tone stored is %f\r\n\n", record[i]);
        else
            return false;
    } 
    //wait_ms(1000);
    fclose(fp);
    return true;
}//end read

//remove the file from the SD card before we create a new one
bool removeFile()
{
    //create a string
    char filename[] = "/sd/sdfile.txt";
    
    int ret = remove(filename);

    if(ret == 0) 
    {
      pc.printf("File deleted successfully");
      return false;
    }
    else 
    {
      pc.printf("Error: unable to delete the file");
    } 
    return true;
}