Bluetooth Enabled Keyboard/Synthesizer

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

Fork of 4180_Final_Design_Project by John Pinion

Revision:
6:68c6a50e1437
Parent:
5:afd67e985df0
Child:
7:d4c3260cb092
--- a/main.cpp	Thu Apr 28 07:29:08 2016 +0000
+++ b/main.cpp	Thu Apr 28 18:21:52 2016 +0000
@@ -1,5 +1,8 @@
 #include "mbed.h"
 #include "SDFileSystem.h"
+#include "rtos.h"
+#include <vector>
+#include "uLCD_4DGL.h"
 #include "synthesizer.h"
 Serial Blue(p28,p27);
 Serial PC(USBTX,USBRX);
@@ -8,22 +11,41 @@
 
 SDFileSystem sd(p5, p6, p7, p8, "sd"); //SD card setup
 
+Mutex mtx;  //Mutex lock
+
+uLCD_4DGL uLCD(p13,p14,p11); // serial tx, serial rx, reset pin;
+
+
 //global variables for main and interrupt routine
 volatile bool readyFlag = true;
 volatile char keyPress;
 WaveType myWave = sine; // default to sine wave
 volatile int currentOctave = 4; // default to 4 because thats where middle C is
 volatile int currentAttackVal = 3;   // values will range from 1-5, default to 3
-volatile int currentDelayVal = 3;    // values will range from 1-5, default to 3
+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
+int *currentAttackTable;            // pointer to the correct attack coefficient table
+int *currentDecayTable;             // pointer to the correct decay coefficient table
+int *currentSustainTable;           // pointer to the correct sustain coefficient table
+int *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
 
 /* 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.
 */
 
