diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index b887302..1149217 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -59,8 +59,9 @@ pub const MAX_BLOCK_SIZE : usize = 128; /// This trait is an interface between the graph functions /// and the AtomDataModel of the UI. pub trait GraphAtomData { - fn get(&self, node_id: usize, param_idx: u32) -> Option; - fn get_denorm(&self, node_id: usize, param_idx: u32) -> f32; + fn get(&self, param_idx: u32) -> Option; + fn get_denorm(&self, param_idx: u32) -> f32; + fn get_norm(&self, param_idx: u32) -> f32; } pub type GraphFun = Box f32>; diff --git a/src/dsp/node_ad.rs b/src/dsp/node_ad.rs index 7e5f926..e50f7a8 100644 --- a/src/dsp/node_ad.rs +++ b/src/dsp/node_ad.rs @@ -3,7 +3,10 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{ + NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, + GraphAtomData, GraphFun, +}; use super::helpers::{Trigger, TrigSignal, sqrt4_to_pow4}; #[macro_export] @@ -230,4 +233,18 @@ impl DspNode for Ad { ctx_vals[0].set(self.value as f32); // ctx_vals[1].set(self.phase / self. + self.stage * ); } + + fn graph_fun() -> Option { + Some(Box::new(|gd: &dyn GraphAtomData, _init: bool, x: f32| -> f32 { + let atk_idx = NodeId::Ad(0).inp_param("atk").unwrap().inp(); + let dcy_idx = NodeId::Ad(0).inp_param("dcy").unwrap().inp(); + let ashp_idx = NodeId::Ad(0).inp_param("ashp").unwrap().inp(); + let dshp_idx = NodeId::Ad(0).inp_param("dshp").unwrap().inp(); + + let ashp = gd.get_denorm(ashp_idx as u32); + + let v = sqrt4_to_pow4(x * gd.get_norm(atk_idx as u32), ashp); + v + })) + } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 2cc7378..06de95a 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -162,6 +162,38 @@ assertion failed: `(left[{}] == right[{}])` } } + +#[macro_export] +macro_rules! assert_decimated_slope_feq_fine { + ($vec:expr, $decimate:expr, $cmp_vec:expr) => { + let cmp_vec = $cmp_vec; + let mut res : Vec = vec![]; + let mut prev = 0.0; + for (i, s) in $vec.iter().enumerate() { + let delta = *s - prev; + if i > 0 { + res.push(delta); + } + prev = *s; + } + + let res : Vec = res.iter().step_by($decimate).copied().collect(); + + for (i, (s, scmp)) in res.iter().zip(cmp_vec.iter()).enumerate() { + if (s - scmp).abs() > 0.0000001 { + panic!(r#" +table_left: {:?} + +table_right: {:?} + +assertion failed: `(left[{}] == right[{}])` + left: `{:?}`, + right: `{:?}`"#, &res[i..], &(cmp_vec[i..]), i, i, s, scmp) + } + } + } +} + #[macro_export] macro_rules! assert_rmsmima { ($rms:expr, $b:expr) => { diff --git a/tests/node_ad.rs b/tests/node_ad.rs index f3ac04f..642c2f6 100644 --- a/tests/node_ad.rs +++ b/tests/node_ad.rs @@ -256,9 +256,8 @@ fn check_node_ad_shp_exp() { ]); } - #[test] -fn check_node_ad_trig_out() { +fn check_node_ad_eoet() { let (node_conf, mut node_exec) = new_node_engine(); let mut matrix = Matrix::new(node_conf, 3, 3); @@ -274,10 +273,7 @@ fn check_node_ad_trig_out() { .input(out.inp("ch2"), None, None)); matrix.sync().unwrap(); - let trig_p = ad.inp_param("trig").unwrap(); - pset_n(&mut matrix, ad, "trig", 1.0); - let res = run_for_ms(&mut node_exec, 25.0); // just make sure we are running an env: assert_decimated_slope_feq!(res.0, 50, vec![ @@ -308,7 +304,7 @@ fn check_node_ad_trig_out() { #[test] -fn check_node_ad_atk() { +fn check_node_ad_atk_dcy() { let (node_conf, mut node_exec) = new_node_engine(); let mut matrix = Matrix::new(node_conf, 3, 3); @@ -346,4 +342,55 @@ fn check_node_ad_atk() { // attack phase ended, and now we decay: -0.002267599 ]); + + // check if decay stays stable: + let res = run_for_ms(&mut node_exec, 2.0); + assert_decimated_slope_feq!(res.0, 40, vec![-0.002267599; 3]); + + pset_d(&mut matrix, ad, "dcy", 200.0); + let res = run_for_ms(&mut node_exec, 20.0); + assert_decimated_slope_feq!(res.0, 40, vec![ + // Slope is getting less and less steep, as expected: + -0.002197802, -0.0012806058, -0.00083732605, -0.0005899668, + -0.00043797493, -0.00033789873, -0.00026863813, -0.00021868944, + -0.00018143654, -0.00015294552, -0.00013071299, + // Slope does not change after the "dcy" change has been smoothed + -0.000113368034, -0.000113368034, -0.00011339784, -0.00011339784, + -0.000113368034, -0.000113368034, -0.00011339784, -0.000113368034, + -0.000113368034, -0.00011339784, -0.000113368034, -0.000113368034 + ]); } + +#[test] +fn check_node_ad_mult() { + let (node_conf, mut node_exec) = new_node_engine(); + let mut matrix = Matrix::new(node_conf, 3, 3); + + let ad = NodeId::Ad(0); + let out = NodeId::Out(0); + matrix.place(0, 0, Cell::empty(ad) + .out(None, None, ad.out("sig"))); + matrix.place(0, 1, Cell::empty(out) + .input(out.inp("ch1"), None, None)); + matrix.sync().unwrap(); + + pset_n(&mut matrix, ad, "trig", 1.0); + pset_n(&mut matrix, ad, "mult", 2.0); + let res = run_for_ms(&mut node_exec, 2000.0); + assert_decimated_slope_feq_fine!(res.0, 1800, vec![ + 0.0, + // looong attack: + 0.00007558, 0.00007558, 0.00007558, 0.00007558, + 0.00007558, 0.00007558, 0.00007558, + // looong decay: + -0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709, + -0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709, + -0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709, + -0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709, + -0.000022709, -0.000022709, -0.000022709, -0.000022709, -0.000022709, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + ]); +} +