Skip to main content

rill_lofi/dsp/
quantization.rs

1//! Functions for quantization and bit reduction
2
3/// Basic quantization with bit depth reduction
4pub 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
23/// Sample rate reduction with sample-and-hold
24pub 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
33/// Calculate reduction factor
34pub fn calculate_reduction_factor(input_sr: f32, target_sr: f32) -> usize {
35    (input_sr / target_sr).ceil() as usize
36}
37
38/// Nonlinear quantization (μ-law)
39pub 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}