-const double attackVals5[32] = {    //Approaches the maximum amplitude the quickest - corresponds to an attackValue of 5
+double attackVals5[32] = {    //Approaches the maximum amplitude the quickest - corresponds to an attackValue of 5
 0, 0.275 , 0.55 , 0.7 ,
 0.8 , 0.85 , 0.9 , 0.91 ,
 0.92 , 0.93 , 0.939 , 0.948 ,
@@ -33,7 +55,7 @@
 0.995 , 0.996 , 0.997 , 0.998 ,
 0.9985 , 0.999 , 0.9995 , 1
 };
-const double attackVals4[32] = {    //Corresponds to an attackValue of 4
+double attackVals4[32] = {    //Corresponds to an attackValue of 4
 0 , 0.18 , 0.38 , 0.58 , 
 0.66 , 0.69 , 0.72 , 0.74 , 
 0.76 , 0.78 , 0.795 , 0.81 , 
@@ -43,7 +65,7 @@
 0.95 , 0.96 , 0.97 , 0.98 , 
 0.985 , 0.99 , 0.995 , 1
 };
-const double attackVals3[32] = {    //Corresponds to an attackValue of 3
+double attackVals3[32] = {    //Corresponds to an attackValue of 3
 0 , 0.09 , 0.18 , 0.27 , 
 0.35 , 0.43 , 0.5 , 0.57 , 
 0.61 , 0.65 , 0.68 , 0.71 , 
@@ -53,7 +75,7 @@
 0.95 , 0.96 , 0.97 , 0.98 , 
 0.985 , 0.99 , 0.995 , 1
 };
-const double attackVals2[32] = {    //Corresponds to an attackValue of 2
+double attackVals2[32] = {    //Corresponds to an attackValue of 2
 0 , 0.06 , 0.12 , 0.18 , 
 0.23 , 0.28 , 0.32 , 0.36 , 
 0.4 , 0.44 , 0.48 , 0.52 , 
@@ -63,7 +85,7 @@
 0.87 , 0.895 , 0.92 , 0.945 , 
 0.965 , 0.985 , 0.995 , 1
 };
-const double attackVals1[32] = {    //Approaches the mamimum amplitude the slowest, in a linear fashion - corresponds to an attackValue of 1
+double attackVals1[32] = {    //Approaches the mamimum amplitude the slowest, in a linear fashion - corresponds to an attackValue of 1
 0 , 0.032258065 , 0.064516129 , 0.096774194 , 
 0.129032258 , 0.161290323 , 0.193548387 , 0.225806452 , 
 0.258064516 , 0.290322581 , 0.322580645 , 0.35483871 , 
@@ -74,17 +96,17 @@
 0.903225806 , 0.935483871 , 0.967741935 , 1
 };
 
-const double decayVals5[32] = {
+double decayVals5[32] = {
     };
-const double decayVals4[32] = {
+double decayVals4[32] = {
     };
-const double decayVals3[32] = {
+double decayVals3[32] = {
     };
-const double decayVals2[32] = {
+double decayVals2[32] = {
     };
-const double decayVals1[32] = {
+double decayVals1[32] = {
     };
-const double sustainVals5[160] = {  //Highest level of sustain, the note will cap out at an amplitude of 1 - corresponds to sustain value of 5
+double sustainVals5[160] = {  //Highest level of sustain, the note will cap out at an amplitude of 1 - corresponds to sustain value of 5
 .8 , .8 , .8 , .8 ,
 .8 , .8 , .8 , .8 , 
 .8 , .8 , .8 , .8 , 
@@ -94,7 +116,7 @@
 .8 , .8 , .8 , .8 , 
 .8 , .8 , .8 , .8  
 };
-const double sustainVal4[160] = {   //Sustain value of 4
+double sustainVal4[160] = {   //Sustain value of 4
 .65 , .65 , .65 , .65 , 
 .65 , .65 , .65 , .65 , 
 .65 , .65 , .65 , .65 , 
@@ -104,7 +126,7 @@
 .65 , .65 , .65 , .65 , 
 .65 , .65 , .65 , .65
 };
-const double sustainVals3[160] = {  //Sustain value of 3
+double sustainVals3[160] = {  //Sustain value of 3
 .5 , .5 , .5 , .5 , 
 .5 , .5 , .5 , .5 , 
 .5 , .5 , .5 , .5 , 
@@ -114,7 +136,7 @@
 .5 , .5 , .5 , .5 , 
 .5 , .5 , .5 , .5
 };
-const double sustainVals2[160] = {  //Sustain value of 2
+double sustainVals2[160] = {  //Sustain value of 2
 .35 , .35 , .35 , .35 , 
 .35 , .35 , .35 , .35 , 
 .35 , .35 , .35 , .35 , 
@@ -124,7 +146,7 @@
 .35 , .35 , .35 , .35 , 
 .35 , .35 , .35 , .35
 };
-const double sustainVals1[160] = {  //Lowest level of sustain, the note will cap at at an amplitude of .2 - corresponds to sustain value of 1
+double sustainVals1[160] = {  //Lowest level of sustain, the note will cap at at an amplitude of .2 - corresponds to sustain value of 1
 .20 , .20 , .20 , .20 ,
 .20 , .20 , .20 , .20 , 
 .20 , .20 , .20 , .20 , 
@@ -133,20 +155,20 @@
 .20 , .20 , .20 , .20 , 
 .20 , .20 , .20 , .20 , 
 .20 , .20 , .20 , .20
-}
-const double releaseVals5[32] = {
+};
+double releaseVals5[32] = {
     };
-const double releaseVals4[32] = {
+double releaseVals4[32] = {
     };
-const double releaseVals3[32] = {
+double releaseVals3[32] = {
     };
-const double releaseVals2[32] = {
+double releaseVals2[32] = {
     };
-const double releaseVals1[32] = {
+double releaseVals1[32] = {
     };
     
 /* Values for the sine wave shaped waveform */
-const int sineTable[256] = {
+int sineTable[256] = {
 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,
 0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,
@@ -182,7 +204,7 @@
 };
  
 /* Values for the square shaped waveform */
-const uint8_t squareTable[256] = {
+int squareTable[256] = {
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
@@ -218,7 +240,7 @@
 };
  
 /* Values for the sawtooth shaped waveform */
-const uint8_t sawtoothTable[256] = {
+int sawtoothTable[256] = {
 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
@@ -253,9 +275,197 @@
 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
 };
 
+int noteArray[7][7] = { // Array holding different note frequencies
+  C1 , D1 , E1 , F1 , G1 , A1 , B1 ,
+  C2 , D2 , E2 , F2 , G2 , A2 , B2,
+  C3 , D3 , E3 , F3 , G3 , A3 , B2 , 
+  C4 , D4 , E4 , F4 , G4 , A4 , B4 ,
+  C5 , D5 , E5 , F5 , G5 , A5 , B5 ,
+  C6 , D6 , E6 , F6 , G6 , A6 , B6 ,
+  C7 , D7 , E7 , F7 , G7 , A7 , B7 
+};
 
+void uLCD_Display_Thread(void const *args){
+    mtx.lock();
+    uLCD.locate(0,0);
+    uLCD.printf("Current Waveform Shape: %s\r\n",myWave);
+    uLCD.printf("Current Octave: %i\r\n",currentOctave);
+    uLCD.printf("Current Attack Value: %i\r\n",currentAttackVal);
+    uLCD.printf("Current Decay Value: %i\r\n",currentDecayVal);
+    uLCD.printf("Current Sustain Value: %i\r\n",currentSustainVal);
+    uLCD.printf("Current Release Value: %i\r\n",currentReleaseVal);
+    mtx.unlock();
+    Thread::wait(250);
+    }
+    
+void set_Frequency_Tuner(void) {
+  frequencyTuner = ((((int)noteFreq) * num_samples) *
+            (shift_factor / sampling_frequency));
+}
+
+void accumulator_Increment(void) {
+  phaseAccumulator += frequencyTuner;
+}
+
+void accumulator_reset(void) {
+  phaseAccumulator = 0;
+}
+
+void set_Note_Freq(int frequency){
+    accumulator_reset();
+    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)
+{
+    switch(attackVal){
+    case 5:
+        currentAttackTable = attackVals5;
+        break;
+    case 4:
+        currentAttackTable = attackVals4;
+        break;
+    case 3:
+        currentAttackTable = attackVals3;
+        break;
+    case 2:
+        currentAttackTable = attackVals2;
+        break;
+    case 1:
+        currentAttackTable = attackVals1;
+        break;
+    default:
+    break;
+    }
+}
 
+void change_Decay_Table(int decayVal)
+{
+    switch(decayVal){
+    case 5:
+        currentDecayTable = decayVals5;
+        break;
+    case 4:
+        currentDecayTable = decayVals4;
+        break;
+    case 3:
+        currentDecayTable = decayVals3;
+        break;
+    case 2:
+        currentDecayTable = decayVals2;
+        break;
+    case 1:
+        currentDecayTable = decayVals1;
+        break;
+    default:
+    break;
+    }
+}
 
+void change_Sustain_Table(int sustainVal)
+{
+    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:
+    break;
+    }
+}
+
+void change_Release_Table(int releaseVal)
+{
+    switch(releaseVal){
+    case 5:
+        currentReleaseTable = releaseVals5;
+        break;
+    case 4:
+        currentReleaseTable = releaseVals4;
+        break;
+    case 3:
+        currentReleaseTable = releaseVals3;
+        break;
+    case 2:
+        currentReleaseTable = releaseVals2;
+        break;
+    case 1:
+        currentReleaseTable = releaseVals1;
+        break;
+    default:
+    break;
+    }
+} 
+
+void clear_Buffer(void){
+    sampleBuffer.clear();
+}
+    
+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
+    }
+}
+
+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
+    sustain_range = sustain_range + (sampleBuffer.size() * (5/8));  // The sustain portion of the waveform will take (5/8) of the note's duration
+    release_range = release_range + (sampleBuffer.size() * (1/8));  // The release portion of the waveform will take (1/8) of the note's duration
+    for(int i = 0; i < attack_range; i++)
+    {
+        sampleBuffer[i] = sampleBuffer[i] * currentAttackTable[i];
+    }
+    for(int k = attack_range; k < decay_range; k++)
+    {
+        sampleBuffer[k] = sampleBuffer[k] * currentDecayTable[k-attack_range];
+    }
+    for(int m = decay_range; m < sustain_range; m++)
+    {
+        sampleBuffer[m] = sampleBuffer[m] * currentSustainTable[m-decay_range];
+    }
+    for(int n = sustain_range; n < release_range; n++)
+    {
+        sampleBuffer[n] = sampleBuffer[n] * currentReleaseTable[n-sustain_range];
+    }     
+}
 
 
 
@@ -283,7 +493,7 @@
 The note played will be encoded in hexadecimal, as well as the octave, Attack Value,
 Delay Value, Sustain Value, and Release Value. The format of the bits will be as follows:
 | 3 bits | 3 bits | 3 bits | 3 bits | 3 bits | 3 bits |
-| Attack | Delay | Susttain | Release | Octave | Note |
+| Attack | Decay | Susttain | Release | Octave | Note |
 For the 3 bits representing note, A will correspond to 1, B to 2, and so on.
 For example, if the lower 3 bits corresponding to note are 001, then the note is an A.
 
@@ -292,10 +502,10 @@
 
 void write_to_SDCard(char note)
 {
-    int AttackBits, SustainBits, DelayBits, ReleaseBits, OctaveBits, NoteBits;
+    int AttackBits, SustainBits, DecayBits, ReleaseBits, OctaveBits, NoteBits;
  
     AttackBits = currentAttackVal;
-    DelayBits = currentDelayVal;
+    DecayBits = currentDecayVal;
     SustainBits = currentSustainVal;
     ReleaseBits = currentReleaseVal;
     OctaveBits = currentOctave;
@@ -326,7 +536,7 @@
             break;
         }
     int writeVal;
-    writeVal  = (AttackBits << 15) | (DelayBits << 12) | (SustainBits << 9) | (ReleaseBits << 6) | (OctaveBits << 3) | (NoteBits);
+    writeVal  = (AttackBits << 15) | (DecayBits << 12) | (SustainBits << 9) | (ReleaseBits << 6) | (OctaveBits << 3) | (NoteBits);
     
     FILE *fp = fopen("/sd/noteRecords/note_record_01.txt", "w");
     if(fp == NULL) {
@@ -347,42 +557,48 @@
     while(1) {
         //check for a new button message ready
         if((keyPress == C_NOTE_KEY) && (readyFlag)){ // button Z pressed
-            PC.printf("Got a Z");
+            set_Note_Freq(noteArray[currentOctave-1][0]);
             write_to_SDCard('C');
             readyFlag = false;
             // Play note that corresponds to Z
             }
         else if((keyPress == D_NOTE_KEY) && (readyFlag)){ // button X pressed
-                PC.printf("Got an X");
+                set_Note_Freq(noteArray[currentOctave-1][1]);
                 write_to_SDCard('D');
                 readyFlag = false;
             // Play note that corresponds to X
             }
         else if((keyPress == E_NOTE_KEY) && (readyFlag)){ // button C pressed
+            set_Note_Freq(noteArray[currentOctave-1][2]);
             // Play note that corresponds to C
             // Make note of which note was played in file on SD Card
             write_to_SDCard('E');
             readyFlag = false;
             }
         else if((keyPress == F_NOTE_KEY) && (readyFlag)){ // button V pressed
+            set_Note_Freq(noteArray[currentOctave-1][3]);
             // Play note that corresponds to V
             // Make note of which note was played in file on SD Card
             write_to_SDCard('F');
             readyFlag = false;
             }
         else if((keyPress == G_NOTE_KEY) && (readyFlag)){ // button B pressed
+            set_Note_Freq(noteArray[currentOctave-1][4]);
             // Play note that corresponds to B
             // Make note of which note was played in file on SD Card
             write_to_SDCard('G');
             readyFlag = false;
             }
         else if((keyPress == A_NOTE_KEY) && (readyFlag)){ // button N pressed
+            set_Note_Freq(noteArray[currentOctave][5]);
+            buffer_Samples();
             // Play note that corresponds to N
             // Make note of which note was played in file on SD Card
             write_to_SDCard('A');
             readyFlag = false;
             }
         else if((keyPress == B_NOTE_KEY) && (readyFlag)){ // button M pressed
+            set_Note_Freq(noteArray[currentOctave][6]);
             // Play note that corresponds to M
             // Make note of which note was played in file on SD Card
             write_to_SDCard('B');
@@ -390,11 +606,11 @@
             }
         else if((keyPress == RAISE_OCTAVE_KEY) && (readyFlag)){ // button O pressed
             // Raise an octave
-            if(currentOcatve < 7)
+            if(currentOctave < 7)
             currentOctave++;
-            }
             else
             printf("Cannot raise octave above 7.\r\n");
+            }
         else if((keyPress == LOWER_OCTAVE_KEY) && (readyFlag)){ // button L pressed
             // Lower an octave
             if(currentOctave > 1)
@@ -404,71 +620,90 @@
             }
         else if((keyPress == RAISE_ATTACK_KEY) && (readyFlag)){ // button Q pressed
             // Raise Attack Value
-            if(currentAttackVal < 5)
+            if(currentAttackVal < 5){
             currentAttackVal++;
+            change_Attack_Table(currentAttackVal);
+            }
             else
             printf("Cannot raise value above 5.\r\n");
             }
         else if((keyPress == LOWER_ATTACK_KEY) && (readyFlag)){ // button A pressed
             // Lower Attack Value
-            if(currentAttackVal > 1)
+            if(currentAttackVal > 1){
             currentAttackVal--;
+            change_Attack_Table(currentAttackVal);
+            }
             else
-            printf("Cannot lower value below 1.\r\n";
+            printf("Cannot lower value below 1.\r\n");
             }
         else if((keyPress == RAISE_DELAY_KEY) && (readyFlag)){ // button W pressed
             // Raise Delay Value
-            if(currentDelayVal < 5)
-            currentDelayVal++;
+            if(currentDecayVal < 5){
+            currentDecayVal++;
+            change_Decay_Table(currentDecayVal);
+            }
             else
             printf("Cannot raise value above 5.\r\n");
             }
         else if((keyPress == LOWER_DELAY_KEY) && (readyFlag)){ // button S pressed
             // Lower Delay Value
-            if(currentDelayVal > 1)
-            currentDelayVal--;
+            if(currentDecayVal > 1){
+            currentDecayVal--;
+            change_Decay_Table(currentDecayVal);
+            }
             else
-            printf("Cannot lower value below 1.\r\n";
+            printf("Cannot lower value below 1.\r\n");
             }
         else if((keyPress == RAISE_SUSTAIN_KEY) && (readyFlag)){ // button E pressed
             // Raise Sustain Value
-            if(currentSustainVal < 5)
+            if(currentSustainVal < 5){
             currentSustainVal++;
+            change_Sustain_Table(currentSustainVal);
+            }
             else
             printf("Cannot raise value above 5.\r\n");
             }
         else if((keyPress == LOWER_SUSTAIN_KEY) && (readyFlag)){ // button D pressed
             // Lower Sustain Value
-            if(currentSustainVal > 1)
+            if(currentSustainVal > 1){
             currentSustainVal--;
+            change_Sustain_Table(currentSustainVal);
+            }
             else
-            printf("Cannot lower value below 1.\r\n";
+            printf("Cannot lower value below 1.\r\n");
             }
         else if((keyPress == RAISE_RELEASE_KEY) && (readyFlag)){ // button R pressed
             // Raise Release Value
-            if(currentReleaseVal < 5)
+            if(currentReleaseVal < 5){
             currentReleaseVal++;
+            change_Release_Table(currentReleaseVal);
+            }
             else
             printf("Cannot raise value above 5.\r\n");
             }
         else if((keyPress == LOWER_RELEASE_KEY) && (readyFlag)){ // button F pressed
             // Lower Release Value
-            if(currentReleaseVal > 1)
+            if(currentReleaseVal > 1){
             currentReleaseVal--;
+            change_Release_Table(currentReleaseVal);
+            }
             else
-            printf("Cannot lower value below 1.\r\n";
+            printf("Cannot lower value below 1.\r\n");
             }
         else if((keyPress == CHANGE_WAVESHAPE_UP) && (readyFlag)){ // button T pressed
             // Change waveform shape to next waveform type
             switch(myWave){
                 case sine:
                     myWave = square;
+                    change_Wave(myWave);
                     break;
                 case square:
                     myWave = sawtooth;
+                    change_Wave(myWave);
                     break;
                 case sawtooth:
                     myWave = sine;
+                    change_Wave(myWave);
                     break;
                 default:
                 break;
@@ -479,12 +714,15 @@
             switch(myWave){
                 case sine:
                     myWave = sawtooth;
+                    change_Wave(myWave);
                     break;
                 case square:
                     myWave = sine;
+                    change_Wave(myWave);
                     break;
                 case sawtooth:
                     myWave = square;
+                    change_Wave(myWave);
                     break;
                 default:
                 break;