piano 4 octaves, 8 notes, 3 threads, 2 outputs, no bug, clean code, comments, remove old .h
Fork of Nucleo_piano_CS435 by
main.cpp
- Committer:
- aknin001
- Date:
- 2018-07-05
- Revision:
- 10:a5b01cfb5097
- Parent:
- 9:9a36b66869fa
File content as of revision 10:a5b01cfb5097:
#include "mbed.h" #include "TextLCD.h" #include <string.h> #include <math.h> // Define screen TextLCD lcd(PA_8, PA_9, PC_7, PB_6, PA_7, PA_6, PA_5); // RS, RW, E, D4-D7 // Define Bus In for Buttons (Do, Re, Mi, Fa, Sol, La, Si, Do) BusIn Bus_In(PA_0, PA_1, PA_4, PB_0, PA_10, PB_3, PB_5, PB_4); // Define the PWM speaker PwmOut speaker(PB_10); // Define Bus In for Octave Button BusIn Bus_In_Octave(PB_9); // Define Serial Serial pc(PA_2, PA_3); //Define variables for sound struct NoteReference { // struct note reference char name[4]; // note name int mask; // note mask for pressed of not double frequency; // note base frequency (octave 0) double add[4]; // add value for octave balancing (from octave 0 to 3) }; // NoteReference tab for 8 notes (1 octave) NoteReference noteReference[8] = { {"Do", 0x1, 262.0, {0.0, -1.0, -1.5, -3.0}}, {"Re", 0x2, 294.0, {0.0, -1.0, -1.0, -3.0 }}, {"Mi", 0x4, 330.0, {0.0, -1.0, -1.5, -3.0}}, {"Fa", 0x8, 349.0, {0.0, 0.5, -1.0, 2.0}}, {"Sol", 0x10, 392.0, {0.0, 0.0, 0.0, 0.0}}, {"La", 0x20, 440.0, {0.0, 0.0, 0.0, 0.0}}, {"Si", 0x40, 494.0, {0.0, 0.0, -1.0, -1.0}}, {"Do", 0x80, 523.0, {0.0, 0.0, 0.0, 0.0}} }; volatile int stateButtons = 0; // State of notes buttons volatile int stateButtonOctave = 0; // State of octave button volatile int octave = 0; // octave value //Define variable for display char bufferOutput[30] = ""; // buffer for notes and octave //Define variables for synchronization Mutex lockBufferOutput; // Mutex for BufferOutput variable Mutex lockOctave; // Mutex for octave variable /** Function that refresh the value of stateButtons depending on data input from Bus_In. **/ void refresh_state_buttons() { stateButtons = Bus_In & Bus_In.mask(); // read the bus and mask out bits not being used } /** Function that output notes on the buzzer. **/ void play_music(int notes, double frequency) { speaker.period(1.0 / frequency); // set the period to 1 / frequency of notes while (stateButtons == notes) { // while button is pressed refresh_state_buttons(); // refresh stateButtons } } /** Function that generate a frequency depending on pressed notes and actual octave. **/ double generate_frequency(double frequency, int actualOctave) { frequency = 0.0; // init frequency to 0 lockBufferOutput.lock(); // lock lockBufferOutput Mutex for writing new notes on bufferOutput variable strcpy(bufferOutput, ""); // init bufferOuput to empty string for (int i = 0; i < 8; i++) { // iterate on all element of noteReference tab if (!(stateButtons & noteReference[i].mask)) // if note is pressed { // add note frequency to frequency (base frequency * 2^octave + add) frequency += noteReference[i].frequency * pow(2.0, (double)actualOctave) + noteReference[i].add[actualOctave]; strcat(bufferOutput, noteReference[i].name); // concatenate bufferOutput and new note name strcat(bufferOutput, " "); // concatenate with space separator } } lockBufferOutput.unlock(); // unlock bufferOutput at the end of the loop, end of writing return (frequency); // return the frequency to play on buzzer } /** Check if buttons if pressed and play notes otherwise set duty cycle to 0. **/ void check_buttons(double frequency, int actualOctave) { if (stateButtons == 0xFF) // if nothing pressed { speaker = 0; // duty cycle equal to 0 lockBufferOutput.lock(); // lock BufferOutput Mutex for writing on bufferOutput variable strcpy(bufferOutput, ""); // set bufferOutput to empty string lockBufferOutput.unlock(); // unlock BufferOutput Mutex } else { // if buttons pressed lockOctave.lock(); // lock lockOctave Mutex for reading octave value actualOctave = octave; // read octave value lockOctave.unlock(); // unlock lockOctave Mutex frequency = generate_frequency(frequency, actualOctave); // generate frequency depending on notes and octave speaker = 0.5; // set duty cycle to 0.5 for volume play_music(stateButtons, frequency); // play notes on buzzer } } /** Thread for playing notes. check states of buttons notes and play music. **/ void run_music() { double frequency = 0.0; // double for total frequency int actualOctave = 0; // int for actual octave while (true) // Infinte loop to catch buttons notes events { refresh_state_buttons(); // refresh state_buttons check_buttons(frequency, actualOctave); // check if buttons are pressed and play music Thread::wait(100); // Thread wait 100 for scheduling } } /** Function that will display notes and gammes on LCD screen and PC device. Only called when a change on BufferOutput occurs. **/ void refresh_display_notes(char *old_buffer) { lcd.cls(); // clear LCD screen lockOctave.lock(); // lock lockOctave Mutex for reading octave variable if (strcmp(bufferOutput, "")) // if buffer not empty { lcd.printf("%s- O[%d]", bufferOutput, octave); // display notes and actual octave on LCD screen pc.printf("Play notes: %s with octave %d\n", bufferOutput, octave); // display notes and actual octave on PC device } else { lcd.printf("Octave = %d", octave); // only display octave on screen if no button pressed pc.printf("Release notes\n"); // display release message on PC device } lockOctave.unlock(); // unlock lockOctave strcpy(old_buffer, bufferOutput); // save value bufferOutput in old_buffer } /** Function that will display the octave on screen if a change in octave occurs. **/ int refresh_display_octave(int old_octave) { lockOctave.lock(); // lock lockOctave for reading octave variable if (old_octave != octave) // if change occurs { lcd.cls(); // clear screen lcd.printf("Octave = %d", octave); // display new octave on LCD screen pc.printf("[INFO] - Change octave %d to octave %d\n", old_octave, octave); // display new octave on PC device old_octave = octave; // save the new value to old_octave } lockOctave.unlock(); // Unlock lockOctave return old_octave; // return old_octave } /** Thread display function that output notes and octaves on the LCD screen and the PC device by Serial. **/ void run_display() { char old_buffer[30] = ""; // buffer of old value BufferOutput variable, init to empty string by default int old_octave = 0; // value for old octave variable, init to zero by default lockOctave.lock(); // Lock lockOctave Mutex for reading octave variable lcd.printf("Octave = %d", octave); // Display default octave on screen until event occurs lockOctave.unlock(); // Unlock lockOctave Mutex while(true) // Infinite loop to run display { lockBufferOutput.lock(); // Lock lockBufferOutput Mutex for reading BufferOutput if (strcmp(old_buffer, bufferOutput)) // if bufferOutput has change refresh_display_notes(old_buffer); // output notes else old_octave = refresh_display_octave(old_octave); // else output gamme if change lockBufferOutput.unlock(); // Unlock lockBufferOutput Mutex Thread::wait(100); // Thread wait 100 for synchronization } } /** Function that refresh the value of stateButtonOctave variable based on the input data from Bus_In_Octave. **/ void check_state_button_octave() { stateButtonOctave = Bus_In_Octave & Bus_In_Octave.mask(); } /** Thread for Octave. Catch octave button state and increment the octave value if button has been pressed. **/ void run_octave() { while(true) // Infinite loop for octave Thread { check_state_button_octave(); // refresh state_button_octave if (stateButtonOctave == 0x0) // if button is pressed { lockOctave.lock(); // lock lockOctave Mutex, enter in critical section octave = (octave == 3) ? 0 : octave + 1; // increment octave or set to 0 lockOctave.unlock(); // unlock lockOctave Mutex, end of critical section while (stateButtonOctave == 0x0) // continue check button state until it is unpressed check_state_button_octave(); // refresh state_button_octave } Thread::wait(100); // Thread wait 100 for scheduling } } /** Main function Piano Project. Launch 3 threads for playing music, display notes and octaves. **/ int main() { Thread music_thread; // Thread for playing music Thread display_thread; // Thread for display notes and octave Thread octave_thread; // Thread for octave lcd.printf("CS435\nPiano Project"); // Print welcome string on LCD screen wait(3); lcd.cls(); // Clear the screen music_thread.start(run_music); // Start music thread display_thread.start(run_display); // Start display thread octave_thread.start(run_octave); // Start octave thread while(1); // Infinite loop for main thread }