// Speaker test for FirstPCB (Pico W + LM4871 amp)
// Plays three beeps (440, 880, 1760 Hz) and then a rising sweep, then repeats.
//
// The audio is a sine wave made with fast PWM on PIN_AUDIO. The R2/C1 filter on
// the board smooths the PWM, and C2/R3 feed it into the amp.
//
// Board package: "Raspberry Pi Pico W" (Arduino-Pico by Earle Philhower)

#define PIN_AUDIO                                                              \
  18 // the amp input wire on the schematic; change if yours differs

#define PWM_FREQ 100000   // PWM carrier, Hz (well above hearing)
#define PWM_RANGE 255     // duty resolution
#define SAMPLE_RATE 20000 // audio samples per second

// Output level, percent of full swing. The 8 ohm speaker is rated 0.25 W,
// so keep this at 40 or below. Start lower (20) if it is loud.
#define AMPLITUDE_PCT 40

#define RAMP_MS 10 // fade in/out time per tone to reduce clicks

int8_t sine_table[256];

void buildTable() {
  for (int i = 0; i < 256; i++) {
    sine_table[i] = (int8_t)lroundf(127.0f * sinf(2.0f * PI * i / 256.0f));
  }
}

void silence() {
  analogWrite(PIN_AUDIO, PWM_RANGE / 2); // mid duty = no AC signal
}

// Play a tone that glides from f_start to f_end over ms milliseconds
void play(float f_start, float f_end, unsigned long ms) {
  const unsigned long period_us = 1000000UL / SAMPLE_RATE;
  const unsigned long total_samples = (ms * SAMPLE_RATE) / 1000UL;
  const unsigned long ramp_samples = (RAMP_MS * SAMPLE_RATE) / 1000UL;

  uint32_t phase = 0;
  unsigned long next = micros();

  for (unsigned long n = 0; n < total_samples; n++) {
    float f = f_start + (f_end - f_start) * ((float)n / total_samples);
    uint32_t phase_inc = (uint32_t)(f * 4294967296.0f / SAMPLE_RATE);
    phase += phase_inc;

    // fade in at the start, fade out at the end (env is 0..256)
    unsigned long edge = (n < total_samples - n) ? n : (total_samples - n);
    int env =
        (edge >= ramp_samples) ? 256 : (int)((edge * 256UL) / ramp_samples);

    int s = sine_table[phase >> 24]; // -127..127
    int level = (s * AMPLITUDE_PCT / 100) * env / 256;
    analogWrite(PIN_AUDIO, PWM_RANGE / 2 + level);

    next += period_us;
    while ((long)(micros() - next) < 0) {
    }
  }
  silence();
}

void setup() {
  Serial.begin(115200);
  unsigned long start = millis();
  while (!Serial && millis() - start < 3000) {
  }

  buildTable();
  analogWriteFreq(PWM_FREQ);
  analogWriteRange(PWM_RANGE);
  silence();
  Serial.println("Speaker test starting");
}

void loop() {
  Serial.println("Beep 440 Hz");
  play(440, 440, 500);
  delay(250);

  Serial.println("Beep 880 Hz");
  play(880, 880, 500);
  delay(250);

  Serial.println("Beep 1760 Hz");
  play(1760, 1760, 500);
  delay(500);

  Serial.println("Sweep 200 -> 2000 Hz");
  play(200, 2000, 3000);

  Serial.println("--- cycle done ---");
  delay(3000);
}
