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

Files at this revision

API Documentation at this revision

Comitter:
mvanderpohl
Date:
Sat Apr 30 14:12:06 2016 +0000
Commit message:
initial commit

Changed in this revision

SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 660f0e3ac256 SDFileSystem.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Sat Apr 30 14:12:06 2016 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/mbed/code/SDFileSystem/#7b35d1709458
diff -r 000000000000 -r 660f0e3ac256 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Apr 30 14:12:06 2016 +0000
@@ -0,0 +1,497 @@
+//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;
+}
diff -r 000000000000 -r 660f0e3ac256 mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Apr 30 14:12:06 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/99a22ba036c9
\ No newline at end of file