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
Revision 0:660f0e3ac256, committed 2016-04-30
- Comitter:
- mvanderpohl
- Date:
- Sat Apr 30 14:12:06 2016 +0000
- Commit message:
- initial commit
Changed in this revision
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