Bluetooth Enabled Keyboard/Synthesizer for mbed

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

main.cpp

Committer:
jmpin
Date:
2016-05-01
Revision:
25:5a312725710a
Parent:
24:3bd4e691ae59
Child:
26:d4000870deab

File content as of revision 25:5a312725710a:

#include "mbed.h"
#include "SDFileSystem.h"
#include "rtos.h"
#include <vector>
#include "uLCD_4DGL.h"
#include "synthesizer.h"
#include  "Speaker.h"
RawSerial Blue(p13,p14);
RawSerial PC(USBTX,USBRX);
DigitalOut myled(LED1);
DigitalOut myled4(LED4);

SDFileSystem sd(p5, p6, p7, p8, "sd"); //SD card setup

uLCD_4DGL uLCD(p28,p27,p30); // serial tx, serial rx, reset pin;

Speaker mySpeaker(p18);;     // p18 is the pin that will have the output voltages on it


//global variables for main and interrupt routine
volatile bool readyFlag = false;
volatile char keyPress;
WaveType myWave = sine; // default to sine wave
volatile int currentOctave = 4; // default to 4 because thats where middle C is
int currentADSR = 3;                    // value representing which envelope shape is currently selected. default is 3
float attackVals[32] = {};             // array that holds the attack coefficients
float decayVals[32] = {};             // array that holds the decay coefficients
float sustainVals[32]= {};           // array that holds the sustain coefficients
float releaseVals[32] = {};           // array that holds the release coefficients
short unsigned Analog_out_data[32]; // holds the samples that will be output to p18
volatile int noteFreq;              // the current frequency of the note being played
float noteDuration = 2.0f;          // default note duration will be 2 seconds
float attackPercentage[5] = {};     // holds values for percentage of waveform that the attack should take up
float decayPercentage[5] = {};      // holds values for percentage of waveform that the decay should take up
float sustainPercentage[5] = {};    // holds values for the percentage of waveform that the sustain should take up
float releasePercentage[5] = {};    // holds values for the percentage of waveform that the sutain should take up

/* Coefficient Matrices Corresponding to Different Attack Values
each matrix is comprised of 4 elements (32/8). The first matrix corresponds
to an attack value of 5.
*/

float attackVals5[4] = {0 , 0.9 , 0.98 , 1};  //Approaches the maximum amplitude the quickest - corresponds to an attackValue of 5

float attackVals4[4] = {0 , 0.78 , 0.92 , 1};    //Corresponds to an attackValue of 4

float attackVals3[4] = {0 , 0.63 , 0.84 , 1};    //Corresponds to an attackValue of 3

float attackVals2[4] = {0 , 0.5 , 0.75 , 1};    //Corresponds to an attackValue of 2

float attackVals1[4] = {0 , 0.33 , 0.66 , 1};    //Approaches the mamimum amplitude the slowest, in a linear fashion - corresponds to an attackValue of 1

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){     // uLCD displays curernt waveform shape, current octave, and the values for the ADSR coefficients
    while(1){
        uLCD.locate(0,0);
        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("Current ADSR Preset: %i\r\n",currentADSR);     // displays attack value
    }
}

void set_Note_Freq(int frequency){      // updates the frequency of the note being played
    noteFreq = frequency;
}

/* Generates the coefficients that will shape the waveform */

void initialize_ADSRVals()
{
    for(int j = 0; j < 32; j++)
    {
        attackVals[j] = (float)j/32.0f;
        decayVals[j] = 1.0f - (j*((1-sustainAmplitude)/32))
        sustainVals[j] = (float)sustainAmplitude;
        releaseVals[j] = (float)sustainAmplitude - (((sustainAmplitude)/32)*j);
    }
}

/* Generates the durations of each part of the waveform (attack,decay,sustain, and release)*/

