Piezo buzzer playing multiple tones at once (should be one)

I'm working on a project where inputs from an array of photo interrupters are read by an Arduino ATMega. When the arduino senses a sensor has been triggered, it activates a specific LED on a NeoPixel strip and a specific tone on a piezo buzzer for the duration of the trigger. When not triggered, each LED in the array cycles a random sequence of colors with no buzzer sound. For those interested, there was a previous thread addressing a different set of issues on the same project, and the forum was incredibly helpful in resolving them:

https://forum.arduino.cc/index.php?topic=668665.0

The problem now: the LEDs work perfectly when triggered, but the buzzer sounds like it's trying to make more than one tone (I can faintly hear the correct tone in the resulting noise). I should mention that on power-up the circuit runs a test, flashing the LEDs with the corresponding buzzer tone, and that all works fine. The audio is clean and the tones are correct. But when the photo interrupter signal enters the picture, that's when the audio is corrupted.

Here is the code:

// https://forum.arduino.cc/index.php?topic=668665
// 5 mar 2020

#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
#define LED_PIN 10

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 12


#define GS4 415  //  1*
#define AS4 466  //  2
#define B4  494  //  3
#define CS5 554  //  4
#define DS5 622  //  5* 
#define F5  698  //  6
#define FS5 740  //  7  
#define GS5 831  //  8*
#define AS5 932  //  9*
#define C6  1047 //  10*
#define D6  1175 //  11
#define E6  1319 //  12

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

//states
enum {ST1_idle, ST1_triggered} currentState1 = ST1_idle;
enum {ST2_idle, ST2_triggered} currentState2 = ST2_idle;
// ala https://www.gammon.com.au/statemachine

//pulse, to prove there's no blocking
unsigned long previousPulse;
bool pulseState;
int pulseInterval = 500;

const byte sensors[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
const byte numberOfSensors = sizeof(sensors) / sizeof(sensors[0]);
const int speakerPin = 6;
const int notes[] = {GS4, AS4, B4, CS5, DS5, F5, FS5, GS5, AS5, C6, D6, E6};

enum theStates {ST_idle, ST_triggered};
theStates currentState[numberOfSensors] = {ST_idle, ST_idle};

unsigned long lastBlink[numberOfSensors];
int blinkInterval[numberOfSensors] = {480, 520, 450, 510, 380, 490, 500, 475, 415, 390, 465, 500};
unsigned long triggeredAt[numberOfSensors];

void setup()
{
  // initialize serial communication:
  Serial.begin(9600);
  Serial.print("setup() ... ");

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, pulseState);

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  Serial.println(" done");
  delay(1000);


  //  AV test
  strip.setPixelColor(0, 125, 0, 125);
  strip.show();
  tone(speakerPin, notes[0]);
  delay(100);

  strip.setPixelColor(1, 0, 125, 125);
  strip.show();
  tone(speakerPin, notes[1]);
  delay(100);

  strip.setPixelColor(2, 125, 125, 0);
  strip.show();
  tone(speakerPin, notes[2]);
  delay(100);

  strip.setPixelColor(3, 85, 85, 85);
  strip.show();
  tone(speakerPin, notes[3]);
  delay(100);

  strip.setPixelColor(4, 85, 165, 0);
  strip.show();
  tone(speakerPin, notes[4]);
  delay(100);

  strip.setPixelColor(5, 0, 85, 165);
  strip.show();
  tone(speakerPin, notes[5]);
  delay(100);

  strip.setPixelColor(6, 85, 0, 165);
  strip.show();
  tone(speakerPin, notes[6]);
  delay(100);

  strip.setPixelColor(7, 165, 0, 85);
  strip.show();
  tone(speakerPin, notes[7]);
  delay(100);

  strip.setPixelColor(8, 165, 85, 0);
  strip.show();
  tone(speakerPin, notes[8]);
  delay(100);

  strip.setPixelColor(9, 145, 45, 45);
  strip.show();
  tone(speakerPin, notes[9]);
  delay(100);

  strip.setPixelColor(10, 125, 0, 125);
  strip.show();
  tone(speakerPin, notes[10]);
  delay(100);

  strip.setPixelColor(11, 0, 125, 125);
  strip.show();
  tone(speakerPin, notes[11]);
  delay(100);


  //  turn off
  strip.clear();
  //  strip.fill(0, 0, 11);
  noTone(speakerPin);
  delay(2000);

  // passcode test
  strip.setPixelColor(8, 0, 125, 125);
  strip.show();
  tone(speakerPin, notes[8]);
  delay(300);

  strip.setPixelColor(9, 145, 45, 45);
  strip.show();
  tone(speakerPin, notes[9]);
  delay(300);

  strip.setPixelColor(7, 165, 0, 85);
  strip.show();
  tone(speakerPin, notes[7]);
  delay(300);

  strip.setPixelColor(0, 125, 0, 125);
  strip.show();
  tone(speakerPin, notes[0]);
  delay(300);

  strip.setPixelColor(4, 145, 185, 0);
  strip.show();
  tone(speakerPin, notes[4]);
  delay(300);


  //  turn off
  strip.clear();
  noTone(speakerPin);
  delay(2000);


  Serial.println(" ");
  for (byte i = 0; i < numberOfSensors; i++)
  {
    Serial.print(i);
    Serial.println(" is idle");
  }
}//setup


void loop()
{
  manageStates();
  doPulse();
} //loop



void manageStates()
{
  for (byte i = 0; i < numberOfSensors; i++)
  {
    int val = analogRead(i); //val's not need anywhere else, so local to manage states
    //if val *is* needed elsewhere, make an array val[numberOfSensors] as a global

    switch (currentState[i]) //ST_idle, ST_triggered
    {
      case ST_idle:
        if (millis() - lastBlink[i] >= blinkInterval[i])
        {
          lastBlink[i] = millis();
          strip.setPixelColor(random(i, 1), random(i, 50), random(i, 50), random(i, 50));
          strip.show();
          noTone(speakerPin);
        }

        if (val < 100)
        {
          currentState[i] = ST_triggered;
          Serial.print(i);
          Serial.println(" is triggered");
        }

        break;

      case ST_triggered:
        strip.setPixelColor(i, 200, 0, 0);
        strip.show();
                tone(speakerPin, notes[i]);

        if (val >= 100) //lost trigger
        {
          currentState[i] = ST_idle;
          Serial.print(i);
          Serial.println(" is idle");
        }
        break;

    }//switch i
  }//for
}//manageSnyttates

void doPulse()
{
  if (millis() - previousPulse >= pulseInterval)
  {
    previousPulse = millis();
    pulseState = !pulseState;
    digitalWrite(LED_BUILTIN, pulseState);
  }
}//do pulse

I've read there can be problems using NeoPixels with a piezo buzzer, but if that's the issue I would think the problem would present during the test at power-up.

Ideas?

You are not detecting a state change. See the StateChangeDetection example sketch for how this can be done.

As a result you are calling tone() thousands of times a second for the duration of the stimulus, whereas you
only want to call it once at the start of the stimulus.

In a state machine actions only happen on transitions between states.

MarkT:
You are not detecting a state change. See the StateChangeDetection example sketch for how this can be done.

As a result you are calling tone() thousands of times a second for the duration of the stimulus, whereas you
only want to call it once at the start of the stimulus.

In a state machine actions only happen on transitions between states.

Thank you! Will investigate and implement.