smart_alarm/sound.cpp
2025-10-04 22:25:57 +02:00

74 lines
2.3 KiB
C++

#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_alarm_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_alarm_audio() {
audio_playing = false;
pwm_set_gpio_level(SPEAKER_PIN, 128); // Return to silence level
}