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; }