Stuttering tone, possibly software timer related?

I’m getting stuttering issues with calls to tone, and it’s really a head-scratcher for me. The code is pasted below. The main thing to realize is that I’m calling tone, then setting a timer, then calling noTone when the timer completes. The function tick is called every time the main Arduino loop runs. So the timer is basically in charge of shutting off the tone when the duration is over, and then tick advances to the next note and starts playing it.

The output from the buzzer is very stuttered. I can hear the tones in there, but it’s garbled and broken.

In the past, I have gotten it to make tones using tone(pin, freq, duration) and it’s on pin A4, so I know that should work. I’m just wondering if there is some kind of issue with running software timers (ie. new Timer()) while also doing tone playback.

Update: For future reference, I was able to determine that the timers definitely seem to be an issue, although it’s not necessarily this timer in my MusicPlayer class. I’ve got a few other timers running, specifically one that is controlling a NeoPixel LED strip. If that timer is running, or more accurately, if it is stopping and restarting, the tone function stutters.

In other words, if you’re using tone and Timer together in the same app, you might run into stutter problems.

    #include "Particle.h";
    #include "pitches.h";
    #include "definitions.h";
    #include "MusicPlayer.h";

    MusicPlayer::MusicPlayer() {
      timer = new Timer(1000, &MusicPlayer::endNote, *this);
      noteInProgress = false;
      tuneInProgress = false;
    }

    bool MusicPlayer::playInProgress() {
      return tuneInProgress;
    }

    void MusicPlayer::playTune(String tuneId) {
      if(noteInProgress) {
        return;
      }

      if(tuneId == "win") {
        melodyIndex = 0;
      } else if(tuneId == "live") {
        melodyIndex = 1;
      }

      noteIndex = 0;
      tuneInProgress = true;
      noteInProgress = false;
    }

    void MusicPlayer::tick() {
      if(noteInProgress || !tuneInProgress) {
        return;
      }

      int note = notes[melodyIndex][noteIndex];
      int duration = 1000 / noteDurations[melodyIndex][noteIndex];
      this->playNote(note, duration);
    }

    void MusicPlayer::playNote(int note, int duration) {
      noteInProgress = true;
      tone(SPEAKER_PIN, note);

      timer->changePeriod(duration);
      timer->reset();
    }

    void MusicPlayer::endNote() {
      timer->stop();
      noTone(SPEAKER_PIN);
      noteInProgress = false;

      noteIndex++;
      if(noteIndex > noteCounts[melodyIndex]) {
        this->endPlay();
      }
    }

    void MusicPlayer::endPlay() {
      timer->stop();
      noTone(SPEAKER_PIN);
      noteInProgress = false;
      tuneInProgress = false;
    }

Why are you using the timers for switching of the tone? Doesn’t the tone end after duration anyway?

On the other hand, you might experience the stuttering not actually because of the timer stopping tone but rather because of failing to start the next note in time.
You could purposely choose a overly long duration (since you force stop it anyway) and/or trigger the following note by the timer instead if an async function.

Another option for more robust timing than Software Timers is the interrupt driven SparkIntervalTimer library.

The reason for using the timers is to allow for concurrency. With tone(pin, freq, duration), it’s a blocking call for the duration, correct? I’m using the same sort of setup to control a strip of the Neopixel LEDs and do a light show at the same time as the audio is playing. That’s the idea at least.

I’ll take a look at the timer library you mentioned. I’m definitely interested in something interrupt based.

In that case, you could consider offloading the playing to a dedicated user thread where a blocking tone() wouldn’t matter.

Something like this

void blink() {
  digitalWrite(D7, HIGH);
  delay(500);
  digitalWrite(D7, LOW);
  delay(500);
}

void hardware_fn() {
  // do setup
  pinMode(D7, OUTPUT);
  // loop forever
  for(;;) {
    blink();
  }
}
void setup() {
  // Thread will start and run hardware_fn
  Thread("hardware", hardware_fn, OS_THREAD_PRIORITY_DEFAULT + 1);
  // Use a slightly higher priority to make sure it interrupts the system and application thread
}

Oh wow! I didn’t even know threads existed. That’s awesome! I don’t see them documented in the official docs (unless it’s System Thread, but that seems different). Am I missing it somewhere?

(Thanks for the help, btw.)

It’s not yet documented but available for the daring ones since FreeRTOS was introduced :wink:

These are not the same as SYSTEM_THREAD(), but both are based on the same FreeRTOS foundation.

Doing some research, it seems that tone(pin, freq, duration) is actually non-blocking, and therefore my fancy timer-based concurrency solution may be unnecessary. On the downside, all the other timers I have running might be somehow interfering with the tone call, and they will be harder to replace.

If you have multiple Software Timers, you need to be aware that they all run on the same FreeRTOS thread and have to be cooperative.
So if one of them hogs the thread, the others will have to wait and won’t be able to execute before the uncooperative one finishes.

1 Like

Thanks for the tip. I’m trying to make things as simple as possible in the timer callbacks. Instead of doing anything fancy, they just set a few variables and wait for the tick function called in the main loop to actually do the heavy lifting.

1 Like