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