#include "sound.h" #include "pico/stdlib.h" #include "hardware/pwm.h" #include "hardware/dma.h" #include "hardware/irq.h" #include "hardware/clocks.h" #include "alarm_sound.h" #include "pins.h" #define SAMPLE_RATE 22050 // 22.05kHz #define PWM_WRAP 255 // 8-bit resolution volatile float volume_multiplier = 4.0f; // todo load from flash static uint32_t audio_pos = 0; static bool audio_playing = false; // Timer callback for audio playback bool audio_timer_callback(repeating_timer_t *rt) { if (!audio_playing || audio_pos >= alarm_sound_wav_len) { audio_playing = false; return false; // Stop timer } // Get the original audio sample uint8_t sample = alarm_sound_wav[audio_pos++]; // Apply volume amplification // Convert to float, center around 0, apply multiplier, then center back around 128 float amplified = ((float)sample - 128.0f) * volume_multiplier + 128.0f; // Clamp the result to valid 8-bit range (0-255) if (amplified < 0.0f) amplified = 0.0f; else if (amplified > 255.0f) amplified = 255.0f; // Set PWM duty cycle with amplified sample pwm_set_gpio_level(SPEAKER_PIN, (uint8_t)amplified); return true; // Continue timer } void setup_pwm_audio() { // Set up PWM for audio output gpio_set_function(SPEAKER_PIN, GPIO_FUNC_PWM); uint slice_num = pwm_gpio_to_slice_num(SPEAKER_PIN); // Configure PWM for 8-bit resolution pwm_config config = pwm_get_default_config(); // Calculate clock divider for desired PWM frequency // PWM frequency should be much higher than audio sample rate // Target: ~250kHz PWM frequency for good audio quality float clock_div = (float)clock_get_hz(clk_sys) / (PWM_WRAP + 1) / 250000.0f; pwm_config_set_clkdiv(&config, clock_div); pwm_config_set_wrap(&config, PWM_WRAP); pwm_init(slice_num, &config, true); pwm_set_gpio_level(SPEAKER_PIN, 128); // Set to middle level (silence) } void play_audio() { audio_pos = 0; audio_playing = true; // Set up timer for sample rate (22.05kHz) static repeating_timer_t timer; int32_t delay_us = -1000000 / SAMPLE_RATE; // Negative for precise timing add_repeating_timer_us(delay_us, audio_timer_callback, NULL, &timer); } void stop_audio() { audio_playing = false; pwm_set_gpio_level(SPEAKER_PIN, 128); // Return to silence level }