dirtydata_runtime/nodes/
base.rs1use dirtydata_core::types::ConfigSnapshot;
2
3pub 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}