Bluetooth Enabled Keyboard/Synthesizer

Dependencies:   4DGL-uLCD-SE SDFileSystem mbed-rtos mbed

Fork of 4180_Final_Design_Project by John Pinion

Revision:
12:d60a9d0052a7
Parent:
11:c87f55a3b9e0
Child:
13:25d53936d385
--- a/main.cpp	Fri Apr 29 16:23:20 2016 +0000
+++ b/main.cpp	Fri Apr 29 22:28:48 2016 +0000
@@ -27,25 +27,18 @@
 volatile int currentDecayVal = 3;    // values will range from 1-5, default to 3
 volatile int currentSustainVal = 3;  // values will range from 1-5, default to 3
 volatile int currentReleaseVal = 3;  // values will range from 1-5, default to 3
-int *currentLookupTable;            // pointer to the correct lookup table of values
 double *currentAttackTable;            // pointer to the correct attack coefficient table
 double *currentDecayTable;             // pointer to the correct decay coefficient table
 double *currentSustainTable;           // pointer to the correct sustain coefficient table
 double *currentReleaseTable;           // pointer to the correct release coefficient table
 vector<double> sampleBuffer;        // vector to hold samples of generated waveform
-volatile int lookupTableIndex;      // index used to find values in the lookup table for the waveforms
-volatile int phaseAccumulator;      // stores phase accumulator which is used to index into the lookup table
 int num_samples = 256;              // number of samples 
-int shift_factor = 0x01000000;      // shifting factor
-int sampling_frequency = 40000;     // sampling frequency is 40kHz
-volatile int frequencyTuner;        // the frequency tuner used to increment the accumulator which indexes values in the lookup table
 volatile int noteFreq;              // the current frequency of the note being played
-volatile int sustainAmplitude;      // the desired amplitude of the sustain level
 double timeIncrement = (2/256);     // 2 seconds with 256 samples
 
 /* Coefficient Matrices Corresponding to Different Attack Values
 each matrix is comprised of 32 elements (256/8). The first matrix corresponds
-to an attack value of 1.
+to an attack value of 5.
 */
 
 double attackVals5[32] = {    //Approaches the maximum amplitude the quickest - corresponds to an attackValue of 5
@@ -99,6 +92,11 @@
 0.903225806 , 0.935483871 , 0.967741935 , 1
 };
 
+/* Coefficient Matrices Corresponding to Different Decay Values
+each matrix is comprised of 32 elements (256/8). The first matrix corresponds
+to a decay value of 5.
+*/
+
 double decayVals5[32] = {   //Approaches the sustain amplitude the quickest - corresponds to a decay value of 5
 1 , 0.8 , 0.75 , 0.71 , 
 0.68 , 0.66 , 0.65 , 0.64 , 
@@ -109,7 +107,7 @@
 0.6 , 0.6 , 0.6 , 0.6 , 
 0.6 , 0.6 , 0.6 , 0.6
 };
