#[cfg(feature = "fm")]
pub mod fm;
#[cfg(feature = "fm")]
pub use fm::{FmMapper, FmTick};
use crate::fixed::clamp_sample;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DutyMode {
#[default]
Linear,
SigmaDelta,
SigmaDelta2ndOrder,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SigmaDelta {
integrator: i32,
}
impl SigmaDelta {
pub const fn new() -> Self {
Self { integrator: 0 }
}
pub fn reset(&mut self) {
self.integrator = 0;
}
pub fn shape(&mut self, pcm: i8) -> i8 {
let input = (pcm as i32) << 8;
self.integrator += input;
let high = self.integrator >= 0;
if high {
self.integrator -= 65536;
}
if high { 127 } else { -127 }
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SigmaDelta2ndOrder {
e1: i32,
e2: i32,
}
impl SigmaDelta2ndOrder {
pub const fn new() -> Self {
Self { e1: 0, e2: 0 }
}
pub fn reset(&mut self) {
self.e1 = 0;
self.e2 = 0;
}
pub fn shape(&mut self, pcm: i8) -> i8 {
let input = (pcm as i32) << 8;
self.e1 += input;
let y1 = if self.e1 >= 0 { 32767 } else { -32768 };
self.e1 -= y1;
self.e2 += self.e1;
let y2 = if self.e2 >= 0 { 32767 } else { -32768 };
self.e2 -= y2;
let out = (y1 + y2) >> 9;
out.clamp(-127, 127) as i8
}
}
#[inline]
pub fn pcm_to_duty(pcm: i8, period: u16) -> u16 {
let mid = period as i32 / 2;
let swing = mid - 1;
let offset = (pcm as i32 * swing) / 127;
(mid + offset).clamp(1, period as i32 - 1) as u16
}
pub struct PwmMapper {
pub mode: DutyMode,
pub sigma_delta: SigmaDelta,
pub sigma_delta_2nd: SigmaDelta2ndOrder,
}
impl PwmMapper {
pub const fn new(mode: DutyMode) -> Self {
Self {
mode,
sigma_delta: SigmaDelta::new(),
sigma_delta_2nd: SigmaDelta2ndOrder::new(),
}
}
pub fn map(&mut self, pcm: i8, period: u16) -> u16 {
let shaped = match self.mode {
DutyMode::Linear => pcm,
DutyMode::SigmaDelta => self.sigma_delta.shape(pcm),
DutyMode::SigmaDelta2ndOrder => self.sigma_delta_2nd.shape(pcm),
};
pcm_to_duty(shaped, period)
}
pub fn reset(&mut self) {
self.sigma_delta.reset();
self.sigma_delta_2nd.reset();
}
}
#[inline]
pub fn limit_bus(sum: i32) -> i8 {
clamp_sample(sum)
}
#[inline]
pub fn pcm_to_i16(pcm: i8) -> i16 {
(pcm as i16) << 8
}
#[inline]
pub fn pcm_to_i32(pcm: i8) -> i32 {
(pcm as i32) << 24
}
#[inline]
pub fn pcm_to_dac_u8(pcm: i8) -> u8 {
(pcm as i16 + 128).clamp(0, 255) as u8
}
#[inline]
pub fn pcm_to_dac_u12(pcm: i8) -> u16 {
((pcm as i32 + 128) * 4095 / 255).clamp(0, 4095) as u16
}
#[inline]
pub fn pcm_to_dac_u16(pcm: i8) -> u16 {
((pcm as i32 + 128) << 8).clamp(0, 65535) as u16
}