Summary of Arduino Sketch Music Sequence Demo
This Arduino sketch, "MusicSequenceDemo," demonstrates how to generate two simultaneous audio-frequency square wave tones on pins 4 and 5. It uses a MelodyGenerator class to manage rhythm patterns and pitch changes, allowing parallel timed tasks with different musical note sequences and durations. The project uses event-loop control to update the tones and rhythms, creating layered musical sequences using middle C and a musical fifth (G). The program supports quarter, eighth, and sixteenth note durations and rests in the patterns.
Parts used in the MusicSequenceDemo project:
- Arduino board (e.g., Arduino Uno)
- Two output pins (digital pins 4 and 5)
- Speaker or piezo buzzer (connected to pins 4 and 5)
- Wires for connections
- Power source for Arduino
This sketch is used by Exercise: Music Sequencer.

Full Source Code
The full code is all in one file MusicSequenceDemo.ino.
// 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 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 = "Cq Rq Cq Rq Ge Re Ge Re Ge Re Ge Re Gq Rq Ge Rq";
const char *rhythm2 = "Cs Rs Cs Rs Cs Rs Cs Rs Ge Re Ge Re";
// Define the timing constants for the rhythmic elements.
const long quarter_duration = 500000; // 120 bpm
/****************************************************************/
// Define the timing constants for the audio output.
// 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 low_pitch_half_period = 1911;
// 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):
const long high_pitch_half_period = 1274;
/****************************************************************/
// 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 = low_pitch_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) {
// cycle the speaker to create a tone
digitalWrite( output_pin, output_value);
} else {
// resting, turn speaker off
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 = high_pitch_half_period;
resting = false;
continue;
} else if (next_symbol == 'C') {
tone_interval = low_pitch_half_period;
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, rhythm2 );
// 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 );
}
/****************************************************************/
// 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);
}
/****************************************************************/Source: Arduino Sketch MusicSequenceDemo
