From cbf00e8e39d5c693f973e3a35add671081f54236 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Tue, 19 Jul 2022 11:44:54 +0200 Subject: [PATCH 01/12] initial formant synth --- src/dsp/helpers.rs | 14 ++--- src/dsp/mod.rs | 5 ++ src/dsp/node_formant.rs | 127 ++++++++++++++++++++++++++++++++++++++++ src/nodes/node_conf.rs | 11 +--- src/nodes/node_exec.rs | 4 +- tests/node_delay.rs | 2 +- 6 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 src/dsp/node_formant.rs diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index 5020fc7..91bd482 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -2182,13 +2182,13 @@ impl TriSawLFO { }; if s.abs() > f(1.0) { - println!( - "RECALC TRISAW: rev={}, rise={}, fall={}, phase={}", - self.rev.to_f64().unwrap_or(0.0), - self.rise_r.to_f64().unwrap_or(0.0) as f32, - self.fall_r.to_f64().unwrap_or(0.0) as f32, - self.phase.to_f64().unwrap_or(0.0) as f32 - ); + println!( + "RECALC TRISAW: rev={}, rise={}, fall={}, phase={}", + self.rev.to_f64().unwrap_or(0.0), + self.rise_r.to_f64().unwrap_or(0.0) as f32, + self.fall_r.to_f64().unwrap_or(0.0) as f32, + self.phase.to_f64().unwrap_or(0.0) as f32 + ); } self.phase = self.phase + self.freq * self.israte; diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index e726073..9ae6cd3 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -23,6 +23,8 @@ mod node_delay; #[allow(non_upper_case_globals)] mod node_fbwr_fbrd; #[allow(non_upper_case_globals)] +mod node_formant; +#[allow(non_upper_case_globals)] mod node_map; #[allow(non_upper_case_globals)] mod node_mix3; @@ -108,6 +110,7 @@ use node_cqnt::CQnt; use node_delay::Delay; use node_fbwr_fbrd::FbRd; use node_fbwr_fbrd::FbWr; +use node_formant::Formant; use node_map::Map; use node_mix3::Mix3; use node_mux9::Mux9; @@ -1001,6 +1004,8 @@ 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], + formant => Formant UIType::Generic UICategory::Signal + (0), 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} diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs new file mode 100644 index 0000000..0f73a60 --- /dev/null +++ b/src/dsp/node_formant.rs @@ -0,0 +1,127 @@ +use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger}; +use crate::dsp::{ + DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, +}; +use crate::nodes::{NodeAudioContext, NodeExecContext}; + +/// A simple amplifier +#[derive(Debug, Clone)] +pub struct Formant { + inv_sample_rate: f32, + phase: f32, +} + +impl Formant { + pub fn new(_nid: &NodeId) -> Self { + Self { inv_sample_rate: 1.0 / 44100.0, phase: 0.0 } + } + pub const inp: &'static str = + "Ad inp\nSignal input. If you don't connect this, and set this to 1.0 \ + this will act as envelope signal generator. But you can also just \ + route a signal directly through this of course.\nRange: (-1..1)\n"; + pub const trig: &'static str = + "Ad trig\nTrigger input that starts the attack phase.\nRange: (0..1)\n"; + pub const atk: &'static str = + "Ad atk\nAttack time of the envelope. You can extend the maximum \ + range of this with the 'mult' setting.\nRange: (0..1)\n"; + pub const dcy: &'static str = "Ad atk\nDecay time of the envelope. You can extend the maximum \ + range of this with the 'mult' setting.\nRange: (0..1)\n"; + pub const ashp: &'static str = "Ad ashp\nAttack shape. This allows you to change the shape \ + of the attack stage from a logarithmic, to a linear and to an \ + exponential shape.\nRange: (0..1)\n"; + pub const dshp: &'static str = "Ad dshp\nDecay shape. This allows you to change the shape \ + of the decay stage from a logarithmic, to a linear and to an \ + exponential shape.\nRange: (0..1)\n"; + pub const mult: &'static str = "Ad mult\nAttack and Decay time range multiplier. \ + This will extend the maximum range of the 'atk' and 'dcy' parameters."; + pub const sig: &'static str = + "Ad sig\nEnvelope signal output. If a signal is sent to the 'inp' port, \ + you will receive an attenuated signal here. If you set 'inp' to a \ + fixed value (for instance 1.0), this will output an envelope signal \ + in the range 0.0 to 'inp' (1.0).\nRange: (-1..1)\n"; + pub const eoet: &'static str = + "Ad eoet\nEnd of envelope trigger. This output sends a trigger once \ + the end of the decay stage has been reached.\nRange: (0..1)"; + pub const DESC: &'static str = r#"A direct formant synthesizer + +This generates a single formant from a given frequency, formant frequency, as well as attack and decay frequencies. +The attack and decay frequencies both control the bandwidth of the formant, decay the peak of the bandwidth, attack peak. +"#; + pub const HELP: &'static str = r#"Formant - Single formant synthesizer +This is a formant synthesizer that directly generates the audio, no filters needed. +"#; +} + +impl DspNode for Formant { + 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( + &mut self, + ctx: &mut T, + _ectx: &mut NodeExecContext, + _nctx: &NodeContext, + atoms: &[SAtom], + inputs: &[ProcBuf], + outputs: &mut [ProcBuf], + ctx_vals: LedPhaseVals, + ) { + use crate::dsp::{at, denorm, inp, out}; + + let base_freq = inp::Formant::freq(inputs); + let formant_freq = inp::Formant::fmt(inputs); + let attack_freq = inp::Formant::atk(inputs); + let decay_freq = inp::Formant::dcy(inputs); + let out = out::Formant::sig(outputs); + + for frame in 0..ctx.nframes() { + // where the two decays meet + let carrier_center = decay_freq / (attack_freq + decay_freq); + + // where they meet in amplitude + let carrier_lowest_amplitude = + (-std::f32::consts::TAU * base_freq * carrier_center * decay_freq).exp(); + + // turn it into a triangle wave + let carrier_attack = (1.0 - self.phase) / carrier_center; + let carrier_decay = self.phase / (1.0 - carrier_center); + + // actual triangle wave + let carrier_base = 1.0 - carrier_attack.min(carrier_decay); + + // smoothstep + let carrier = + carrier_base * carrier_base * (3.0 - 2.0 * carrier_base) * carrier_lowest_amplitude + + (1.0 - carrier_lowest_amplitude); + + // multiple of the frequency the modulators are at + let multiple = formant_freq / base_freq; + + // 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) * (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; + + out.write(frame, wave); + } + } +} diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index f067199..ba9f313 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -3,8 +3,8 @@ // See README.md and COPYING for details. use super::{ - FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, - MAX_AVAIL_TRACKERS, MAX_INPUTS, UNUSED_MONITOR_IDX, + FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_TRACKERS, + MAX_INPUTS, UNUSED_MONITOR_IDX, }; use crate::dsp::tracker::{PatternData, Tracker}; use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom}; @@ -245,12 +245,7 @@ impl SharedNodeConf { } ( - Self { - node_ctx_values, - graph_update_prod: rb_graph_prod, - monitor, - drop_thread, - }, + Self { node_ctx_values, graph_update_prod: rb_graph_prod, monitor, drop_thread }, SharedNodeExec { node_ctx_values: exec_node_ctx_vals, graph_update_con: rb_graph_con, diff --git a/src/nodes/node_exec.rs b/src/nodes/node_exec.rs index 069ffb2..699d026 100644 --- a/src/nodes/node_exec.rs +++ b/src/nodes/node_exec.rs @@ -3,8 +3,8 @@ // See README.md and COPYING for details. use super::{ - DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, - MAX_FB_DELAY_SIZE, MAX_SMOOTHERS, UNUSED_MONITOR_IDX, + DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, MAX_FB_DELAY_SIZE, + MAX_SMOOTHERS, UNUSED_MONITOR_IDX, }; use crate::dsp::{Node, NodeContext, NodeId, MAX_BLOCK_SIZE}; use crate::monitor::{MonitorBackend, MON_SIG_CNT}; diff --git a/tests/node_delay.rs b/tests/node_delay.rs index 9bbe833..d137a60 100644 --- a/tests/node_delay.rs +++ b/tests/node_delay.rs @@ -119,7 +119,7 @@ fn check_node_delay_2() { vec![ // 10ms smoothing time for "inp" 0.001133, // 30ms delaytime just mixing the 0.5: - 0.5, 0.5, 0.5, // the delayed smoothing ramp (10ms): + 0.5, 0.5, 0.5, // the delayed smoothing ramp (10ms): 0.951113, // the delay + input signal: 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 ] From ddf42fefd59d6180dd5268e2a383c4e1cf405b1a Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Wed, 20 Jul 2022 12:32:18 +0200 Subject: [PATCH 02/12] now compiles --- src/dsp/mod.rs | 6 ++++- src/dsp/node_formant.rs | 51 ++++++++++++++--------------------------- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 9ae6cd3..0a54d15 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -1005,7 +1005,11 @@ macro_rules! node_list { [0 sig_l] [1 sig_r], formant => Formant UIType::Generic UICategory::Signal - (0), + (0 freq n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + (1 form n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + (2 atk n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + (3 dcy n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + [0 sig], 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} diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 0f73a60..70fefa6 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -1,7 +1,4 @@ -use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger}; -use crate::dsp::{ - DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, -}; +use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; use crate::nodes::{NodeAudioContext, NodeExecContext}; /// A simple amplifier @@ -15,33 +12,13 @@ impl Formant { pub fn new(_nid: &NodeId) -> Self { Self { inv_sample_rate: 1.0 / 44100.0, phase: 0.0 } } - pub const inp: &'static str = - "Ad inp\nSignal input. If you don't connect this, and set this to 1.0 \ - this will act as envelope signal generator. But you can also just \ - route a signal directly through this of course.\nRange: (-1..1)\n"; - pub const trig: &'static str = - "Ad trig\nTrigger input that starts the attack phase.\nRange: (0..1)\n"; + pub const freq: &'static str = "Formant freq\nBase frequency to oscilate at\n"; + pub const form: &'static str = "Formant form\nFrequency of the formant\n"; pub const atk: &'static str = - "Ad atk\nAttack time of the envelope. You can extend the maximum \ - range of this with the 'mult' setting.\nRange: (0..1)\n"; - pub const dcy: &'static str = "Ad atk\nDecay time of the envelope. You can extend the maximum \ - range of this with the 'mult' setting.\nRange: (0..1)\n"; - pub const ashp: &'static str = "Ad ashp\nAttack shape. This allows you to change the shape \ - of the attack stage from a logarithmic, to a linear and to an \ - exponential shape.\nRange: (0..1)\n"; - pub const dshp: &'static str = "Ad dshp\nDecay shape. This allows you to change the shape \ - of the decay stage from a logarithmic, to a linear and to an \ - exponential shape.\nRange: (0..1)\n"; - pub const mult: &'static str = "Ad mult\nAttack and Decay time range multiplier. \ - This will extend the maximum range of the 'atk' and 'dcy' parameters."; - pub const sig: &'static str = - "Ad sig\nEnvelope signal output. If a signal is sent to the 'inp' port, \ - you will receive an attenuated signal here. If you set 'inp' to a \ - fixed value (for instance 1.0), this will output an envelope signal \ - in the range 0.0 to 'inp' (1.0).\nRange: (-1..1)\n"; - pub const eoet: &'static str = - "Ad eoet\nEnd of envelope trigger. This output sends a trigger once \ - the end of the decay stage has been reached.\nRange: (0..1)"; + "Formant atk\nFormant attack bandwidth, controls the general bandwidth"; + pub const dcy: &'static str = + "Formant dcy\nFormant decay bandwidth, controls the peak bandwidth"; + pub const sig: &'static str = "Formant sig\nGenerated formant signal"; pub const DESC: &'static str = r#"A direct formant synthesizer This generates a single formant from a given frequency, formant frequency, as well as attack and decay frequencies. @@ -71,20 +48,26 @@ impl DspNode for Formant { ctx: &mut T, _ectx: &mut NodeExecContext, _nctx: &NodeContext, - atoms: &[SAtom], + _atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], - ctx_vals: LedPhaseVals, + _ctx_vals: LedPhaseVals, ) { - use crate::dsp::{at, denorm, inp, out}; + use crate::dsp::{denorm, inp, out}; let base_freq = inp::Formant::freq(inputs); - let formant_freq = inp::Formant::fmt(inputs); + let formant_freq = inp::Formant::form(inputs); let attack_freq = inp::Formant::atk(inputs); let decay_freq = inp::Formant::dcy(inputs); let out = out::Formant::sig(outputs); for frame in 0..ctx.nframes() { + // get the inputs + let base_freq = denorm::Sampl::freq(base_freq, frame); + let formant_freq = denorm::Sampl::freq(formant_freq, frame); + let attack_freq = denorm::Sampl::freq(attack_freq, frame); + let decay_freq = denorm::Sampl::freq(decay_freq, frame); + // where the two decays meet let carrier_center = decay_freq / (attack_freq + decay_freq); From acc933add3bc2a1c26defb08ed9610f045223a48 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Wed, 20 Jul 2022 13:57:40 +0200 Subject: [PATCH 03/12] increment sample rate --- src/dsp/node_formant.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 70fefa6..dadf4e1 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -104,6 +104,9 @@ impl DspNode for Formant { // entire wave let wave = carrier * modulator; + // increment phase (very imporant) + self.phase += self.inv_sample_rate; + out.write(frame, wave); } } From 505662414a84f8888bd75d214d481764806a6509 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Wed, 20 Jul 2022 14:06:14 +0200 Subject: [PATCH 04/12] sample rate --- src/dsp/node_formant.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index dadf4e1..50d5086 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -105,7 +105,8 @@ impl DspNode for Formant { let wave = carrier * modulator; // increment phase (very imporant) - self.phase += self.inv_sample_rate; + self.phase += base_freq * self.inv_sample_rate; + self.phase = self.phase.fract(); out.write(frame, wave); } From 7b20af0eec0c10ba7cd19c942db1ac62f9c0cf09 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Wed, 20 Jul 2022 14:31:00 +0200 Subject: [PATCH 05/12] proper macros I hope --- src/dsp/mod.rs | 14 +++++++------- src/dsp/node_formant.rs | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 0a54d15..aede570 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -972,6 +972,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], + formant => Formant UIType::Generic UICategory::Osc + (0 freq n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) + (1 form n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) + (2 atk n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) + (3 dcy n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) + [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) @@ -1004,13 +1010,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], - formant => Formant UIType::Generic UICategory::Signal - (0 freq n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) - (1 form n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) - (2 atk n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) - (3 dcy n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) - [0 sig], - 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} diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 50d5086..3084c29 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -63,10 +63,10 @@ impl DspNode for Formant { for frame in 0..ctx.nframes() { // get the inputs - let base_freq = denorm::Sampl::freq(base_freq, frame); - let formant_freq = denorm::Sampl::freq(formant_freq, frame); - let attack_freq = denorm::Sampl::freq(attack_freq, frame); - let decay_freq = denorm::Sampl::freq(decay_freq, frame); + let base_freq = denorm::Formant::freq(base_freq, frame); + let formant_freq = denorm::Formant::freq(formant_freq, frame); + let attack_freq = denorm::Formant::freq(attack_freq, frame); + let decay_freq = denorm::Formant::freq(decay_freq, frame); // where the two decays meet let carrier_center = decay_freq / (attack_freq + decay_freq); From 612660852a43f4c710a1a64c6084ece9e217ad2a Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Wed, 20 Jul 2022 14:45:48 +0200 Subject: [PATCH 06/12] maybe now --- src/dsp/mod.rs | 8 ++++---- src/dsp/node_formant.rs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index aede570..91df89e 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -973,10 +973,10 @@ macro_rules! node_list { {2 0 mode setting(0) mode fa_noise_mode 0 1} [0 sig], formant => Formant UIType::Generic UICategory::Osc - (0 freq n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) - (1 form n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) - (2 atk n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) - (3 dcy n_pit d_pit r_fq f_freq stp_d 0.0, 1.0, 0.5) + (0 freq n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 440.0) + (1 form n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 440.0) + (2 atk n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 44.0) + (3 dcy n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 44.0) [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) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 3084c29..28186d0 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -64,9 +64,9 @@ impl DspNode for Formant { for frame in 0..ctx.nframes() { // get the inputs let base_freq = denorm::Formant::freq(base_freq, frame); - let formant_freq = denorm::Formant::freq(formant_freq, frame); - let attack_freq = denorm::Formant::freq(attack_freq, frame); - let decay_freq = denorm::Formant::freq(decay_freq, frame); + let formant_freq = denorm::Formant::form(formant_freq, frame); + let attack_freq = denorm::Formant::atk(attack_freq, frame); + let decay_freq = denorm::Formant::dcy(decay_freq, frame); // where the two decays meet let carrier_center = decay_freq / (attack_freq + decay_freq); From 8bb075e23e44d415bef59d7dc4e3cdf134dc3368 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Sun, 24 Jul 2022 12:16:39 +0200 Subject: [PATCH 07/12] now it should hopefully work with the new math --- src/dsp/node_formant.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index 28186d0..e07ff7e 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -69,29 +69,26 @@ impl DspNode for Formant { let decay_freq = denorm::Formant::dcy(decay_freq, frame); // where the two decays meet - let carrier_center = decay_freq / (attack_freq + decay_freq); + let carrier_center = attack_freq / (attack_freq + decay_freq); // where they meet in amplitude let carrier_lowest_amplitude = - (-std::f32::consts::TAU * base_freq * carrier_center * decay_freq).exp(); + (-(std::f32::consts::PI * carrier_center * decay_freq) / base_freq).exp(); - // turn it into a triangle wave - let carrier_attack = (1.0 - self.phase) / carrier_center; - let carrier_decay = self.phase / (1.0 - carrier_center); - - // actual triangle wave - let carrier_base = 1.0 - carrier_attack.min(carrier_decay); + // make a triangle wave, with the peak at carrier center + let carrier_base = + (self.phase / carrier_center).min((1.0 - self.phase) / (1.0 - carrier_center)); // smoothstep - let carrier = - carrier_base * carrier_base * (3.0 - 2.0 * carrier_base) * carrier_lowest_amplitude - + (1.0 - carrier_lowest_amplitude); + let carrier = 1.0 + - ((1.0 - carrier_lowest_amplitude) + * (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base))); // multiple of the frequency the modulators are at let multiple = formant_freq / base_freq; // round them to the closest integer of the formant freq - let freq_a = multiple.floor(); + let freq_a = multiple.floor().max(1.0); let freq_b = freq_a + 1.0; // and how much to lerp between them @@ -106,6 +103,8 @@ impl DspNode for Formant { // increment phase (very imporant) self.phase += base_freq * self.inv_sample_rate; + + // wrap around self.phase = self.phase.fract(); out.write(frame, wave); From 2bd22fd73152dda7b258368b2d0b4c98f59f50ea Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Sun, 24 Jul 2022 12:37:56 +0200 Subject: [PATCH 08/12] division by 0 avoid --- src/dsp/node_formant.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index e07ff7e..6e858c3 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -72,8 +72,11 @@ impl DspNode for Formant { let carrier_center = attack_freq / (attack_freq + decay_freq); // where they meet in amplitude - let carrier_lowest_amplitude = - (-(std::f32::consts::PI * carrier_center * decay_freq) / base_freq).exp(); + 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() + }; // make a triangle wave, with the peak at carrier center let carrier_base = @@ -92,7 +95,7 @@ impl DspNode for Formant { let freq_b = freq_a + 1.0; // and how much to lerp between them - let blend = multiple.fract(); + let blend = multiple.max(1.0).fract(); // get the true modulator let modulator = (1.0 - blend) * (std::f32::consts::TAU * self.phase * freq_a).cos() From c656b407296c5887d758b38dcf2c6b445a3f4644 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Sun, 7 Aug 2022 21:28:45 +0200 Subject: [PATCH 09/12] only frequency test left + description --- src/dsp/node_ad.rs | 2 +- src/dsp/node_allp.rs | 2 +- src/dsp/node_biqfilt.rs | 2 +- src/dsp/node_bosc.rs | 2 +- src/dsp/node_bowstri.rs | 2 +- src/dsp/node_comb.rs | 2 +- src/dsp/node_cqnt.rs | 2 +- src/dsp/node_delay.rs | 2 +- src/dsp/node_formant.rs | 28 +++++--- src/dsp/node_mux9.rs | 2 +- src/dsp/node_noise.rs | 2 +- src/dsp/node_pverb.rs | 2 +- src/dsp/node_quant.rs | 2 +- src/dsp/node_rndwk.rs | 2 +- src/dsp/node_sampl.rs | 2 +- src/dsp/node_scope.rs | 2 +- src/dsp/node_sfilter.rs | 4 +- src/dsp/node_sin.rs | 2 +- src/dsp/node_test.rs | 2 +- src/dsp/node_tseq.rs | 2 +- src/dsp/node_tslfo.rs | 2 +- src/dsp/node_vosc.rs | 2 +- src/lib.rs | 2 +- src/matrix.rs | 2 +- src/matrix_repr.rs | 2 +- src/nodes/node_conf.rs | 6 +- src/wblockdsp/compiler.rs | 2 +- src/wblockdsp/definition.rs | 59 ++++++++-------- src/wblockdsp/mod.rs | 4 +- tests/node_formant.rs | 133 ++++++++++++++++++++++++++++++++++++ 30 files changed, 213 insertions(+), 69 deletions(-) create mode 100644 tests/node_formant.rs 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() {} From c4a9b560994a5c6f29802c7cc53d10248e48673d Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Sun, 7 Aug 2022 23:51:12 +0200 Subject: [PATCH 10/12] should be done --- src/dsp/node_formant.rs | 16 ++++++++++++---- tests/node_formant.rs | 29 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formant.rs index dfe4094..c9a623c 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formant.rs @@ -13,19 +13,27 @@ impl Formant { 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\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 atk: &'static str = "Formant atk\nFormant attack bandwidth, controls the general bandwidth"; pub const dcy: &'static str = "Formant dcy\nFormant decay bandwidth, controls the peak bandwidth"; pub const sig: &'static str = "Formant sig\nGenerated formant signal"; - pub const DESC: &'static str = r#"A direct formant synthesizer + pub const DESC: &'static str = r#"Direct formant synthesizer This generates a single formant from a given frequency, formant frequency, as well as attack and decay frequencies. The attack and decay frequencies both control the bandwidth of the formant, decay the peak of the bandwidth, attack peak. "#; - pub const HELP: &'static str = r#"Formant - Single formant synthesizer -This is a formant synthesizer that directly generates the audio, no filters needed. + pub const HELP: &'static str = r#"Formant - Direct formant synthesized +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. +If `form` is lower than `freq`, the overal loudness will go down, however it's guaranteed to never exceed the [-1,1] range. +`atk` and `dcy` both control the bandwidth/resonance of the formant. The further apart they are in value, the higher the bandwidth/lower the resonance. +If these are set to a low value, the sine wave used for the formant effect becomes very audible. "#; } diff --git a/tests/node_formant.rs b/tests/node_formant.rs index e23909d..9bbcdb8 100644 --- a/tests/node_formant.rs +++ b/tests/node_formant.rs @@ -130,4 +130,31 @@ fn check_no_nan() { } #[test] -fn check_formant_freq() {} +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("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 fft = run_and_get_avg_fft4096_now(&mut node_exec, 180); + assert_eq!(fft, vec![(334, 191), (431, 331), (441, 546), (452, 222), (549, 209)]); +} From e1799f6d45f74262fe2e1df39db880b9e7bbd909 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Mon, 8 Aug 2022 13:53:57 +0200 Subject: [PATCH 11/12] renamed and improved, so no more atk/dcy but direct controls instead --- src/dsp/mod.rs | 14 ++-- src/dsp/{node_formant.rs => node_formfm.rs} | 78 ++++++++++----------- tests/{node_formant.rs => node_formfm.rs} | 64 ++++++++--------- 3 files changed, 76 insertions(+), 80 deletions(-) rename src/dsp/{node_formant.rs => node_formfm.rs} (50%) rename tests/{node_formant.rs => node_formfm.rs} (65%) diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 936e189..04e3e4a 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -507,7 +507,7 @@ mod node_delay; #[allow(non_upper_case_globals)] mod node_fbwr_fbrd; #[allow(non_upper_case_globals)] -mod node_formant; +mod node_formfm; #[allow(non_upper_case_globals)] mod node_map; #[allow(non_upper_case_globals)] @@ -595,7 +595,7 @@ use node_cqnt::CQnt; use node_delay::Delay; use node_fbwr_fbrd::FbRd; use node_fbwr_fbrd::FbWr; -use node_formant::Formant; +use node_formfm::FormFM; use node_map::Map; use node_mix3::Mix3; use node_mux9::Mux9; @@ -1499,11 +1499,11 @@ 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], - formant => Formant UIType::Generic UICategory::Osc - (0 freq n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 440.0) - (1 form n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 440.0) - (2 atk n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 44.0) - (3 dcy n_pit d_pit r_fq f_freq stp_d -1.0, 1.0, 44.0) + 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) diff --git a/src/dsp/node_formant.rs b/src/dsp/node_formfm.rs similarity index 50% rename from src/dsp/node_formant.rs rename to src/dsp/node_formfm.rs index c9a623c..702a213 100644 --- a/src/dsp/node_formant.rs +++ b/src/dsp/node_formfm.rs @@ -3,41 +3,50 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; /// A simple amplifier #[derive(Debug, Clone)] -pub struct Formant { +pub struct FormFM { inv_sample_rate: f32, phase: f32, } -impl Formant { +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 atk: &'static str = - "Formant atk\nFormant attack bandwidth, controls the general bandwidth"; - pub const dcy: &'static str = - "Formant dcy\nFormant decay bandwidth, controls the peak bandwidth"; + 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#"Direct formant synthesizer + pub const DESC: &'static str = r#"Formant oscillator -This generates a single formant from a given frequency, formant frequency, as well as attack and decay frequencies. -The attack and decay frequencies both control the bandwidth of the formant, decay the peak of the bandwidth, attack peak. +Simple formant oscillator that generates a formant like sound. +Loosely based on the ModFM synthesis method. "#; - pub const HELP: &'static str = r#"Formant - Direct formant synthesized -This is a formant synthesizer that directly generates the audio of a single formant. + pub const HELP: &'static str = r#"formfm - Direct formant synthesizer -This can be seen as passing a saw wave with frequency `freq` into a bandpass filter with the cutoff at `form` +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. -If `form` is lower than `freq`, the overal loudness will go down, however it's guaranteed to never exceed the [-1,1] range. -`atk` and `dcy` both control the bandwidth/resonance of the formant. The further apart they are in value, the higher the bandwidth/lower the resonance. -If these are set to a low value, the sine wave used for the formant effect becomes very audible. +`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 Formant { +impl DspNode for FormFM { fn outputs() -> usize { 1 } @@ -63,39 +72,26 @@ impl DspNode for Formant { ) { use crate::dsp::{denorm, inp, out}; - let base_freq = inp::Formant::freq(inputs); - let formant_freq = inp::Formant::form(inputs); - let attack_freq = inp::Formant::atk(inputs); - let decay_freq = inp::Formant::dcy(inputs); - let out = out::Formant::sig(outputs); + 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::Formant::freq(base_freq, frame); - let formant_freq = denorm::Formant::form(formant_freq, frame); - let attack_freq = denorm::Formant::atk(attack_freq, frame); - let decay_freq = denorm::Formant::dcy(decay_freq, frame); - - // where the two decays meet - // 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 || base_freq == 0.0 { - 0.0 - } else { - (-(std::f32::consts::PI * carrier_center * decay_freq) / base_freq).exp() - }; + 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 / carrier_center).min((1.0 - self.phase) / (1.0 - carrier_center)); + (self.phase / side_val).min((1.0 - self.phase) / (1.0 - side_val)); // smoothstep let carrier = 1.0 - - ((1.0 - carrier_lowest_amplitude) + - ((1.0 - peak_val) * (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base))); // multiple of the frequency the modulators are at diff --git a/tests/node_formant.rs b/tests/node_formfm.rs similarity index 65% rename from tests/node_formant.rs rename to tests/node_formfm.rs index 9bbcdb8..f54c6f7 100644 --- a/tests/node_formant.rs +++ b/tests/node_formfm.rs @@ -8,23 +8,23 @@ fn check_normalized_if_freq_lower_than_formant_freq() { 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(); + chain.node_out("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); - let formant = NodeId::Formant(0); + let formant = NodeId::FormFM(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(); + 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(atk_p, SAtom::param(0.2)); - matrix.set_param(dcy_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); @@ -42,23 +42,23 @@ fn check_no_dc_bias_at_formant_freq_lower_than_freq() { 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(); + chain.node_out("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); - let formant = NodeId::Formant(0); + let formant = NodeId::FormFM(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(); + 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(atk_p, SAtom::param(0.2)); - matrix.set_param(dcy_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); @@ -77,24 +77,24 @@ fn check_no_nan() { 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(); + chain.node_out("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); - let formant = NodeId::Formant(0); + let formant = NodeId::FormFM(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(); + 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(atk_p, SAtom::param(0.2)); - matrix.set_param(dcy_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); @@ -103,11 +103,11 @@ fn check_no_nan() { assert!(res.0.iter().all(|x| !x.is_nan())); // set params to non-reasonable values here - // base freq attack freq 0 + // side to 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)); + 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); @@ -116,11 +116,11 @@ fn check_no_nan() { assert!(res.0.iter().all(|x| !x.is_nan())); // set params to non-reasonable values here - // decay freq freq 0 + // side to 1 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)); + 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); @@ -136,25 +136,25 @@ fn check_formant_freq() { 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(); + chain.node_out("formfm", "sig").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); matrix.sync().unwrap(); - let formant = NodeId::Formant(0); + let formant = NodeId::FormFM(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(); + 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(atk_p, SAtom::param(0.2)); - matrix.set_param(dcy_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 fft = run_and_get_avg_fft4096_now(&mut node_exec, 180); - assert_eq!(fft, vec![(334, 191), (431, 331), (441, 546), (452, 222), (549, 209)]); + 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)]); } From fd48aebad440ab8c67b78087a1a9c2b41fe153d2 Mon Sep 17 00:00:00 2001 From: Dimas Leenman Date: Mon, 8 Aug 2022 14:19:32 +0200 Subject: [PATCH 12/12] copyright notice --- src/dsp/node_formfm.rs | 4 ++++ tests/node_formfm.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/dsp/node_formfm.rs b/src/dsp/node_formfm.rs index 702a213..265f736 100644 --- a/src/dsp/node_formfm.rs +++ b/src/dsp/node_formfm.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2022 Dimas Leenman +// 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}; diff --git a/tests/node_formfm.rs b/tests/node_formfm.rs index f54c6f7..dc728e6 100644 --- a/tests/node_formfm.rs +++ b/tests/node_formfm.rs @@ -1,3 +1,7 @@ +// Copyright (c) 2022 Dimas Leenman +// 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::*;