dirtydata_runtime/nodes/
processors.rs1use 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}