-double decayVals4[32] = {
+double decayVals4[32] = {   // Decay value of 4
 1 , 0.93 , 0.86 , 0.8 , 
 0.75 , 0.71 , 0.69 , 0.68 , 
 0.67 , 0.66 , 0.655 , 0.65 , 
@@ -119,7 +117,7 @@
 0.6 , 0.6 , 0.6 , 0.6 , 
 0.6 , 0.6 , 0.6 , 0.6
 };
-double decayVals3[32] = {
+double decayVals3[32] = {   // Decay value of 3
 1 , 0.96 , 0.92 , 0.88 , 
 0.85 , 0.82 , 0.79 , 0.76 , 
 0.74 , 0.72 , 0.705 , 0.69 , 
@@ -129,7 +127,7 @@
 0.615 , 0.61 , 0.605 , 0.6 , 
 0.6 , 0.6 , 0.6 , 0.6
 };
-double decayVals2[32] = {
+double decayVals2[32] = {   // Decay value of 2
 1 , 0.98 , 0.96 , 0.94 , 
 0.92 , 0.9 , 0.88 , 0.86 , 
 0.84 , 0.82 , 0.8 , 0.79 , 
@@ -139,7 +137,7 @@
 0.66 , 0.65 , 0.64 , 0.63 , 
 0.62 , 0.61 , 0.6 , 0.6
 };
-double decayVals1[32] = {
+double decayVals1[32] = {   // Decays the slowest, in a linear fashion - corresponds to a decay value of 1
 1 , 0.987096774 , 0.974193548 , 0.961290323 , 
 0.948387097 , 0.935483871 , 0.922580645 , 0.909677419 , 
 0.896774194 , 0.883870968 , 0.870967742 , 0.858064516 , 
@@ -150,12 +148,23 @@
 0.638709677 , 0.625806452 , 0.612903226 , 0.6
 };
 
+/* Coefficient Matrices Corresponding to Different sustain values
+each matrix is comprised of 160 elements 5 * (256/8). The first matrix corresponds
+to a sustain value of 5. The matrices get initialized later in a for loop due to their size.
+*/
+
 double sustainVals5[160]; 
 double sustainVals4[160];
 double sustainVals3[160];
 double sustainVals2[160];
 double sustainVals1[160];
-double releaseVals5[32] = {
+
+/* Coefficient Matrices Corresponding to Different release values
+each matrix is comprised of 32 elements (256/8). The first matrix corresponds
+to a release value of 5.
+*/
+
+double releaseVals5[32] = {     // Releases (goes to 0 amplitude) the quickest - corresponds to a release value of 5
 0.6 , 0.3 , 0.15 , 0.1 , 
 0.09 , 0.08 , 0.07 , 0.06 , 
 0.05 , 0.045 , 0.04 , 0.035 , 
@@ -164,7 +173,7 @@
 0 , 0 , 0 , 0 , 
 0 , 0 , 0 , 0 , 
 0 , 0 , 0 , 0};
-double releaseVals4[32] = {
+double releaseVals4[32] = {     // Release value of 4
 0.6 , 0.45 , 0.3 , 0.2 , 
 0.17 , 0.16 , 0.15 , 0.14 , 
 0.13 , 0.125 , 0.12 , 0.115 , 
@@ -173,7 +182,7 @@
 0.07 , 0.065 , 0.06 , 0.055 , 
 0.05 , 0.045 , 0.04 , 0.035 , 
 0.03 , 0.02 , 0.01 , 0};
-double releaseVals3[32] = {
+double releaseVals3[32] = {     // Release value of 3
 0.6 , 0.5 , 0.43 , 0.37 , 
 0.32 , 0.28 , 0.26 , 0.24 , 
 0.22 , 0.2 , 0.18 , 0.17 , 
@@ -182,7 +191,7 @@
 0.08 , 0.07 , 0.06 , 0.05 , 
 0.04 , 0.035 , 0.03 , 0.025 , 
 0.02 , 0.015 , 0.01 , 0};
-double releaseVals2[32] = {
+double releaseVals2[32] = {     // Release value of 2
 0.6 , 0.55 , 0.5 , 0.46 , 
 0.43 , 0.4 , 0.37 , 0.34 , 
 0.32 , 0.3 , 0.28 , 0.26 , 
@@ -191,7 +200,7 @@
 0.12 , 0.11 , 0.1 , 0.09 , 
 0.08 , 0.07 , 0.06 , 0.05 , 
 0.04 , 0.03 , 0.015 , 0};
-double releaseVals1[32] = {
+double releaseVals1[32] = {     // Release value of 1 - proceeds slowest, in a linear fashion
 0.6 , 0.580645161 , 0.561290323 , 0.541935484 , 
 0.522580645 , 0.503225806 , 0.483870968 , 0.464516129 , 
 0.44516129 , 0.425806452 , 0.406451613 , 0.387096774 , 
@@ -211,49 +220,40 @@
   C7 , D7 , E7 , F7 , G7 , A7 , B7 
 };
 
-void uLCD_Display_Thread(void const *args){
-    //mtx.lock();
+void uLCD_Display_Thread(void const *args){     // uLCD displays curernt waveform shape, current octave, and the values for the ADSR coefficients
     while(1){
         uLCD.locate(0,0);
-        uLCD.printf("Shape: %i\r\n",myWave);
-        uLCD.printf("Octave: %i\r\n",currentOctave);
-        uLCD.printf("Attack: %i\r\n",currentAttackVal);
-        uLCD.printf("Decay: %i\r\n",currentDecayVal);
-        uLCD.printf("Sustain: %i\r\n",currentSustainVal);
-        uLCD.printf("Release: %i\r\n",currentReleaseVal);
+        switch(myWave){
+        case sine:
+            uLCD.printf("Shape: Sine\r\n");     // if wave type is sine wave, display sine
+            break;
+        case square:
+            uLCD.printf("Shape: Square\r\n");   // if wave type is square wave, display square
+            break;
+        case sawtooth:
+            uLCD.printf("Shape: Sawtooth\r\n"); // if wave type is sawtooth wave, display sawtooth
+            break;
+        default:
+        break;
+        }
+        uLCD.printf("Octave: %i\r\n",currentOctave);        // displays octave
+        uLCD.printf("Attack: %i\r\n",currentAttackVal);     // displays attack value
+        uLCD.printf("Decay: %i\r\n",currentDecayVal);       // displays decay value
+        uLCD.printf("Sustain: %i\r\n",currentSustainVal);   // displays sustain value
+        uLCD.printf("Release: %i\r\n",currentReleaseVal);   // displays release value
     }
-    //mtx.unlock();
-    //Thread::wait(250);
-    }
+}
 
-void clear_Buffer(void){
+void clear_Buffer(void){        // clears buffer that holds samples
     sampleBuffer.clear();
 }
 
-void set_Note_Freq(int frequency){
-    //accumulator_reset();
+void set_Note_Freq(int frequency){      // updates the frequency of the note being played
     noteFreq = frequency;
-    //set_Frequency_Tuner();
     clear_Buffer();
 }
 
-//void change_Wave(const WaveType currentWave) {
-//  switch(currentWave) {
-//  case sine:
-//    currentLookupTable = sineTable;
-//    break;
-//  case square:
-//    currentLookupTable = squareTable;
-//    break;
-//  case sawtooth:
-//    currentLookupTable = sawtoothTable;
-//    break;
-//  default:
-//    break;
-//  }
-//}
-
-void change_Attack_Table(int attackVal)
+void change_Attack_Table(int attackVal)     // change which table of coefficients to use for altering the attack portion of the waveform
 {
     switch(attackVal){
     case 5:
@@ -276,7 +276,7 @@
     }
 }
 
-void change_Decay_Table(int decayVal)
+void change_Decay_Table(int decayVal)       // change which table of coefficients to use for altering the decay portion of the waveform
 {
     switch(decayVal){
     case 5:
@@ -299,27 +299,22 @@
     }
 }
 
-void change_Sustain_Table(int sustainVal)
+    void change_Sustain_Table(int sustainVal)   // change which table of coefficients to use for altering the sustain portion of the waveform
 {
     switch(sustainVal){
     case 5:
-        //sustainAmplitude = .8;
         currentSustainTable = sustainVals5;
         break;
     case 4:
-        //sustainAmplitude = .65;
         currentSustainTable = sustainVals4;
         break;
     case 3:
-        //sustainAmplitude = .5;
         currentSustainTable = sustainVals3;
         break;
     case 2:
-        //sustainAmplitude = .35;
         currentSustainTable = sustainVals2;
         break;
     case 1:
-        //sustainAmplitude = .2;
         currentSustainTable = sustainVals1;
         break;
     default:
@@ -327,7 +322,7 @@
     }
 }
 
-void change_Release_Table(int releaseVal)
+void change_Release_Table(int releaseVal)       // change which table of coefficients to use for altering the release portion of the waveform
 {
     switch(releaseVal){
     case 5:
@@ -349,7 +344,13 @@
     break;
     }
 } 
-void initialize_sustainVals()
+
+/* Having different sustain values for the amplitude of the wave would make the math neccesary to generate the other
+coefficient matrices very complex, so only .6 is used, meaning a sustain value of 1-5 will all correspond to a sustain amplitude
+of .6. Since the sustain coefficient matrices are 160 elements long, they are all filled in a for loop with this function call.
+*/
+
+void initialize_sustainVals()   
 {
     for(int j = 0; j < 160; j++)
     {
@@ -360,16 +361,13 @@
         sustainVals1[j] = .6;
     }
 }
-    
-void buffer_Samples(void){
-    for(int j=0;j<num_samples;j++){
-    //accumulator_Increment(); // Increment the phase accumulator
-    lookupTableIndex = phaseAccumulator >> 24; // Get address into wavetable    
-    sampleBuffer.push_back(currentLookupTable[lookupTableIndex] / 255);  // divide by 255 so that we get values between 0 and 1
-    }
-}
+/* Applies the envelope to the waveform. Each set of coefficients is applied to a certain portion of the waveform to alter its shape.
+The attack coefficients are appplied to the first 32 samples, the decay coefficients are applied to samples 33-64, the sustain coefficients
+are applied to samples 65 - 224, and the release coefficients are appplied to samples 225-256.
+*/
 
-void apply_Envelope(void){
+
+void apply_Envelope(void){                                          
     int attack_range, decay_range, sustain_range, release_range;
     attack_range = sampleBuffer.size() * (1/8);                     // The attack portion of the waveform will take (1/8) of the note's duration
     decay_range = attack_range + (sampleBuffer.size() * (1/8));     // The decay portion of the waveform will take (1/8) of the note's duration
@@ -393,9 +391,9 @@
     }     
 }
 
-void generate_sineWave(int frequency)
+void generate_sineWave(int frequency)       // Generates samples for a sine wave of a given input frequency
 {
-    double t = 0;   
+    double t = 0;                           // Represents time, since we want each note to last 2 seconds and have 256 samples
     for(int i = 0; i < 256 ; i++)
     {
         sampleBuffer.push_back(((sin(2*(PI)*frequency*t)) + 1)/2);  // scaled to be a % of maximum output voltage (3.3V)
@@ -403,20 +401,20 @@
     }
 }
 
-void generate_sawtoothWave(int frequency)
+void generate_sawtoothWave(int frequency)   // Generates samples for a sawtooth wave of a given input frequency
 {
-    double t = 0;
+    double t = 0;                           // Represents time, since we want each note to last 2 seconds and have 256 samples
     for(int i = 0; i<256 ; i++)
     {
         sampleBuffer.push_back((2*(t*frequency) - (.5 + (t*frequency)) + 1) / 2);
-        t = t + timeIncrement;                       // increment t for calculation of next value in the waveform   
+        t = t + timeIncrement;              // increment t for calculation of next value in the waveform   
     }
 }
 
-void generate_squareWave(int frequency)
+void generate_squareWave(int frequency)     // Generates samples for a square wave of a given input frequency. Looks at whether we have seen an even or odd number of 'widths' to determine if wave should be high or low at given t
 {
-    double width = (1 / 2 * frequency);     //Width of a half period of the square wave
-    double t = 0;
+    double width = (1 / 2 * frequency);     // Width of a half period of the square wave
+    double t = 0;                           // Represents time, since we want a 2 second note with 256 samples
     for(int i = 0; i < 256; i++)
     {
         if(((int)(t / width) % 2 ) == 0)     // Even, write a 1 for the square wave
@@ -427,6 +425,13 @@
     }
 }
 
+/* Generates the waveforms that will be output to the AnalogOut pin after being altered by the ADSR coefficient matrices.
+The envelope is only applied to sine waves here because when applied to the other wave shapes, the sound does not sounds good.
+@param: frequency - the frequency of the waveform to be generated
+@param: currentWaveType - the shape of the wave that needs to be generated
+*/
+
+
 void create_samples(int frequency, WaveType currentWaveType)
 {
     switch(currentWaveType){
@@ -451,7 +456,10 @@
 }
 
 
-
+/* Outputs the samples that are currently in the buffer one at a time. There is a period of time
+where the program waits so that the 256 samples fill up the entire 2 seconds. The buffer is cleared
+after the output is finished so that next time the buffer will be ready for new samples.
+*/
 void output_samples()
 {
     for( int sample = 0; sample < 256; sample++)
@@ -459,6 +467,7 @@
         synthPin = sampleBuffer[sample];
         Thread::wait(timeIncrement * 1000);
     }
+    clear_Buffer();
 }
         
     
@@ -467,15 +476,15 @@
 //Interrupt routine to parse message with one new character per serial RX interrupt
 void parse_message()
 {
-    PC.printf("Parse_message was called");
+    //PC.printf("Parse_message was called");
     while(Blue.readable())
     {
         keyPress = Blue.getc();
-        PC.putc(keyPress);
+        //PC.putc(keyPress);
         readyFlag = true;
-        PC.printf("\n\r Value of readyFlag is: %i",readyFlag);
-        PC.printf("Value of keyPress is: %c\n\r",keyPress);
-        wait(1);
+        //PC.printf("\n\r Value of readyFlag is: %i",readyFlag);
+        //PC.printf("Value of keyPress is: %c\n\r",keyPress);
+        //wait(1);
     }
 }
 
@@ -496,32 +505,32 @@
 {
     int AttackBits, SustainBits, DecayBits, ReleaseBits, OctaveBits, NoteBits;
  
-    AttackBits = currentAttackVal;
-    DecayBits = currentDecayVal;
-    SustainBits = currentSustainVal;
-    ReleaseBits = currentReleaseVal;
+    AttackBits = currentAttackVal;  // Holds the value of the attack parameter
+    DecayBits = currentDecayVal;    // Holds the value of the decay parameter
+    SustainBits = currentSustainVal;// Holds the value of the sustain parameter
+    ReleaseBits = currentReleaseVal;// Holds the value of the release parameter
     OctaveBits = currentOctave;
     switch(note){
-        case 'C':
+        case 'C':                   // a C corresponds to a 3
             NoteBits = 3;
             break;
         case 'D':
-            NoteBits = 4;
+            NoteBits = 4;           // a D corresponds to a 4
             break;
         case 'E':
-            NoteBits = 5;
+            NoteBits = 5;           // an E corresponds to a 5
             break;
         case 'F':
-            NoteBits = 6;
+            NoteBits = 6;           // an F corresponds to a 6
             break;
         case 'G':
-            NoteBits = 7;
+            NoteBits = 7;           // a G corresponds to a 7
             break;
         case 'A':
-            NoteBits = 1;
+            NoteBits = 1;           // an A corresponds to a 1
             break;
         case 'B':
-            NoteBits = 2;
+            NoteBits = 2;           // a B corresponds to a 2
             break;
         default:
             NoteBits = 0;
@@ -530,9 +539,9 @@
     int writeVal;
     writeVal  = (AttackBits << 15) | (DecayBits << 12) | (SustainBits << 9) | (ReleaseBits << 6) | (OctaveBits << 3) | (NoteBits);
     
-    FILE *fp = fopen("/sd/noteRecords/note_record_01.txt", "w");
+    FILE *fp = fopen("/sd/noteRecords/note_record_01.txt", "w");        // creates handle for file we want to write to
     if(fp == NULL) {
-        error("Could not open file for write\n");
+        error("Could not open file for write\n");                       // if this is not a valid name, tell user there is an error
     }
     fprintf(fp,"%X\r\n",writeVal);      // writes value to the text file in hexadecimal
     fclose(fp);
@@ -540,22 +549,22 @@
 
 int main()
 {
-    Thread thread1(uLCD_Display_Thread);
+    Thread thread1(uLCD_Display_Thread);        // the thread that displays the current values of the parameters as well as the octave and wave shape
     
-    // make directory to hold the record of notes played
-    mkdir("/sd/noteRecords", 0777);
+    
+    mkdir("/sd/noteRecords", 0777);             // make directory to hold the record of notes played
 
-    initialize_sustainVals();      // fill the lookup tables with the sustain values in them
+    initialize_sustainVals();                   // fill the lookup tables with the sustain values in them
     
-    PC.baud(9600);
-    Blue.baud(9600);
+    PC.baud(9600);                              // setup baud rate for PC serial connection
+    Blue.baud(9600);                            // setup baud rate for bluetooth serial connection
 
-    //attach interrupt function for each new Bluetooth serial character
-    Blue.attach(&parse_message,Serial::RxIrq);
+    
+    Blue.attach(&parse_message,Serial::RxIrq);  //attach interrupt function for each new Bluetooth serial character
     while(1) {
         //check for a new button message ready
-        if((keyPress == C_NOTE_KEY) && (readyFlag)){ // button Z pressed
-            set_Note_Freq(noteArray[currentOctave-1][0]);
+        if((keyPress == C_NOTE_KEY) && (readyFlag)){        // button Z pressed
+            set_Note_Freq(noteArray[currentOctave-1][0]);   // set the note frequency to the proper value 
             create_samples(noteFreq, myWave);
             write_to_SDCard('C');
             readyFlag = false;