1pub type Phase = u32;
3
4#[inline]
6pub const fn hz_to_phase_inc(freq_hz: u32, sample_rate_hz: u32) -> u32 {
7 if sample_rate_hz == 0 {
8 0
9 } else {
10 (((freq_hz as u64) << 32) / (sample_rate_hz as u64)) as u32
11 }
12}
13
14#[inline]
16pub const fn phase_index(phase: Phase) -> u8 {
17 (phase >> 24) as u8
18}
19
20#[inline]
22pub fn sin_table(index: u8) -> i8 {
23 let x = index as i32;
24 let y = if x < 128 { x * 2 } else { (255 - x) * 2 };
25 let quad = (y * (255 - y)) >> 8;
26 let s = if x < 128 { quad } else { -quad };
27 s.clamp(-127, 127) as i8
28}
29
30#[inline]
32pub fn lerp_i8(a: i8, b: i8, frac: u8) -> i8 {
33 let ai = a as i32;
34 let bi = b as i32;
35 let v = ai + ((bi - ai) * frac as i32) / 256;
36 v.clamp(-128, 127) as i8
37}
38
39#[inline]
41pub fn apply_gain_q8(sample: i8, gain_q8: u8) -> i8 {
42 ((sample as i32 * gain_q8 as i32) >> 8).clamp(-128, 127) as i8
43}
44
45#[inline]
47pub fn mix_crossfade(a: i8, b: i8, t_q8: u8) -> i8 {
48 let ai = a as i32;
49 let bi = b as i32;
50 let t = t_q8 as i32;
51 let v = ai + ((bi - ai) * t) / 256;
52 v.clamp(-128, 127) as i8
53}
54
55#[inline]
57pub fn clamp_sample(v: i32) -> i8 {
58 v.clamp(-128, 127) as i8
59}
60
61#[allow(clippy::approx_constant)]
67pub fn db_to_q8(db: f32) -> u8 {
68 if db >= 0.0 {
69 return 255;
70 }
71 if db <= -48.0 {
72 return 0;
73 }
74 let x = db / 20.0;
78 let lin = 1.0 + 2.3026 * x + 2.651 * x * x + 2.03 * x * x * x;
79 (lin * 255.0).clamp(0.0, 255.0) as u8
80}
81
82#[allow(clippy::approx_constant)]
84pub fn q8_to_db(gain_q8: u8) -> f32 {
85 if gain_q8 == 0 {
86 return -96.0;
87 }
88 let lin = gain_q8 as f32 / 255.0;
89 let bits = lin.to_bits();
92 let exp = ((bits >> 23) & 0xFF) as i32 - 127;
93 let mantissa = f32::from_bits((bits & 0x007F_FFFF) | 0x3F80_0000);
94 let log2_val = exp as f32 + (mantissa - 1.0) * (1.4427 - 0.4427 * (mantissa - 1.0));
95 let log10_val = log2_val * 0.30103;
96 (20.0 * log10_val).clamp(-96.0, 0.0)
97}