This demo uses an array of LEDs and two sets of speakers. As there are 12 different steps in an octave, I used 4 LEDs to represent the steps, leaving 4 undefined light combinations. Different songs can be programmed in to the code.
Sources: Tech Excercise music Sequencer</pre> // MusicSequenceDemo.ino : demonstrate generation of two simultaneous tones at different rates and patterns // The example program generates audio-frequency square waves at different // pitches and patterns on pins 4 and 5 to demonstrate an event-loop control // structure allowing parallel execution of multiple timed tasks with pattern // generation. // Define the pin numbers on which the outputs are generated. const int outputPin1 = 4; const int outputPin2 = 5; //Define LED outputs const int outputPin3 = 7; const int outputPin4 = 8; const int outputPin5 = 9; const int outputPin6 = 10; int outputPins[4] = {outputPin3, outputPin4, outputPin5, outputPin6}; /****************************************************************/ // Define the rhythm patterns for the two outputs using a simple pattern language. // Note values: // G = high pitch // C = low pitch // R = rest // Note durations: // q = quarter note // e = eighth note // s = sixteenth note // A note value symbol affects the pitch generated by the successive note duration symbols. // Any unknown symbol (including spaces) are ignored. const char *rhythm1 = "fs Rs fs Rs Es Rs fs Rs As Rs Es Rs fs Rs fs Rs"; const char *rhythm2 = "Cs Rs Cs Rs Cs Rs Cs Rs Ge Re Ge Re"; const char *rhythm3 = "fq Rq fq Rq Ee Re fe Re Ae Re Ee Re fq Rq fe Rq"; // Define the timing constants for the rhythmic elements. const long quarter_duration = 500000; // 120 bpm /****************************************************************/ // Define the timing constants for the audio output. //A3 is 1e6/(440)=2273 const long A3_half_period = 2273; bool a_Lights [4] = {false, false, false, true}; //A#3 is 1 step down from A3= 233.08, half period is const long AS3_half_period = 2145; bool aS_Lights [4] = {false, false, true, false}; //B3 is 2 steps down from A3= 246.94 const long B3_half_period = 2025; bool b_Lights [4] = {false, false, true, true}; // The low pitch is middle-C, the high pitch is the G a fifth above it. Given // A3 of 220 Hz and equal temperament, middle C4 has a frequency // 220*pow(2, 3.0/12) = 261.626 Hz. // The half-period in microseconds is 1e6/(2*261.626), rounded to an integer: const long C4_half_period = 1911; bool c_Lights [4] = {false, true, false, false}; const long CS4_half_period = 1804; bool cS_Lights [4] = {false, true, false, true}; const long D4_half_period = 1703; bool d_Lights [4] = {false, true, true, false}; const long DS4_half_period = 1607; bool dS_Lights [4] = {false, true, true, true}; const long E4_half_period = 1517; bool e_Lights [4] = {true, false, false, false}; const long F4_half_period = 1432; bool f_Lights [4] = {true, false, false, true}; const long FS4_half_period = 1351; bool fS_Lights [4] = {true, false, true, false}; // The just intonation ratio for a musical fifth is 3/2, so // G4 = 1.5*261.626 = 392.438 Hz, and the half period duration in // microseconds is 1e6/(2*392.438): G4_half_period const long G4_half_period = 1274; bool g_Lights [4] = {true, false, true, true}; const long GS4_half_period = 1204; bool gS_Lights [4] = {true, true, false, false}; void LEDflip(bool lights[4]) { for(int i=0; i<4; i++) { if(lights[i]==true) { digitalWrite(outputPins[i], HIGH); } else{ digitalWrite(outputPins[i], LOW); } } } /****************************************************************/ // C++ class to generate a rhythmic sound pattern on a single output. class MelodyGenerator { private: // number of the pin to use for output int output_pin; // current output state int output_value; /// the time elapsed in microseconds since the last waveform update occurred unsigned long tone_elapsed; /// the time elapsed in microseconds since the last pattern update occurred unsigned long pattern_elapsed; // interval between output waveform transitions in microseconds long tone_interval; // flag which indicates that no tone is generated bool resting; // interval between pattern transitions in microseconds long pattern_interval; // current pattern string const char *pattern_string; // current position within the pattern string int pattern_pos; public: // Constructor to initialize an instance of the class. This does not // configure the hardware, only the internal state. MelodyGenerator( int pin, const char *pattern ); // Update function to be called as frequently as possible to generate the // output. It requires the number of microseconds elapsed since the last // update. void update(long interval); }; // Constructor for an instance of the class. MelodyGenerator::MelodyGenerator(int pin, const char *pattern) { // initialize the state variables output_pin = pin; output_value = LOW; tone_elapsed = 0; pattern_elapsed = 0; tone_interval = C4_half_period; resting = false; pattern_interval = quarter_duration; pattern_string = pattern; pattern_pos = 0; } // Update polling function for an instance of the class. void MelodyGenerator::update(long interval) { // Check whether the next transition time has been reached, and if so, update // the state and hardware output. tone_elapsed += interval; if (tone_elapsed >= tone_interval) { // Reset the timer according to the desired interval to produce a correct // average rate even if extra time has passed. tone_elapsed -= tone_interval; // Update the output pin to generate the audio waveform. output_value = !output_value; if (!resting) { digitalWrite( output_pin, output_value); } else { digitalWrite( output_pin, LOW); } } //----------------------------------------------- // Check whether the pattern interval has expired. pattern_elapsed += interval; if (pattern_elapsed >= pattern_interval) { pattern_elapsed -= pattern_interval; // Process one or more symbols from the rhythm pattern. This will process // any note value symbols until a note duration symbol is reached. for(;;) { char next_symbol = pattern_string[pattern_pos]; // Advance counter to next pattern string position. pattern_pos++; // if the next symbol is the end of the string, recycle to the beginning. if (next_symbol == 0) { pattern_pos = 0; continue; } else if (next_symbol == 'G') { tone_interval = G4_half_period; LEDflip(g_Lights); resting = false; continue; } else if (next_symbol == 'A') { tone_interval = A3_half_period; LEDflip(a_Lights); resting = false; continue; } else if (next_symbol == 'B') { tone_interval = B3_half_period; LEDflip(b_Lights); resting = false; continue; } else if (next_symbol == 'C') { tone_interval = C4_half_period; LEDflip(c_Lights); resting = false; continue; } else if (next_symbol == 'D') { tone_interval = D4_half_period; LEDflip(d_Lights); resting = false; continue; } else if (next_symbol == 'E') { tone_interval = E4_half_period; LEDflip(e_Lights); resting = false; continue; } else if (next_symbol == 'F') { tone_interval = F4_half_period; LEDflip(f_Lights); resting = false; continue; } else if (next_symbol == 'a') { tone_interval = AS3_half_period; LEDflip(aS_Lights); resting = false; continue; } else if (next_symbol == 'c') { tone_interval = CS4_half_period; LEDflip(cS_Lights); resting = false; continue; } else if (next_symbol == 'd') { tone_interval = DS4_half_period; LEDflip(dS_Lights); resting = false; continue; } else if (next_symbol == 'f') { tone_interval = FS4_half_period; LEDflip(fS_Lights); resting = false; continue; } else if (next_symbol == 'g') { tone_interval = GS4_half_period; LEDflip(gS_Lights); resting = false; continue; } else if (next_symbol == 'R') { resting = true; continue; } else if (next_symbol == 'q') { pattern_interval = quarter_duration; break; // leave the symbol-reading loop } else if (next_symbol == 'e') { pattern_interval = quarter_duration / 2; break; // leave the symbol-reading loop } else if (next_symbol == 's') { pattern_interval = quarter_duration / 4; break; // leave the symbol-reading loop } else { // all other symbols are ignored continue; } } } } /****************************************************************/ // Global variables. // Declare two instances of the pattern generator. MelodyGenerator generator1( outputPin1, rhythm1 ); MelodyGenerator generator2( outputPin2, rhythm3 ); // The timestamp in microseconds for the last polling cycle, used to compute // the exact interval between output updates. unsigned long last_update_clock = 0; /****************************************************************/ /****************************************************************/ // This function is called once after reset to initialize the program. void setup() { // Initialize two digital output pins, one for each pattern generator. pinMode( outputPin1, OUTPUT ); pinMode( outputPin2, OUTPUT ); pinMode(outputPin3, OUTPUT); pinMode(outputPin4, OUTPUT); pinMode(outputPin5, OUTPUT); pinMode(outputPin6, OUTPUT); } /****************************************************************/ // This function is called repeatedly as fast as possible from within the // built-in library to poll program events. void loop() { // The timestamp in microseconds for the last polling cycle, used to compute // the exact interval between output updates. static unsigned long last_update_clock = 0; // Read the microsecond clock. unsigned long now = micros(); // Compute the time elapsed since the last poll. This will correctly handle wrapround of // the 32-bit long time value given the properties of twos-complement arithmetic. unsigned long interval = now - last_update_clock; last_update_clock = now; // update the pattern generators generator1.update(interval); generator2.update(interval); } /****************************************************************/ <pre>
Leave a Reply
You must be logged in to post a comment.