void generate_durations(int index)
{
        attackDuration = attackPercentage[index-1] * noteDuration;
        decayDuration = decayPercentage[index-1] * noteDuration;
        sustainDuration = sustainPercentage[index-1] * noteDuration;
        releaseDuration = releasePercentage[index-1] * noteDuration;
}
        


/* 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 4 samples, the decay coefficients are applied to samples 5-8, the sustain coefficients
are applied to samples 9 - 28, and the release coefficients are appplied to samples 29-32.
*/




void generate_sineWave(int frequency)       // Generates samples for a sine wave of a given input frequency
{
    for(int i = 0; i < 32 ; i++)
    {
        Analog_out_data[i] = int (65536.0 * ((1.0 + sin((float(i)/32.0*6.28318530717959)))/2.0));  // scaled to be 16bit
    }
}

void generate_sawtoothWave(int frequency)   // Generates samples for a sawtooth wave of a given input frequency
{
    float t = 0;                           // Represents time
    for(int i = 0; i<32 ; i++)
    {
        Analog_out_data[i] = int(t * 65536.0); //scaled to 16bit  
        t+= 1.0/32.0;             // increment t for calculation of next value in the waveform    
    }
}

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
{
    for(int i = 0; i < 32; i++){
        if(i<16){
            Analog_out_data[i] = 65535; //scaled to 16bit    
        }
        else{
            Analog_out_data[i] = 0; //scaled to 16bit
        }
    }
}

/* 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){
        case sine:
            //Generate sine wave values
            generate_sineWave(frequency);
            generate_durations(currentASDR);
            break;
        case square:
            //Generate square wave values
            generate_squareWave(frequency);
            generate_durations(currentASDR);
            break;
        case sawtooth:
            //Generate sawtooth wave values
            generate_sawtoothWave(frequency);
            generate_durations(currentASDR);
            break;
        default:
        break;
    }
}
        

//Interrupt routine to parse message with one new character per serial RX interrupt
void parse_message()
{
    //PC.printf("Parse_message was called");
    while(Blue.readable())
    {
        keyPress = Blue.getc();
        readyFlag = true;
    }
}


/*
This function writes which note was just played to a text file on the SDCard.
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 | 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.

@param: The note that is being played/recorded into the text file
*/

void write_to_SDCard(char note)
{
    int AttackBits, SustainBits, DecayBits, ReleaseBits, OctaveBits, NoteBits;
 
    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':                   // a C corresponds to a 3
            NoteBits = 3;
            break;
        case 'D':
            NoteBits = 4;           // a D corresponds to a 4
            break;
        case 'E':
            NoteBits = 5;           // an E corresponds to a 5
            break;
        case 'F':
            NoteBits = 6;           // an F corresponds to a 6
            break;
        case 'G':
            NoteBits = 7;           // a G corresponds to a 7
            break;
        case 'A':
            NoteBits = 1;           // an A corresponds to a 1
            break;
        case 'B':
            NoteBits = 2;           // a B corresponds to a 2
            break;
        default:
            NoteBits = 0;
            break;
        }
    int writeVal;
    writeVal  = (AttackBits << 15) | (DecayBits << 12) | (SustainBits << 9) | (ReleaseBits << 6) | (OctaveBits << 3) | (NoteBits);
    FILE *fp = fopen("/sd/noteRecords/note_record_01.txt", "a");        // creates handle for file we want to write to
    if(fp == NULL) {
        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);
}

