Arduino Sketch Music Sequence Demo

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

Leave a Comment

Your email address will not be published. Required fields are marked *

*
= 4 + 5

(Spamcheck Enabled)

Scroll to Top