Merge pull request #2 from Dimev/modfm

Awesome! Thanks a lot for the work and polish!
This commit is contained in:
Weird Constructor 2022-08-08 14:21:59 +02:00 committed by GitHub
commit 72eff23839
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 368 additions and 60 deletions

View file

@ -507,6 +507,8 @@ mod node_delay;
#[allow(non_upper_case_globals)]
mod node_fbwr_fbrd;
#[allow(non_upper_case_globals)]
mod node_formfm;
#[allow(non_upper_case_globals)]
mod node_map;
#[allow(non_upper_case_globals)]
mod node_mix3;
@ -593,6 +595,7 @@ use node_cqnt::CQnt;
use node_delay::Delay;
use node_fbwr_fbrd::FbRd;
use node_fbwr_fbrd::FbWr;
use node_formfm::FormFM;
use node_map::Map;
use node_mix3::Mix3;
use node_mux9::Mux9;
@ -1496,6 +1499,12 @@ macro_rules! node_list {
(1 offs n_id d_id r_s f_def stp_d -1.0, 1.0, 0.0)
{2 0 mode setting(0) mode fa_noise_mode 0 1}
[0 sig],
formfm => FormFM UIType::Generic UICategory::Osc
(0 freq n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 440.0)
(1 form n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 440.0)
(2 side n_id d_id r_id f_def stp_d 0.0, 1.0, 0.2)
(3 peak n_id d_id r_id f_def stp_d 0.0, 1.0, 0.4)
[0 sig],
sfilter => SFilter UIType::Generic UICategory::Signal
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
(1 freq n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 1000.0)
@ -1528,7 +1537,7 @@ macro_rules! node_list {
(14 mix n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
[0 sig_l]
[1 sig_r],
test => Test UIType::Generic UICategory::IOUtil
test => Test UIType::Generic UICategory::IOUtil
(0 f n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
{1 0 p param(0.0) knob fa_test_s 0 10}
{2 1 trig param(0.0) knob fa_test_s 0 0}

View file

@ -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 {

View file

@ -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)]

View file

@ -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 {

View file

@ -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 {

View file

@ -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),

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

132
src/dsp/node_formfm.rs Normal file
View file

@ -0,0 +1,132 @@
// Copyright (c) 2022 Dimas Leenman <skythedragon@outlook.com>
// 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};
/// A simple amplifier
#[derive(Debug, Clone)]
pub struct FormFM {
inv_sample_rate: f32,
phase: f32,
}
impl FormFM {
pub fn new(_nid: &NodeId) -> Self {
Self { inv_sample_rate: 1.0 / 44100.0, phase: 0.0 }
}
pub const freq: &'static str = "Formant freq\nBase frequency to oscilate at\n";
pub const form: &'static str = "Formant form\nFrequency of the formant\nThis affects how much lower or higher tones the sound has.";
pub const side: &'static str =
"Formant side\nWhich side the peak of the wave is. Values more towards 0.0 or 1.0 make the base frequency more pronounced";
pub const peak: &'static str =
"Formant peak\nHow high the peak amplitude is. Lower values make the effect more pronounced";
pub const sig: &'static str = "Formant sig\nGenerated formant signal";
pub const DESC: &'static str = r#"Formant oscillator
Simple formant oscillator that generates a formant like sound.
Loosely based on the ModFM synthesis method.
"#;
pub const HELP: &'static str = r#"formfm - Direct formant synthesizer
This is a formant synthesizer that directly generates
the audio of a single formant.
This can be seen as passing a saw wave with frequency `freq`
into a bandpass filter with the cutoff at `form`
`freq` controls the base frequency of the formant.
`form` controls the formant frequency. Lower values give more bass to the sound,
and higher values give the high frequencies more sound.
`side` controls where the peak of the carrier wave is,
and in turn controls the bandwidth of the effect. The more towards 0.0 or 1.0,
the more the formant is audible.
`peak` controls how high the peak of the carrier wave is.
This also controls the bandwidth of the effect, where lower means a higher
bandwidth, and thus more audible formant.
"#;
}
impl DspNode for FormFM {
fn outputs() -> usize {
1
}
fn set_sample_rate(&mut self, srate: f32) {
self.inv_sample_rate = 1.0 / srate;
}
fn reset(&mut self) {
self.phase = 0.0;
}
#[inline]
fn process<T: NodeAudioContext>(
&mut self,
ctx: &mut T,
_ectx: &mut NodeExecContext,
_nctx: &NodeContext,
_atoms: &[SAtom],
inputs: &[ProcBuf],
outputs: &mut [ProcBuf],
_ctx_vals: LedPhaseVals,
) {
use crate::dsp::{denorm, inp, out};
let base_freq = inp::FormFM::freq(inputs);
let formant_freq = inp::FormFM::form(inputs);
let side_val = inp::FormFM::side(inputs);
let peak_val = inp::FormFM::peak(inputs);
let out = out::FormFM::sig(outputs);
for frame in 0..ctx.nframes() {
// get the inputs
let base_freq = denorm::FormFM::freq(base_freq, frame);
let formant_freq = denorm::FormFM::form(formant_freq, frame);
let side_val = denorm::FormFM::side(side_val, frame).min(1.0 - 1e-6).max(1e-6);
let peak_val = denorm::FormFM::peak(peak_val, frame);
// make a triangle wave, with the peak at carrier center
let carrier_base =
(self.phase / side_val).min((1.0 - self.phase) / (1.0 - side_val));
// smoothstep
let carrier = 1.0
- ((1.0 - peak_val)
* (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base)));
// multiple of the frequency the modulators are at
let multiple = formant_freq / base_freq.max(1e-6);
// round them to the closest integer of the formant freq
let freq_a = multiple.floor();
let freq_b = freq_a + 1.0;
// and how much to lerp between them
let blend = multiple.fract();
// get the true modulator
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
let wave = carrier * modulator;
// increment phase (very imporant)
self.phase += base_freq * self.inv_sample_rate;
// wrap around
self.phase = self.phase.fract();
out.write(frame, wave);
}
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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,

View file

@ -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 {

View file

@ -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)]

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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)]

View file

@ -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 {

View file

@ -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;

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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};

View file

@ -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 {

View file

@ -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;

View file

@ -210,7 +210,7 @@ pub struct Block2JITCompiler {
#[cfg(not(feature = "synfx-dsp-jit"))]
pub enum ASTNode {
NoSynfxDSPJit
NoSynfxDSPJit,
}
impl Block2JITCompiler {

View file

@ -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");

View file

@ -6,10 +6,10 @@
*/
mod compiler;
mod definition;
mod language;
mod compiler;
pub use compiler::*;
pub use definition::*;
pub use language::*;
pub use compiler::*;

164
tests/node_formfm.rs Normal file
View file

@ -0,0 +1,164 @@
// Copyright (c) 2022 Dimas Leenman <skythedragon@outlook.com>
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
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("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap();
let formant = NodeId::FormFM(0);
// params
let freq_p = formant.inp_param("freq").unwrap();
let form_p = formant.inp_param("form").unwrap();
let side_p = formant.inp_param("side").unwrap();
let peak_p = formant.inp_param("peak").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(side_p, SAtom::param(0.2));
matrix.set_param(peak_p, SAtom::param(0.4));
// 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("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap();
let formant = NodeId::FormFM(0);
// params
let freq_p = formant.inp_param("freq").unwrap();
let form_p = formant.inp_param("form").unwrap();
let side_p = formant.inp_param("side").unwrap();
let peak_p = formant.inp_param("peak").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(side_p, SAtom::param(0.2));
matrix.set_param(peak_p, SAtom::param(0.4));
// run
let res = run_for_ms(&mut node_exec, 100.0);
// average should remain at ~0
let sum = res.0.iter().sum::<f32>();
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("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap();
let formant = NodeId::FormFM(0);
// params
let freq_p = formant.inp_param("freq").unwrap();
let form_p = formant.inp_param("form").unwrap();
let side_p = formant.inp_param("side").unwrap();
let peak_p = formant.inp_param("peak").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(side_p, SAtom::param(0.2));
matrix.set_param(peak_p, SAtom::param(0.4));
// 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
// side to 0
matrix.set_param(freq_p, SAtom::param(-0.2));
matrix.set_param(form_p, SAtom::param(0.0));
matrix.set_param(side_p, SAtom::param(-1.0));
matrix.set_param(peak_p, SAtom::param(0.4));
// 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
// side to 1
matrix.set_param(freq_p, SAtom::param(-0.2));
matrix.set_param(form_p, SAtom::param(0.0));
matrix.set_param(peak_p, SAtom::param(1.0));
matrix.set_param(side_p, SAtom::param(0.4));
// 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() {
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("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap();
let formant = NodeId::FormFM(0);
// params
let freq_p = formant.inp_param("freq").unwrap();
let form_p = formant.inp_param("form").unwrap();
let side_p = formant.inp_param("side").unwrap();
let peak_p = formant.inp_param("peak").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(side_p, SAtom::param(0.2));
matrix.set_param(peak_p, SAtom::param(0.4));
// run
let fft = run_and_get_avg_fft4096_now(&mut node_exec, 100);
assert_eq!(fft, vec![(323, 106), (334, 131), (431, 430), (441, 708), (452, 288), (549, 140)]);
}