Skip to main content

dirtydata_runtime/nodes/
base.rs

1use dirtydata_core::types::ConfigSnapshot;
2
3/// A helper for smoothing parameter changes using a One-Pole LPF.
4pub struct SmoothedValue {
5    current: f32,
6    target: f32,
7    coeff: f32,
8}
9
10impl SmoothedValue {
11    pub fn new(initial: f32, sample_rate: f32, time_constant_ms: f32) -> Self {
12        let tau = time_constant_ms * 0.001;
13        let coeff = 1.0 - (-1.0 / (sample_rate * tau)).exp();
14        Self { current: initial, target: initial, coeff }
15    }
16    pub fn set_target(&mut self, target: f32) { self.target = target; }
17    pub fn next(&mut self) -> f32 {
18        self.current += self.coeff * (self.target - self.current);
19        self.current
20    }
21    pub fn current(&self) -> f32 { self.current }
22}
23
24pub fn rk4_step<F>(state: &mut [f32], dt: f32, t: f32, derivative: F)
25where F: Fn(&[f32], f32) -> Vec<f32>,
26{
27    let k1 = derivative(state, t);
28    let mut s2 = state.to_vec();
29    for i in 0..state.len() { s2[i] += k1[i] * dt * 0.5; }
30    let k2 = derivative(&s2, t + dt * 0.5);
31    let mut s3 = state.to_vec();
32    for i in 0..state.len() { s3[i] += k2[i] * dt * 0.5; }
33    let k3 = derivative(&s3, t + dt * 0.5);
34    let mut s4 = state.to_vec();
35    for i in 0..state.len() { s4[i] += k3[i] * dt; }
36    let k4 = derivative(&s4, t + dt);
37    for i in 0..state.len() {
38        state[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]);
39    }
40}
41
42pub fn rk4_step_fixed<const N: usize, F>(state: &mut [f32; N], dt: f32, t: f32, derivative: F)
43where F: Fn(&[f32; N], f32) -> [f32; N],
44{
45    let k1 = derivative(state, t);
46    let mut s2 = *state;
47    for i in 0..N { s2[i] += k1[i] * dt * 0.5; }
48    let k2 = derivative(&s2, t + dt * 0.5);
49    let mut s3 = *state;
50    for i in 0..N { s3[i] += k2[i] * dt * 0.5; }
51    let k3 = derivative(&s3, t + dt * 0.5);
52    let mut s4 = *state;
53    for i in 0..N { s4[i] += k3[i] * dt; }
54    let k4 = derivative(&s4, t + dt);
55    for i in 0..N {
56        state[i] += (dt / 6.0) * (k1[i] + 2.0 * k2[i] + 2.0 * k3[i] + k4[i]);
57    }
58}
59
60pub struct OscMessage {
61    pub addr: String,
62    pub args: Vec<rosc::OscType>,
63}
64
65pub struct ProcessContext<'a> {
66    pub sample_rate: f32,
67    pub global_sample_index: u64,
68    pub crash_flag: Option<&'a std::sync::atomic::AtomicBool>,
69    pub osc_tx: Option<&'a crossbeam_channel::Sender<OscMessage>>,
70    pub convergence_info: Option<&'a dashmap::DashMap<dirtydata_core::types::StableId, usize>>,
71    pub node_diagnostics: Option<&'a dashmap::DashMap<dirtydata_core::types::StableId, crate::DiagnosticRecord>>,
72    pub node_id: Option<dirtydata_core::types::StableId>,
73}
74
75#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
76pub enum NodeState {
77    Empty,
78    Serialized(serde_json::Value),
79}
80
81impl NodeState {
82    pub fn from_json<T: serde::Serialize>(data: T) -> Self {
83        Self::Serialized(serde_json::to_value(data).unwrap_or(serde_json::Value::Null))
84    }
85    pub fn to_json<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
86        if let Self::Serialized(val) = self {
87            serde_json::from_value(val.clone()).ok()
88        } else {
89            None
90        }
91    }
92}
93
94pub trait DspNode: Send + Sync {
95    fn process(&mut self, inputs: &[f32], outputs: &mut [[f32; 2]], config: &ConfigSnapshot, ctx: &ProcessContext);
96    fn update_parameter(&mut self, _param: &str, _value: f32) {}
97    fn extract_state(&self) -> NodeState { NodeState::Empty }
98    fn inject_state(&mut self, _state: &NodeState) {}
99}