diff --git a/src/dsp/node_ad.rs b/src/dsp/node_ad.rs index ef69e5a..89b58f4 100644 --- a/src/dsp/node_ad.rs +++ b/src/dsp/node_ad.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{sqrt4_to_pow4, TrigSignal, Trigger}; use crate::dsp::{ DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{sqrt4_to_pow4, TrigSignal, Trigger}; #[macro_export] macro_rules! fa_ad_mult { diff --git a/src/dsp/node_allp.rs b/src/dsp/node_allp.rs index adf3cbd..d76ab7c 100644 --- a/src/dsp/node_allp.rs +++ b/src/dsp/node_allp.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::AllPass; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::AllPass; /// A simple amplifier #[derive(Debug, Clone)] diff --git a/src/dsp/node_biqfilt.rs b/src/dsp/node_biqfilt.rs index 8d3c373..ac27b07 100644 --- a/src/dsp/node_biqfilt.rs +++ b/src/dsp/node_biqfilt.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::*; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::*; #[macro_export] macro_rules! fa_biqfilt_type { diff --git a/src/dsp/node_bosc.rs b/src/dsp/node_bosc.rs index e4eb603..ddf8ade 100644 --- a/src/dsp/node_bosc.rs +++ b/src/dsp/node_bosc.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::PolyBlepOscillator; use crate::dsp::{ DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::PolyBlepOscillator; #[macro_export] macro_rules! fa_bosc_wtype { diff --git a/src/dsp/node_bowstri.rs b/src/dsp/node_bowstri.rs index f16ea01..7e68b8a 100644 --- a/src/dsp/node_bowstri.rs +++ b/src/dsp/node_bowstri.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{DelayBuffer, FixedOnePole, Biquad}; use crate::dsp::{ denorm, denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{Biquad, DelayBuffer, FixedOnePole}; // Bowed String instrument oscillator // Bowed string model, a la Smith (1986), diff --git a/src/dsp/node_comb.rs b/src/dsp/node_comb.rs index 285368b..e96cd78 100644 --- a/src/dsp/node_comb.rs +++ b/src/dsp/node_comb.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp; #[macro_export] macro_rules! fa_comb_mode { diff --git a/src/dsp/node_cqnt.rs b/src/dsp/node_cqnt.rs index 3f36f3a..ddb92e7 100644 --- a/src/dsp/node_cqnt.rs +++ b/src/dsp/node_cqnt.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{ChangeTrig, CtrlPitchQuantizer}; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{ChangeTrig, CtrlPitchQuantizer}; #[macro_export] macro_rules! fa_cqnt { diff --git a/src/dsp/node_delay.rs b/src/dsp/node_delay.rs index 2e2e826..ae9ebbe 100644 --- a/src/dsp/node_delay.rs +++ b/src/dsp/node_delay.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{crossfade, DelayBuffer, TriggerSampleClock}; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{crossfade, DelayBuffer, TriggerSampleClock}; #[macro_export] macro_rules! fa_delay_mode { diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 6e858c3..dfe4094 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -69,14 +69,17 @@ impl DspNode for Formant { let decay_freq = denorm::Formant::dcy(decay_freq, frame); // where the two decays meet - let carrier_center = attack_freq / (attack_freq + decay_freq); + // clamp to avoid division by 0 + let carrier_center = + (attack_freq / (attack_freq + decay_freq)).max(1e-6).min(1.0 - 1e-6); // where they meet in amplitude - let carrier_lowest_amplitude = if carrier_center * decay_freq > base_freq * 2.0 { - 0.0 - } else { - (-(std::f32::consts::PI * carrier_center * decay_freq) / base_freq).exp() - }; + let carrier_lowest_amplitude = + if carrier_center * decay_freq > base_freq * 2.0 || base_freq == 0.0 { + 0.0 + } else { + (-(std::f32::consts::PI * carrier_center * decay_freq) / base_freq).exp() + }; // make a triangle wave, with the peak at carrier center let carrier_base = @@ -88,17 +91,22 @@ impl DspNode for Formant { * (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base))); // multiple of the frequency the modulators are at - let multiple = formant_freq / base_freq; + let multiple = formant_freq / base_freq.max(1e-6); // round them to the closest integer of the formant freq - let freq_a = multiple.floor().max(1.0); + let freq_a = multiple.floor(); let freq_b = freq_a + 1.0; // and how much to lerp between them - let blend = multiple.max(1.0).fract(); + let blend = multiple.fract(); // get the true modulator - let modulator = (1.0 - blend) * (std::f32::consts::TAU * self.phase * freq_a).cos() + let modulator = (1.0 - blend) + * if multiple < 1.0 { + 0.0 + } else { + (std::f32::consts::TAU * self.phase * freq_a).cos() + } + blend * (std::f32::consts::TAU * self.phase * freq_b).cos(); // entire wave diff --git a/src/dsp/node_mux9.rs b/src/dsp/node_mux9.rs index ff8aa58..a1da24a 100644 --- a/src/dsp/node_mux9.rs +++ b/src/dsp/node_mux9.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::Trigger; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::Trigger; #[macro_export] macro_rules! fa_mux9_in_cnt { diff --git a/src/dsp/node_noise.rs b/src/dsp/node_noise.rs index ce3d5ca..0009cd4 100644 --- a/src/dsp/node_noise.rs +++ b/src/dsp/node_noise.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::Rng; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::Rng; #[macro_export] macro_rules! fa_noise_mode { diff --git a/src/dsp/node_pverb.rs b/src/dsp/node_pverb.rs index 20853c1..75f0034 100644 --- a/src/dsp/node_pverb.rs +++ b/src/dsp/node_pverb.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{DattorroReverb, DattorroReverbParams, crossfade}; use crate::dsp::{denorm, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{crossfade, DattorroReverb, DattorroReverbParams}; pub struct DatParams { frame: usize, diff --git a/src/dsp/node_quant.rs b/src/dsp/node_quant.rs index 6955fd6..0d40549 100644 --- a/src/dsp/node_quant.rs +++ b/src/dsp/node_quant.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{ChangeTrig, Quantizer}; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{ChangeTrig, Quantizer}; #[macro_export] macro_rules! fa_quant { diff --git a/src/dsp/node_rndwk.rs b/src/dsp/node_rndwk.rs index 3996944..a60d152 100644 --- a/src/dsp/node_rndwk.rs +++ b/src/dsp/node_rndwk.rs @@ -2,9 +2,9 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{Rng, SlewValue, Trigger}; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{Rng, SlewValue, Trigger}; /// A triggered random walker #[derive(Debug, Clone)] diff --git a/src/dsp/node_sampl.rs b/src/dsp/node_sampl.rs index 0e7e6ae..45556bf 100644 --- a/src/dsp/node_sampl.rs +++ b/src/dsp/node_sampl.rs @@ -2,10 +2,10 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{cubic_interpolate, Trigger}; use crate::dsp::{at, denorm, denorm_offs, inp, out}; //, inp, denorm, denorm_v, inp_dir, at}; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{cubic_interpolate, Trigger}; #[macro_export] macro_rules! fa_sampl_dir { diff --git a/src/dsp/node_scope.rs b/src/dsp/node_scope.rs index 76a6f46..ab2b7f7 100644 --- a/src/dsp/node_scope.rs +++ b/src/dsp/node_scope.rs @@ -8,12 +8,12 @@ // Copyright by Andrew Belt, 2021 //use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger}; -use synfx_dsp::CustomTrigger; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::SCOPE_SAMPLES; use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::ScopeHandle; use std::sync::Arc; +use synfx_dsp::CustomTrigger; #[macro_export] macro_rules! fa_scope_tsrc { diff --git a/src/dsp/node_sfilter.rs b/src/dsp/node_sfilter.rs index ad6f519..d887d5c 100644 --- a/src/dsp/node_sfilter.rs +++ b/src/dsp/node_sfilter.rs @@ -2,13 +2,13 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. +use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; +use crate::nodes::{NodeAudioContext, NodeExecContext}; use synfx_dsp::{ process_1pole_highpass, process_1pole_lowpass, process_1pole_tpt_highpass, process_1pole_tpt_lowpass, process_hal_chamberlin_svf, process_simper_svf, process_stilson_moog, }; -use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; -use crate::nodes::{NodeAudioContext, NodeExecContext}; #[macro_export] macro_rules! fa_sfilter_type { diff --git a/src/dsp/node_sin.rs b/src/dsp/node_sin.rs index 05afe05..3988b40 100644 --- a/src/dsp/node_sin.rs +++ b/src/dsp/node_sin.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::fast_sin; use crate::dsp::{ denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::fast_sin; /// A sine oscillator #[derive(Debug, Clone)] diff --git a/src/dsp/node_test.rs b/src/dsp/node_test.rs index 3eb0b32..02bcb87 100644 --- a/src/dsp/node_test.rs +++ b/src/dsp/node_test.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::TrigSignal; use crate::dsp::{ DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::TrigSignal; #[macro_export] macro_rules! fa_test_s { diff --git a/src/dsp/node_tseq.rs b/src/dsp/node_tseq.rs index 3397568..43f75c4 100644 --- a/src/dsp/node_tseq.rs +++ b/src/dsp/node_tseq.rs @@ -2,10 +2,10 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{Trigger, TriggerPhaseClock}; use crate::dsp::tracker::TrackerBackend; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{Trigger, TriggerPhaseClock}; use crate::dsp::MAX_BLOCK_SIZE; diff --git a/src/dsp/node_tslfo.rs b/src/dsp/node_tslfo.rs index 00355d6..bcefe93 100644 --- a/src/dsp/node_tslfo.rs +++ b/src/dsp/node_tslfo.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{TriSawLFO, Trigger}; use crate::dsp::{ DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{TriSawLFO, Trigger}; #[derive(Debug, Clone)] pub struct TsLFO { diff --git a/src/dsp/node_vosc.rs b/src/dsp/node_vosc.rs index a35a371..aa92e55 100644 --- a/src/dsp/node_vosc.rs +++ b/src/dsp/node_vosc.rs @@ -2,11 +2,11 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use synfx_dsp::{Oversampling, apply_distortion, VPSOscillator}; use crate::dsp::{ DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, }; use crate::nodes::{NodeAudioContext, NodeExecContext}; +use synfx_dsp::{apply_distortion, Oversampling, VPSOscillator}; #[macro_export] macro_rules! fa_vosc_ovrsmpl { diff --git a/src/lib.rs b/src/lib.rs index f944009..7a4ad7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -321,8 +321,8 @@ pub mod monitor; pub mod nodes; pub mod sample_lib; pub mod scope_handle; -pub mod wblockdsp; mod util; +pub mod wblockdsp; pub use cell_dir::CellDir; pub use chain_builder::MatrixCellChain; diff --git a/src/matrix.rs b/src/matrix.rs index ee2fb50..2404341 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -8,9 +8,9 @@ use crate::matrix_repr::*; pub use crate::monitor::MON_SIG_CNT; pub use crate::nodes::MinMaxMonitorSamples; use crate::nodes::{NodeConfigurator, NodeGraphOrdering, NodeProg, MAX_ALLOCATED_NODES}; +use crate::wblockdsp::{BlkJITCompileError, BlockFun, BlockFunSnapshot}; pub use crate::CellDir; use crate::ScopeHandle; -use crate::wblockdsp::{BlockFun, BlockFunSnapshot, BlkJITCompileError}; use std::collections::{HashMap, HashSet}; diff --git a/src/matrix_repr.rs b/src/matrix_repr.rs index 3d34aa3..f054aa5 100644 --- a/src/matrix_repr.rs +++ b/src/matrix_repr.rs @@ -3,8 +3,8 @@ // See README.md and COPYING for details. use crate::dsp::{NodeId, ParamId, SAtom}; -use serde_json::{json, Value}; use crate::wblockdsp::BlockFunSnapshot; +use serde_json::{json, Value}; #[derive(Debug, Clone, Copy)] pub struct CellRepr { diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index 422e8be..5f1b5f4 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -6,15 +6,15 @@ use super::{ FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_CODE_ENGINES, MAX_AVAIL_TRACKERS, MAX_INPUTS, MAX_SCOPES, UNUSED_MONITOR_IDX, }; -use crate::wblockdsp::*; use crate::dsp::tracker::{PatternData, Tracker}; use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom}; use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT}; use crate::nodes::drop_thread::DropThread; -#[cfg(feature = "synfx-dsp-jit")] -use synfx_dsp_jit::engine::CodeEngine; +use crate::wblockdsp::*; use crate::SampleLibrary; use crate::ScopeHandle; +#[cfg(feature = "synfx-dsp-jit")] +use synfx_dsp_jit::engine::CodeEngine; use ringbuf::{Producer, RingBuffer}; use std::collections::HashMap; diff --git a/src/wblockdsp/compiler.rs b/src/wblockdsp/compiler.rs index 2492d60..99e7689 100644 --- a/src/wblockdsp/compiler.rs +++ b/src/wblockdsp/compiler.rs @@ -210,7 +210,7 @@ pub struct Block2JITCompiler { #[cfg(not(feature = "synfx-dsp-jit"))] pub enum ASTNode { - NoSynfxDSPJit + NoSynfxDSPJit, } impl Block2JITCompiler { diff --git a/src/wblockdsp/definition.rs b/src/wblockdsp/definition.rs index f73a00a..c92cc36 100644 --- a/src/wblockdsp/definition.rs +++ b/src/wblockdsp/definition.rs @@ -208,39 +208,42 @@ pub fn setup_hxdsp_block_language( }); } - dsp_lib.borrow().for_each(|node_type| -> Result<(), ()> { - let max_ports = node_type.input_count().max(node_type.output_count()); - let is_stateful = node_type.is_stateful(); + dsp_lib + .borrow() + .for_each(|node_type| -> Result<(), ()> { + let max_ports = node_type.input_count().max(node_type.output_count()); + let is_stateful = node_type.is_stateful(); - let mut inputs = vec![]; - let mut outputs = vec![]; + let mut inputs = vec![]; + let mut outputs = vec![]; - let mut i = 0; - while let Some(name) = node_type.input_names(i) { - inputs.push(Some(name[0..(name.len().min(2))].to_string())); - i += 1; - } + let mut i = 0; + while let Some(name) = node_type.input_names(i) { + inputs.push(Some(name[0..(name.len().min(2))].to_string())); + i += 1; + } - let mut i = 0; - while let Some(name) = node_type.output_names(i) { - outputs.push(Some(name[0..(name.len().min(2))].to_string())); - i += 1; - } + let mut i = 0; + while let Some(name) = node_type.output_names(i) { + outputs.push(Some(name[0..(name.len().min(2))].to_string())); + i += 1; + } - lang.define(BlockType { - category: if is_stateful { "nodes".to_string() } else { "functions".to_string() }, - name: node_type.name().to_string(), - rows: max_ports, - area_count: 0, - user_input: BlockUserInput::None, - description: node_type.documentation().to_string(), - color: if is_stateful { 8 } else { 16 }, - inputs, - outputs, - }); + lang.define(BlockType { + category: if is_stateful { "nodes".to_string() } else { "functions".to_string() }, + name: node_type.name().to_string(), + rows: max_ports, + area_count: 0, + user_input: BlockUserInput::None, + description: node_type.documentation().to_string(), + color: if is_stateful { 8 } else { 16 }, + inputs, + outputs, + }); - Ok(()) - }).expect("seriously no error here"); + Ok(()) + }) + .expect("seriously no error here"); lang.define_identifier("in1"); lang.define_identifier("in2"); diff --git a/src/wblockdsp/mod.rs b/src/wblockdsp/mod.rs index fc210df..6743b70 100644 --- a/src/wblockdsp/mod.rs +++ b/src/wblockdsp/mod.rs @@ -6,10 +6,10 @@ */ +mod compiler; mod definition; mod language; -mod compiler; +pub use compiler::*; pub use definition::*; pub use language::*; -pub use compiler::*; diff --git a/tests/node_formant.rs b/tests/node_formant.rs new file mode 100644 index 0000000..e23909d --- /dev/null +++ b/tests/node_formant.rs @@ -0,0 +1,133 @@ +mod common; +use common::*; + +#[test] +fn check_normalized_if_freq_lower_than_formant_freq() { + let (node_conf, mut node_exec) = new_node_engine(); + + let mut matrix = Matrix::new(node_conf, 3, 3); + + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("formant", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); + + matrix.sync().unwrap(); + + let formant = NodeId::Formant(0); + + // params + let freq_p = formant.inp_param("freq").unwrap(); + let form_p = formant.inp_param("form").unwrap(); + let atk_p = formant.inp_param("atk").unwrap(); + let dcy_p = formant.inp_param("dcy").unwrap(); + + // set params to reasonable values + matrix.set_param(freq_p, SAtom::param(-0.2)); + matrix.set_param(form_p, SAtom::param(0.0)); + matrix.set_param(atk_p, SAtom::param(0.2)); + matrix.set_param(dcy_p, SAtom::param(-0.2)); + + // run + let res = run_for_ms(&mut node_exec, 100.0); + + // and check it's normalized + let max = res.0.iter().fold(0.0 as f32, |acc, x| acc.max(*x)); + let min = res.0.iter().fold(0.0 as f32, |acc, x| acc.min(*x)); + assert!(max > 0.8 && max < 1.01 && min < -0.8 && min > -1.01); +} + +#[test] +fn check_no_dc_bias_at_formant_freq_lower_than_freq() { + let (node_conf, mut node_exec) = new_node_engine(); + + let mut matrix = Matrix::new(node_conf, 3, 3); + + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("formant", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); + + matrix.sync().unwrap(); + + let formant = NodeId::Formant(0); + + // params + let freq_p = formant.inp_param("freq").unwrap(); + let form_p = formant.inp_param("form").unwrap(); + let atk_p = formant.inp_param("atk").unwrap(); + let dcy_p = formant.inp_param("dcy").unwrap(); + + // set params to reasonable values + matrix.set_param(freq_p, SAtom::param(0.0)); + matrix.set_param(form_p, SAtom::param(-0.2)); + matrix.set_param(atk_p, SAtom::param(0.2)); + matrix.set_param(dcy_p, SAtom::param(-0.2)); + + // run + let res = run_for_ms(&mut node_exec, 100.0); + + // average should remain at ~0 + let sum = res.0.iter().sum::(); + let avg = sum / res.0.len() as f32; + + assert!(avg > -0.05 && avg < 0.05); +} + +#[test] +fn check_no_nan() { + let (node_conf, mut node_exec) = new_node_engine(); + + let mut matrix = Matrix::new(node_conf, 3, 3); + + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("formant", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); + + matrix.sync().unwrap(); + + let formant = NodeId::Formant(0); + + // params + let freq_p = formant.inp_param("freq").unwrap(); + let form_p = formant.inp_param("form").unwrap(); + let atk_p = formant.inp_param("atk").unwrap(); + let dcy_p = formant.inp_param("dcy").unwrap(); + + // set params to non-reasonable values here + // base freq 0 + matrix.set_param(freq_p, SAtom::param(-1.0)); + matrix.set_param(form_p, SAtom::param(0.0)); + matrix.set_param(atk_p, SAtom::param(0.2)); + matrix.set_param(dcy_p, SAtom::param(-0.2)); + + // run + let res = run_for_ms(&mut node_exec, 100.0); + + // and check there's no NaN + assert!(res.0.iter().all(|x| !x.is_nan())); + + // set params to non-reasonable values here + // base freq attack freq 0 + matrix.set_param(freq_p, SAtom::param(-0.2)); + matrix.set_param(form_p, SAtom::param(0.0)); + matrix.set_param(atk_p, SAtom::param(-1.0)); + matrix.set_param(dcy_p, SAtom::param(-0.2)); + + // run + let res = run_for_ms(&mut node_exec, 100.0); + + // and check there's no NaN + assert!(res.0.iter().all(|x| !x.is_nan())); + + // set params to non-reasonable values here + // decay freq freq 0 + matrix.set_param(freq_p, SAtom::param(-0.2)); + matrix.set_param(form_p, SAtom::param(0.0)); + matrix.set_param(atk_p, SAtom::param(0.2)); + matrix.set_param(dcy_p, SAtom::param(-1.0)); + + // run + let res = run_for_ms(&mut node_exec, 100.0); + + // and check there's no NaN + assert!(res.0.iter().all(|x| !x.is_nan())); +} + +#[test] +fn check_formant_freq() {}