Skip to main content

embedded_audio/
config.rs

1use crate::output::DutyMode;
2
3/// Recommended duty-modulation defaults for piezo / buzzer PWM audio.
4pub const DEFAULT_SAMPLE_RATE_HZ: u32 = 16_000;
5
6/// Fixed PWM carrier; must exceed ~2× the highest passed audio frequency.
7pub const DEFAULT_PWM_CARRIER_HZ: u32 = 32_000;
8
9/// Default timer period counts (ARR+1). Set from your timer setup:
10/// `period ≈ timer_clk / carrier_hz`.
11pub const DEFAULT_PWM_PERIOD: u16 = 1000;
12
13/// Engine and bank builder configuration.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct AudioConfig {
16    pub sample_rate_hz: u32,
17    pub pwm_period: u16,
18    pub duty_mode: DutyMode,
19    pub master_gain_q8: u8,
20}
21
22impl AudioConfig {
23    pub const fn new(sample_rate_hz: u32, pwm_period: u16, duty_mode: DutyMode) -> Self {
24        Self {
25            sample_rate_hz,
26            pwm_period,
27            duty_mode,
28            master_gain_q8: 255,
29        }
30    }
31
32    pub const fn default_duty() -> Self {
33        Self::new(
34            DEFAULT_SAMPLE_RATE_HZ,
35            DEFAULT_PWM_PERIOD,
36            DutyMode::SigmaDelta,
37        )
38    }
39
40    pub const fn master_gain_q8(mut self, gain: u8) -> Self {
41        self.master_gain_q8 = gain;
42        self
43    }
44}
45
46impl Default for AudioConfig {
47    fn default() -> Self {
48        Self::default_duty()
49    }
50}
51
52/// Crossfade step in Q8 units so a fade completes in `duration_ms`.
53#[inline]
54pub const fn crossfade_step_q8(duration_ms: u16, sample_rate_hz: u32) -> u8 {
55    if duration_ms == 0 || sample_rate_hz == 0 {
56        return 255;
57    }
58    let samples = (duration_ms as u32 * sample_rate_hz) / 1000;
59    if samples == 0 {
60        return 255;
61    }
62    let step = 255u32.div_ceil(samples);
63
64    if step > 255 { 255 } else { step as u8 }
65}