rill_lofi/dsp/
quantization.rs1pub fn bitcrush(sample: f32, bit_depth: u8, dither: bool) -> f32 {
5 if bit_depth >= 24 {
6 return sample;
7 }
8
9 let steps = (1u32 << bit_depth) as f32;
10 let max_val = 1.0 - (1.0 / steps);
11
12 let scaled = sample.clamp(-1.0, 1.0) * max_val;
13
14 if dither {
15 let dither_amount = 1.0 / steps;
16 let dither_sample = (rand::random::<f32>() - 0.5) * 2.0 * dither_amount;
17 ((scaled + dither_sample) * steps).round() / steps
18 } else {
19 (scaled * steps).round() / steps
20 }
21}
22
23pub fn sample_rate_reduce(sample: f32, factor: usize, hold: &mut f32, counter: &mut usize) -> f32 {
25 *counter += 1;
26 if *counter >= factor {
27 *counter = 0;
28 *hold = sample;
29 }
30 *hold
31}
32
33pub fn calculate_reduction_factor(input_sr: f32, target_sr: f32) -> usize {
35 (input_sr / target_sr).ceil() as usize
36}
37
38pub fn nonlinear_quantize(sample: f32, bit_depth: u8) -> f32 {
40 let sign = sample.signum();
41 let abs_sample = sample.abs().min(1.0);
42
43 let mu = 100.0;
44 let compressed = sign * (1.0 + mu * abs_sample).ln() / (1.0 + mu).ln();
45
46 let quantized = bitcrush(compressed, bit_depth, false);
47
48 let expanded = sign * ((1.0 + mu).powf(quantized.abs()) - 1.0) / mu;
49 expanded.clamp(-1.0, 1.0)
50}