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