diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index c307717..4b0b35b 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -513,6 +513,8 @@ mod node_map; #[allow(non_upper_case_globals)] mod node_midicc; #[allow(non_upper_case_globals)] +mod node_exta; +#[allow(non_upper_case_globals)] mod node_midip; #[allow(non_upper_case_globals)] mod node_mix3; @@ -605,6 +607,7 @@ use node_fbwr_fbrd::FbWr; use node_formfm::FormFM; use node_map::Map; use node_midicc::MidiCC; +use node_exta::ExtA; use node_midip::MidiP; use node_mix3::Mix3; use node_mux9::Mux9; @@ -1449,6 +1452,14 @@ macro_rules! node_list { [0 sig1] [1 sig2] [2 sig3], + exta => ExtA UIType::Generic UICategory::IOUtil + (0 slew n_timz d_timz r_tmz f_ms stp_m 0.0, 1.0, 0.0) + (1 atv1 n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0) + (2 atv2 n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0) + (3 atv3 n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0) + [0 sig1] + [1 sig2] + [2 sig3], out => Out UIType::Generic UICategory::IOUtil (0 ch1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (1 ch2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) diff --git a/src/dsp/node_exta.rs b/src/dsp/node_exta.rs new file mode 100644 index 0000000..d1284fe --- /dev/null +++ b/src/dsp/node_exta.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2022 Weird Constructor +// This file is a part of HexoDSP. Released under GPL-3.0-or-later. +// See README.md and COPYING for details. + +use crate::dsp::{ + denorm, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, +}; +use crate::nodes::{HxMidiEvent, MidiEventPointer, NodeAudioContext, NodeExecContext}; +use synfx_dsp::SlewValue; + +/// The (stereo) output port of the plugin +#[derive(Debug, Clone)] +pub struct ExtA { + slew1: SlewValue, + slew2: SlewValue, + slew3: SlewValue, +} + +impl ExtA { + pub fn new(_nid: &NodeId) -> Self { + Self { slew1: SlewValue::new(), slew2: SlewValue::new(), slew3: SlewValue::new() } + } + + pub const slew: &'static str = "ExtA slew\nSlew limiter for the 3 parameters\nRange: (0..1)"; + pub const atv1: &'static str = "ExtA atv1\nAttenuverter for the A1 parameter\nRange: (-1..1)"; + pub const atv2: &'static str = "ExtA atv2\nAttenuverter for the A2 parameter\nRange: (-1..1)"; + pub const atv3: &'static str = "ExtA atv3\nAttenuverter for the A3 parameter\nRange: (-1..1)"; + + pub const sig1: &'static str = "ExtA sig1\nA1 output channel\nRange: (-1..1)"; + pub const sig2: &'static str = "ExtA sig2\nA2 output channel\nRange: (-1..1)"; + pub const sig3: &'static str = "ExtA sig3\nA3 output channel\nRange: (-1..1)"; + + pub const DESC: &'static str = "External Parameter Set A Input\n\n\ + \ + \ + \ + "; + pub const HELP: &'static str = r#"External Parameter Set A Input +"#; +} + +impl DspNode for ExtA { + fn outputs() -> usize { + 0 + } + + fn set_sample_rate(&mut self, _srate: f32) {} + fn reset(&mut self) {} + + #[inline] + fn process( + &mut self, + ctx: &mut T, + ectx: &mut NodeExecContext, + _nctx: &NodeContext, + atoms: &[SAtom], + inputs: &[ProcBuf], + outputs: &mut [ProcBuf], + ctx_vals: LedPhaseVals, + ) { + let slew = inp::ExtA::slew(inputs); + let atv1 = inp::ExtA::atv1(inputs); + let atv2 = inp::ExtA::atv2(inputs); + let atv3 = inp::ExtA::atv3(inputs); + let sig2_i = out_idx::ExtA::sig2(); + let (sig1, r) = outputs.split_at_mut(sig2_i); + let (sig2, sig3) = r.split_at_mut(1); + let sig1 = &mut sig1[0]; + let sig2 = &mut sig2[0]; + let sig3 = &mut sig3[0]; + + if let Some(params) = &ectx.ext_param { + for frame in 0..ctx.nframes() { + let slew_ms = denorm::ExtA::slew(slew, frame); + sig1.write( + frame, + denorm::ExtA::atv1(atv1, frame) * self.slew1.next(params.a1(), slew_ms), + ); + sig2.write( + frame, + denorm::ExtA::atv2(atv2, frame) * self.slew2.next(params.a2(), slew_ms), + ); + sig3.write( + frame, + denorm::ExtA::atv3(atv3, frame) * self.slew3.next(params.a3(), slew_ms), + ); + } + } + + // ctx_vals[0].set(if change { 1.0 } else { 0.0 }); + } +} diff --git a/src/nodes/node_exec.rs b/src/nodes/node_exec.rs index cc0beab..15bd9a5 100644 --- a/src/nodes/node_exec.rs +++ b/src/nodes/node_exec.rs @@ -175,12 +175,21 @@ impl Default for FeedbackBuffer { } } +/// This trait needs to be implemented by the caller of the [NodeExecutor] +/// if it wants to provide the parameters for the "ExtA" to "ExtL" nodes. +pub trait ExternalParams: Send + Sync { + fn a1(&self) -> f32; + fn a2(&self) -> f32; + fn a3(&self) -> f32; +} + /// Contains global state that all nodes can access. /// This is used for instance to implement the feedbackd delay nodes. pub struct NodeExecContext { pub feedback_delay_buffers: Vec, pub midi_notes: Vec, pub midi_ccs: Vec, + pub ext_param: Option>, } impl NodeExecContext { @@ -189,7 +198,7 @@ impl NodeExecContext { fbdb.resize_with(MAX_ALLOCATED_NODES, FeedbackBuffer::new); let midi_notes = Vec::with_capacity(MAX_MIDI_NOTES_PER_BLOCK); let midi_ccs = Vec::with_capacity(MAX_MIDI_CC_PER_BLOCK); - Self { feedback_delay_buffers: fbdb, midi_notes, midi_ccs } + Self { feedback_delay_buffers: fbdb, midi_notes, midi_ccs, ext_param: None } } fn set_sample_rate(&mut self, srate: f32) { @@ -361,6 +370,10 @@ impl NodeExecutor { } } + pub fn set_external_params(&mut self, ext_param: Arc) { + self.exec_ctx.ext_param = Some(ext_param); + } + pub fn set_sample_rate(&mut self, sample_rate: f32) { self.sample_rate = sample_rate; self.exec_ctx.set_sample_rate(sample_rate); diff --git a/tests/node_exta.rs b/tests/node_exta.rs new file mode 100644 index 0000000..6324679 --- /dev/null +++ b/tests/node_exta.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2022 Weird Constructor +// 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::*; + +struct MyParams {} + +impl hexodsp::nodes::ExternalParams for MyParams { + fn a1(&self) -> f32 { + 0.23 + } + fn a2(&self) -> f32 { + 0.44 + } + fn a3(&self) -> f32 { + -0.33 + } +} + +#[test] +fn check_node_exta() { + let (node_conf, mut node_exec) = new_node_engine(); + let mut matrix = Matrix::new(node_conf, 3, 3); + + let myparams = std::sync::Arc::new(MyParams {}); + + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("exta", "sig1").node_inp("out", "ch1").place(&mut matrix, 0, 0).unwrap(); + let mut chain = MatrixCellChain::new(CellDir::B); + chain.node_out("exta", "sig3").node_inp("out", "ch2").place(&mut matrix, 1, 0).unwrap(); + matrix.sync().unwrap(); + + node_exec.set_external_params(myparams); + + let (ch1, ch2) = node_exec.test_run(0.1, false, &[]); + assert_decimated_feq!(ch1, 10, vec![0.23; 100]); + assert_decimated_feq!(ch2, 10, vec![-0.33; 100]); + + node_pset_n(&mut matrix, "exta", 0, "atv1", -1.0); + node_pset_n(&mut matrix, "exta", 0, "atv3", 0.5); + + let (ch1, ch2) = node_exec.test_run(0.1, false, &[]); + assert_decimated_feq!( + ch1, + 80, + vec![ + 0.22895692, + 0.14551038, + 0.062063817, + -0.021382917, + -0.10482957, + -0.18827613, + -0.23, + -0.23, + -0.23, + -0.23, + -0.23 + ] + ); + assert_decimated_feq!( + ch2, + 80, + vec![ + -0.32962584, + -0.29969355, + -0.26976123, + -0.23982893, + -0.20989662, + -0.17996432, + -0.165, + -0.165, + -0.165, + -0.165, + -0.165, + -0.165, + -0.165 + ] + ); +}