pub type Phase = u32;
#[inline]
pub const fn hz_to_phase_inc(freq_hz: u32, sample_rate_hz: u32) -> u32 {
if sample_rate_hz == 0 {
0
} else {
(((freq_hz as u64) << 32) / (sample_rate_hz as u64)) as u32
}
}
#[inline]
pub const fn phase_index(phase: Phase) -> u8 {
(phase >> 24) as u8
}
#[inline]
pub fn sin_table(index: u8) -> i8 {
let x = index as i32;
let y = if x < 128 { x * 2 } else { (255 - x) * 2 };
let quad = (y * (255 - y)) >> 8;
let s = if x < 128 { quad } else { -quad };
s.clamp(-127, 127) as i8
}
#[inline]
pub fn lerp_i8(a: i8, b: i8, frac: u8) -> i8 {
let ai = a as i32;
let bi = b as i32;
let v = ai + ((bi - ai) * frac as i32) / 256;
v.clamp(-128, 127) as i8
}
#[inline]
pub fn apply_gain_q8(sample: i8, gain_q8: u8) -> i8 {
((sample as i32 * gain_q8 as i32) >> 8).clamp(-128, 127) as i8
}
#[inline]
pub fn mix_crossfade(a: i8, b: i8, t_q8: u8) -> i8 {
let ai = a as i32;
let bi = b as i32;
let t = t_q8 as i32;
let v = ai + ((bi - ai) * t) / 256;
v.clamp(-128, 127) as i8
}
#[inline]
pub fn clamp_sample(v: i32) -> i8 {
v.clamp(-128, 127) as i8
}
#[allow(clippy::approx_constant)]
pub fn db_to_q8(db: f32) -> u8 {
if db >= 0.0 {
return 255;
}
if db <= -48.0 {
return 0;
}
let x = db / 20.0;
let lin = 1.0 + 2.3026 * x + 2.651 * x * x + 2.03 * x * x * x;
(lin * 255.0).clamp(0.0, 255.0) as u8
}
#[allow(clippy::approx_constant)]
pub fn q8_to_db(gain_q8: u8) -> f32 {
if gain_q8 == 0 {
return -96.0;
}
let lin = gain_q8 as f32 / 255.0;
let bits = lin.to_bits();
let exp = ((bits >> 23) & 0xFF) as i32 - 127;
let mantissa = f32::from_bits((bits & 0x007F_FFFF) | 0x3F80_0000);
let log2_val = exp as f32 + (mantissa - 1.0) * (1.4427 - 0.4427 * (mantissa - 1.0));
let log10_val = log2_val * 0.30103;
(20.0 * log10_val).clamp(-96.0, 0.0)
}