int main()
{
    Thread thread1(uLCD_Display_Thread);        // the thread that displays the current values of the parameters as well as the octave and wave shape
    
    
    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_ADSRVals();                      // fill the lookup tables for ADSR percentages
    
    PC.baud(9600);                              // setup baud rate for PC serial connection
    Blue.baud(9600);                            // setup baud rate for bluetooth serial connection

    
    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)){        // Play note C
            set_Note_Freq(noteArray[currentOctave-1][0]);   // set the note frequency to the proper value 
            create_samples(noteFreq, myWave);               // creates the samples that are going to be output to the waveform
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);                              // outputs the samples that are currently in the buffer to p18
            write_to_SDCard('C');                           // writes to the SD card
            readyFlag = false;                              // set this flag to false so that the program will not try to process the key press more than once
            
        }
        else if((keyPress == D_NOTE_KEY) && (readyFlag)){ // Play note D
            set_Note_Freq(noteArray[currentOctave-1][1]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('D');
            readyFlag = false;
            
        }
        else if((keyPress == E_NOTE_KEY) && (readyFlag)){ // Play note E
            set_Note_Freq(noteArray[currentOctave-1][2]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('E');
            readyFlag = false;
        }
        else if((keyPress == F_NOTE_KEY) && (readyFlag)){ // Play note F
            set_Note_Freq(noteArray[currentOctave-1][3]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('F');
            readyFlag = false;
        }
        else if((keyPress == G_NOTE_KEY) && (readyFlag)){ // Play note G
            set_Note_Freq(noteArray[currentOctave-1][4]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('G');
            readyFlag = false;
        }
        else if((keyPress == A_NOTE_KEY) && (readyFlag)){ // Play note A
            set_Note_Freq(noteArray[currentOctave][5]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('A');
            readyFlag = false;
        }
        else if((keyPress == B_NOTE_KEY) && (readyFlag)){ // Play note B
            set_Note_Freq(noteArray[currentOctave][6]);
            create_samples(noteFreq, myWave);
            mySpeaker.PlayNote(noteFreq, 2, coefficientMatrix, Analog_out_data);
            write_to_SDCard('B');
            readyFlag = false;
        }
        else if((keyPress == RAISE_OCTAVE_KEY) && (readyFlag)){ // button O pressed
            // Raise an octave
            if(currentOctave < 7)
            currentOctave++;
            else
            printf("Cannot raise octave above 7.\r\n");
            readyFlag = false;
        }
        else if((keyPress == LOWER_OCTAVE_KEY) && (readyFlag)){ // button L pressed
            // Lower an octave
            if(currentOctave > 1)
            currentOctave--;
            else
            printf("Cannot lower octave below 1.\r\n");
            readyFlag = false;
        }
        
        else if((keyPress == RAISE_DURATION_KEY) && (readyFlag)){ // button E pressed
            noteDuration++;
            readyFlag = false;
        }
        else if((keyPress == LOWER_DURATION_KEY) && (readyFlag)){ // button D pressed
            if(noteDuration >= 2)
            noteDuration--;
            else
            printf("Cannot decrease duration further.\n\r");
            readyFlag = false;
        }
        else if((keyPress == RAISE_ADSR_KEY) && (readyFlag)){ // button R pressed
            // Raise Release Value
            if(currentReleaseVal < 5){
            currentADSR++;
            }
            else
            printf("Cannot raise value above 5.\r\n");
            readyFlag = false;
        }
        else if((keyPress == LOWER_ADSR_KEY) && (readyFlag)){ // button F pressed
            // Lower Release Value
            if(currentReleaseVal > 1){
            currentADSR--;
            }
            else
            printf("Cannot lower value below 1.\r\n");
            readyFlag = false;
        }
        else if((keyPress == CHANGE_WAVESHAPE_UP) && (readyFlag)){ // button T pressed
            // Change waveform shape to next waveform type
            switch(myWave){
                case sine:
                    myWave = square;
                    break;
                case square:
                    myWave = sawtooth;
                    break;
                case sawtooth:
                    myWave = sine;
                    break;
                default:
                break;
                }
                readyFlag = false;
        }
        else if((keyPress == CHANGE_WAVESHAPE_DOWN) && (readyFlag)){ // button G pressed
            // Change waveform shape to previous waveform type
            switch(myWave){
                case sine:
                    myWave = sawtooth;
                    break;
                case square:
                    myWave = sine;
                    break;
                case sawtooth:
                    myWave = square;
                    break;
                default:
                break;
                }
                readyFlag = false;
                
        }
    }
}