Skip to main content

dirtydata_runtime/nodes/
processors.rs

1use super::base::*;
2use dirtydata_core::types::ConfigSnapshot;
3
4pub struct GainNode {
5    pub gain_smooth: Option<SmoothedValue>,
6}
7
8impl GainNode {
9    pub fn new() -> Self { Self { gain_smooth: None } }
10}
11
12impl DspNode for GainNode {
13    fn process(&mut self, inputs: &[f32], outputs: &mut [[f32; 2]], config: &ConfigSnapshot, ctx: &ProcessContext) {
14        let gain_db_target = config.get("gain_db").and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
15        let smooth = self.gain_smooth.get_or_insert_with(|| SmoothedValue::new(gain_db_target, ctx.sample_rate, 10.0));
16        let linear = 10.0_f32.powf(smooth.next() / 20.0);
17        if inputs.len() >= 2 {
18            outputs[0] = [inputs[0] * linear, inputs[1] * linear];
19        }
20    }
21}
22
23pub struct BiquadFilterNode {
24    z1: [f32; 2],
25    z2: [f32; 2],
26    freq_smooth: Option<SmoothedValue>,
27}
28
29impl BiquadFilterNode {
30    pub fn new() -> Self { Self { z1: [0.0, 0.0], z2: [0.0, 0.0], freq_smooth: None } }
31}
32
33impl DspNode for BiquadFilterNode {
34    fn process(&mut self, inputs: &[f32], outputs: &mut [[f32; 2]], config: &ConfigSnapshot, ctx: &ProcessContext) {
35        let freq_target = config.get("frequency").and_then(|v| v.as_float()).unwrap_or(1000.0) as f32;
36        let q = config.get("q").and_then(|v| v.as_float()).unwrap_or(0.707) as f32;
37        let smooth = self.freq_smooth.get_or_insert_with(|| SmoothedValue::new(freq_target, ctx.sample_rate, 10.0));
38        let w0 = 2.0 * std::f32::consts::PI * smooth.next() / ctx.sample_rate;
39        let alpha = w0.sin() / (2.0 * q);
40        let cos_w0 = w0.cos();
41        let b0 = (1.0 - cos_w0) / 2.0;
42        let b1 = 1.0 - cos_w0;
43        let b2 = (1.0 - cos_w0) / 2.0;
44        let a0 = 1.0 + alpha;
45        let a1 = -2.0 * cos_w0;
46        let a2 = 1.0 - alpha;
47        let inv_a0 = 1.0 / a0;
48        for i in 0..2 {
49            let x = if inputs.len() > i { inputs[i] } else { 0.0 };
50            let y = (b0 * x + self.z1[i]) * inv_a0;
51            self.z1[i] = b1 * x - a1 * y + self.z2[i];
52            self.z2[i] = b2 * x - a2 * y;
53            outputs[0][i] = y;
54        }
55    }
56}
57
58pub struct CompressorNode {
59    envelope: f32,
60}
61
62impl CompressorNode {
63    pub fn new() -> Self { Self { envelope: 0.0 } }
64}
65
66impl DspNode for CompressorNode {
67    fn process(&mut self, inputs: &[f32], outputs: &mut [[f32; 2]], config: &ConfigSnapshot, _ctx: &ProcessContext) {
68        let threshold_db = config.get("threshold_db").and_then(|v| v.as_float()).unwrap_or(-20.0) as f32;
69        let ratio = config.get("ratio").and_then(|v| v.as_float()).unwrap_or(4.0) as f32;
70        let threshold = 10.0_f32.powf(threshold_db / 20.0);
71        let peak = if inputs.len() >= 2 { inputs[0].abs().max(inputs[1].abs()) } else { 0.0 };
72        self.envelope += 0.1 * (peak - self.envelope);
73        let gain = if self.envelope > threshold {
74            let over_db = 20.0 * (self.envelope / threshold).log10();
75            10.0_f32.powf(-(over_db * (1.0 - 1.0 / ratio)) / 20.0)
76        } else { 1.0 };
77        if inputs.len() >= 2 {
78            outputs[0] = [inputs[0] * gain, inputs[1] * gain];
79        }
80    }
81}