Stopping a Melody

So now we know how to play a melody. For our Hot Potato game, we will want to let this melody play for a random amount of time and then stop it. How do we do that?

There is a very simple way we can do this. Instead of thinking in terms of time, just think in terms of number of notes. We'll just pick a random number of notes and play only that many.

Stop Melody 1

Here is a modified version of Mike Barela's chiptune sketch which only plays a random number of notes from the melody.

The modifications are minor. The majority of the new lines deal with seeding the pseudo-random number generator to increase actual randomness. It is the same approach as taken in the Circuit Playground D6 Dice guide. Here are the lines:

Download: file
  // Seed the random function with noise
  int seed = 0;
  
  seed += analogRead(12);
  seed += analogRead(7);
  seed += analogRead(9);
  seed += analogRead(10);
 
  randomSeed(seed);

With that taken care, we then just compute a random number for how many notes to play and modify the loop control appropriately.

Download: file
    int numNotesToPlay = random(numNotes);
    for (int thisNote = 0; thisNote < numNotesToPlay; thisNote++) { // play notes of the melody

Try pressing the right button multiple times and hear how the length of the melody varies.

Stop Melody 2

The one problem with the previous approach is it will play the melody at most only once. For our Hot Potato game, we may want the melody to play longer (multiple times) to make the game more exciting. So we want to be able to specify a random number bigger than the number of notes in the melody. Say the melody had 10 notes. A loop of 10 would play it once. But we want to be able to loop 20 times to play it twice. Or 17 times to play it once, and then the first 7 notes again. Etc.

But we can't simply increase the size of our random number and use the same loop structure. Once the loop number exceeds the number of notes in the melody, bad things will happen. For example, there is no value for melody[numNotes+1]. In fact, because of zero indexing, the highest defined note is only melody[numNotes-1].

So how do we deal with this? One answer is to simply use two variables. The first will be our random loop variable that will be the total number of notes to play. The second will be used to actually play the melody note. It will be incremented manually inside the loop, and once it exceeds the size of the melody, it will be set back to zero. That way the melody will loop.

Here's a second version of the sketch that makes these changes.

The two variables are setup before the loop:

Download: file
    int numNotesToPlay = random(numNotes,3*numNotes);
    int noteToPlay = 0;
    for (int thisNote = 0; thisNote < numNotesToPlay; thisNote++) { // play notes of the melody

We've increased the range of numNotesToPlay (up to 3 times the length of the song). Then, the new variable noteToPlay is used to actually play the note:

Download: file
      int noteDuration = 1000 / noteDurations[noteToPlay];
      CircuitPlayground.playTone(melody[noteToPlay], noteDuration);

And it is incremented and checked at the end of the loop:

Download: file
      // increment and check note counter
      noteToPlay++;
      if (noteToPlay >= numNotes) noteToPlay = 0;

Try again pressing the right button and play the melody multiple times to see how the length varies. This time however, the melody will play through at least once and then stop randomly at some point after that.

This guide was first published on Apr 05, 2017. It was last updated on Apr 05, 2017. This page (Stopping a Melody) was last updated on Aug 06, 2019.