diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index d4344ee..0e0cc72 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -76,6 +76,18 @@ pub const MIDI_MAX_FREQ : f32 = 13289.75; pub const MAX_BLOCK_SIZE : usize = 128; + +/// A context structure that holds temporary information about the +/// currently executed node. +/// This structure is created by the [crate::nodes::NodeExecutor] on the fly. +pub struct NodeContext<'a> { + /// The bitmask that indicates which output ports are used/connected + /// to some input. + pub out_connected: u64, + /// The node parameters, which are usually not accessed directly. + pub params: &'a [ProcBuf], +} + /// This trait is an interface between the graph functions /// and the AtomDataModel of the UI. pub trait GraphAtomData { @@ -116,8 +128,9 @@ pub trait DspNode { /// * `outputs` are the output buffers of this node. fn process( &mut self, ctx: &mut T, ectx: &mut NodeExecContext, - atoms: &[SAtom], params: &[ProcBuf], inputs: &[ProcBuf], - outputs: &mut [ProcBuf], led: LedPhaseVals); + nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], + led: LedPhaseVals); /// A function factory for generating a graph for the generic node UI. fn graph_fun() -> Option { None } @@ -564,7 +577,11 @@ macro_rules! node_list { {1 0 p param(0.0) fa_test_s 0 10} {2 1 trig param(0.0) fa_test_s 0 0} [0 sig] - [1 tsig], + [1 tsig] + [2 out2] + [3 out3] + [4 out4] + [5 outc], } } } @@ -1119,6 +1136,15 @@ macro_rules! make_node_info_enum { })+ } + #[allow(non_snake_case)] + pub mod out_buf { + $(pub mod $variant { + $(#[inline] pub fn $out(outputs: &mut [crate::dsp::ProcBuf]) -> crate::dsp::ProcBuf { + outputs[$out_idx] + })* + })+ + } + #[allow(non_snake_case)] pub mod out_idx { $(pub mod $variant { @@ -1126,6 +1152,15 @@ macro_rules! make_node_info_enum { })+ } + #[allow(non_snake_case)] + pub mod is_out_con { + $(pub mod $variant { + $(#[inline] pub fn $out(nctx: &crate::dsp::NodeContext) -> bool { + nctx.out_connected & (1 << $out_idx) != 0x0 + })* + })+ + } + mod ni { $( #[derive(Debug, Clone)] @@ -1441,8 +1476,8 @@ impl Node { #[inline] pub fn process( &mut self, ctx: &mut T, ectx: &mut NodeExecContext, - atoms: &[SAtom], params: &[ProcBuf], - inputs: &[ProcBuf], outputs: &mut [ProcBuf], + nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], led: LedPhaseVals) { macro_rules! make_node_process { @@ -1462,7 +1497,7 @@ impl Node { match self { Node::$v1 => {}, $(Node::$variant { node } => - node.process(ctx, ectx, atoms, params, + node.process(ctx, ectx, nctx, atoms, inputs, outputs, led),)+ } } diff --git a/src/dsp/node_ad.rs b/src/dsp/node_ad.rs index d1a4ddc..c1c8a2d 100644 --- a/src/dsp/node_ad.rs +++ b/src/dsp/node_ad.rs @@ -5,7 +5,7 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::dsp::{ NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, - GraphAtomData, GraphFun, + GraphAtomData, GraphFun, NodeContext, }; use super::helpers::{Trigger, TrigSignal, sqrt4_to_pow4}; @@ -120,7 +120,8 @@ impl DspNode for Ad { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, denorm, at}; diff --git a/src/dsp/node_allp.rs b/src/dsp/node_allp.rs index 3153c0d..5cd7621 100644 --- a/src/dsp/node_allp.rs +++ b/src/dsp/node_allp.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; use crate::dsp::helpers::AllPass; /// A simple amplifier @@ -82,7 +82,8 @@ impl DspNode for AllP { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - _atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + _atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, denorm}; diff --git a/src/dsp/node_amp.rs b/src/dsp/node_amp.rs index d0131a7..63c0a96 100644 --- a/src/dsp/node_amp.rs +++ b/src/dsp/node_amp.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; #[macro_export] macro_rules! fa_amp_neg_att { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -71,7 +71,8 @@ impl DspNode for Amp { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, denorm, denorm_v, inp_dir, at}; diff --git a/src/dsp/node_delay.rs b/src/dsp/node_delay.rs index 0af5478..04cb6b1 100644 --- a/src/dsp/node_delay.rs +++ b/src/dsp/node_delay.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; use crate::dsp::helpers::{DelayBuffer, crossfade, TriggerSampleClock}; #[macro_export] @@ -91,7 +91,8 @@ impl DspNode for Delay { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{at, out, inp, denorm}; diff --git a/src/dsp/node_fbwr_fbrd.rs b/src/dsp/node_fbwr_fbrd.rs index 8ad881a..d53345d 100644 --- a/src/dsp/node_fbwr_fbrd.rs +++ b/src/dsp/node_fbwr_fbrd.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; /// A simple amplifier #[derive(Debug, Clone)] @@ -54,7 +54,8 @@ impl DspNode for FbWr { #[inline] fn process( &mut self, ctx: &mut T, ectx: &mut NodeExecContext, - _atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + _atoms: &[SAtom], inputs: &[ProcBuf], _outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{inp}; @@ -126,7 +127,8 @@ impl DspNode for FbRd { #[inline] fn process( &mut self, ctx: &mut T, ectx: &mut NodeExecContext, - _atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + _atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, denorm}; diff --git a/src/dsp/node_map.rs b/src/dsp/node_map.rs index 6e8cc1c..085aab1 100644 --- a/src/dsp/node_map.rs +++ b/src/dsp/node_map.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; #[macro_export] macro_rules! fa_map_clip { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -91,7 +91,8 @@ impl DspNode for Map { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, at}; diff --git a/src/dsp/node_noise.rs b/src/dsp/node_noise.rs index a48713b..758fde6 100644 --- a/src/dsp/node_noise.rs +++ b/src/dsp/node_noise.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; use crate::dsp::helpers::Rng; #[macro_export] @@ -84,7 +84,8 @@ impl DspNode for Noise { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{at, out, inp, denorm}; diff --git a/src/dsp/node_out.rs b/src/dsp/node_out.rs index 18625bc..bf30742 100644 --- a/src/dsp/node_out.rs +++ b/src/dsp/node_out.rs @@ -3,7 +3,8 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, inp, at, denorm, DspNode, LedPhaseVals}; +use crate::dsp::{ + NodeId, SAtom, ProcBuf, inp, at, denorm, DspNode, LedPhaseVals, NodeContext}; #[macro_export] macro_rules! fa_out_mono { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -81,7 +82,8 @@ impl DspNode for Out { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], _outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { let in1 = inp::Out::ch1(inputs); diff --git a/src/dsp/node_sampl.rs b/src/dsp/node_sampl.rs index c425bca..dbbfc4e 100644 --- a/src/dsp/node_sampl.rs +++ b/src/dsp/node_sampl.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; use crate::dsp::{out, at, inp, denorm, denorm_offs}; //, inp, denorm, denorm_v, inp_dir, at}; use super::helpers::Trigger; @@ -370,7 +370,8 @@ impl DspNode for Sampl { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { let sample = at::Sampl::sample(atoms); diff --git a/src/dsp/node_sin.rs b/src/dsp/node_sin.rs index 1dcc9d4..3611492 100644 --- a/src/dsp/node_sin.rs +++ b/src/dsp/node_sin.rs @@ -5,7 +5,7 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::dsp::{ NodeId, SAtom, ProcBuf, denorm_offs, - out, inp, DspNode, LedPhaseVals + out, inp, DspNode, LedPhaseVals, NodeContext }; use crate::dsp::helpers::fast_sin; @@ -76,7 +76,8 @@ impl DspNode for Sin { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - _atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + _atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { let o = out::Sin::sig(outputs); diff --git a/src/dsp/node_smap.rs b/src/dsp/node_smap.rs index fb46e2d..67d3cbf 100644 --- a/src/dsp/node_smap.rs +++ b/src/dsp/node_smap.rs @@ -3,7 +3,7 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; #[macro_export] macro_rules! fa_smap_clip { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -98,7 +98,8 @@ impl DspNode for SMap { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, at}; diff --git a/src/dsp/node_test.rs b/src/dsp/node_test.rs index e8f21c9..3a5f58c 100644 --- a/src/dsp/node_test.rs +++ b/src/dsp/node_test.rs @@ -3,7 +3,10 @@ // See README.md and COPYING for details. use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::dsp::{NodeId, SAtom, ProcBuf, GraphFun, GraphAtomData, DspNode, LedPhaseVals}; +use crate::dsp::{ + NodeId, SAtom, ProcBuf, GraphFun, GraphAtomData, DspNode, LedPhaseVals, + NodeContext +}; use crate::dsp::helpers::{TrigSignal}; #[macro_export] @@ -46,6 +49,10 @@ impl Test { pub const trig: &'static str = "Test trig\nA trigger input, that will create a short pulse on the 'tsig' output.\nRange: (-1..1)"; pub const sig : &'static str = "Test sig\nThe output of p as signal"; pub const tsig : &'static str = "Test tsig\nA short trigger pulse will be generated when the 'trig' input is triggered."; + pub const out2 : &'static str = "Test out2\nA test output that will emit 1.0 if output 'sig' is connected."; + pub const out3 : &'static str = "Test out3\n"; + pub const out4 : &'static str = "Test out4\n"; + pub const outc : &'static str = "Test outc\nEmits a number that defines the out_connected bitmask. Used only for testing!"; pub const DESC : &'static str = r#""#; pub const HELP : &'static str = r#""#; @@ -66,14 +73,18 @@ impl DspNode for Test { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], _inputs: &[ProcBuf], + nctx: &NodeContext, + atoms: &[SAtom], _inputs: &[ProcBuf], outputs: &mut [ProcBuf], _led: LedPhaseVals) { - use crate::dsp::{out_idx, at}; + use crate::dsp::{out_idx, at, is_out_con, out_buf}; - let p = at::Test::p(atoms); - let trig = at::Test::trig(atoms); - let tsig = out_idx::Test::tsig(); + let p = at::Test::p(atoms); + let trig = at::Test::trig(atoms); + let tsig = out_idx::Test::tsig(); + + let mut out2 = out_buf::Test::out2(outputs); + let mut outc = out_buf::Test::outc(outputs); let (out, tsig) = outputs.split_at_mut(tsig); let out = &mut out[0]; @@ -99,6 +110,10 @@ impl DspNode for Test { out.write(frame, p.f()); let t = self.trig_sig.next(); tsig.write(frame, t); + + out2.write(frame, + if is_out_con::Test::sig(nctx) { 1.0 } else { 0.0 }); + outc.write(frame, nctx.out_connected as f32); } } diff --git a/src/dsp/node_tseq.rs b/src/dsp/node_tseq.rs index d400b18..dea8bb5 100644 --- a/src/dsp/node_tseq.rs +++ b/src/dsp/node_tseq.rs @@ -4,7 +4,7 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::dsp::helpers::{TriggerPhaseClock, Trigger}; -use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; use crate::dsp::tracker::TrackerBackend; use crate::dsp::MAX_BLOCK_SIZE; @@ -180,7 +180,8 @@ impl DspNode for TSeq { #[inline] fn process( &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + _nctx: &NodeContext, + atoms: &[SAtom], inputs: &[ProcBuf], outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) { use crate::dsp::{out, inp, at, denorm}; diff --git a/src/matrix.rs b/src/matrix.rs index a4a20bf..2d84c7b 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1054,9 +1054,9 @@ mod tests { assert!(nodes[2].to_id(2) == NodeId::Sin(2)); let prog = node_exec.get_prog(); - assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1) in=(0-2) at=(0-0) mod=(0-0))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-2) in=(2-4) at=(0-0) mod=(0-0) cpy=(o0 => i2))"); - assert_eq!(prog.prog[2].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0) mod=(0-0) cpy=(o1 => i4))"); + assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1|1) in=(0-2) at=(0-0) mod=(0-0))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-2|1) in=(2-4) at=(0-0) mod=(0-0) cpy=(o0 => i2))"); + assert_eq!(prog.prog[2].to_string(), "Op(i=2 out=(2-3|0) in=(4-6) at=(0-0) mod=(0-0) cpy=(o1 => i4))"); } #[test] @@ -1146,8 +1146,8 @@ mod tests { let prog = node_exec.get_prog(); assert_eq!(prog.prog.len(), 2); - assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1) in=(0-2) at=(0-0) mod=(0-0))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-1) in=(2-5) at=(0-1) mod=(0-0) cpy=(o0 => i2))"); + assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1|1) in=(0-2) at=(0-0) mod=(0-0))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-1|0) in=(2-5) at=(0-1) mod=(0-0) cpy=(o0 => i2))"); } #[test] @@ -1177,8 +1177,8 @@ mod tests { let prog = node_exec.get_prog(); assert_eq!(prog.prog.len(), 2); - assert_eq!(prog.prog[0].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0) mod=(0-0))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-3) in=(6-9) at=(0-1) mod=(0-0) cpy=(o2 => i6))"); + assert_eq!(prog.prog[0].to_string(), "Op(i=2 out=(2-3|1) in=(4-6) at=(0-0) mod=(0-0))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-3|0) in=(6-9) at=(0-1) mod=(0-0) cpy=(o2 => i6))"); } #[test] @@ -1278,10 +1278,10 @@ mod tests { node_exec.process_graph_updates(); let prog = node_exec.get_prog(); - assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1) in=(0-2) at=(0-0) mod=(0-1))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-4) in=(6-8) at=(0-0) mod=(5-5))"); - assert_eq!(prog.prog[2].to_string(), "Op(i=1 out=(1-2) in=(2-4) at=(0-0) mod=(1-3) cpy=(o0 => i2) mod=1)"); - assert_eq!(prog.prog[3].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0) mod=(3-5) cpy=(o1 => i4) cpy=(o3 => i5) mod=3 mod=4)"); + assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1|1) in=(0-2) at=(0-0) mod=(0-1))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-4|1) in=(6-8) at=(0-0) mod=(5-5))"); + assert_eq!(prog.prog[2].to_string(), "Op(i=1 out=(1-2|1) in=(2-4) at=(0-0) mod=(1-3) cpy=(o0 => i2) mod=1)"); + assert_eq!(prog.prog[3].to_string(), "Op(i=2 out=(2-3|0) in=(4-6) at=(0-0) mod=(3-5) cpy=(o1 => i4) cpy=(o3 => i5) mod=3 mod=4)"); } #[test] @@ -1315,8 +1315,8 @@ mod tests { node_exec.process_graph_updates(); let prog = node_exec.get_prog(); - assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1) in=(0-2) at=(0-0) mod=(0-1))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-2) in=(2-4) at=(0-0) mod=(1-3) cpy=(o0 => i2) mod=1)"); - assert_eq!(prog.prog[2].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0) mod=(3-3) cpy=(o1 => i4))"); + assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1|1) in=(0-2) at=(0-0) mod=(0-1))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-2|1) in=(2-4) at=(0-0) mod=(1-3) cpy=(o0 => i2) mod=1)"); + assert_eq!(prog.prog[2].to_string(), "Op(i=2 out=(2-3|0) in=(4-6) at=(0-0) mod=(3-3) cpy=(o1 => i4))"); } } diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index 686c197..fc0f52c 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -56,17 +56,17 @@ impl NodeInstance { pub fn new(id: NodeId) -> Self { Self { id, - in_use: false, - prog_idx: 0, - out_start: 0, - out_end: 0, - in_start: 0, - in_end: 0, - at_start: 0, - at_end: 0, - mod_start: 0, - mod_end: 0, - in2mod_map: [None; MAX_INPUTS], + in_use: false, + prog_idx: 0, + out_start: 0, + out_end: 0, + in_start: 0, + in_end: 0, + at_start: 0, + at_end: 0, + mod_start: 0, + mod_end: 0, + in2mod_map: [None; MAX_INPUTS], } } @@ -75,12 +75,13 @@ impl NodeInstance { pub fn as_op(&self) -> NodeOp { NodeOp { - idx: self.prog_idx as u8, - out_idxlen: (self.out_start, self.out_end), - in_idxlen: (self.in_start, self.in_end), - at_idxlen: (self.at_start, self.at_end), - mod_idxlen: (self.mod_start, self.mod_end), - inputs: vec![], + idx: self.prog_idx as u8, + out_idxlen: (self.out_start, self.out_end), + in_idxlen: (self.in_start, self.in_end), + at_idxlen: (self.at_start, self.at_end), + mod_idxlen: (self.mod_start, self.mod_end), + out_connected: 0x0, + inputs: vec![], } } diff --git a/src/nodes/node_exec.rs b/src/nodes/node_exec.rs index dc30cd3..91b11bc 100644 --- a/src/nodes/node_exec.rs +++ b/src/nodes/node_exec.rs @@ -7,7 +7,7 @@ use super::{ UNUSED_MONITOR_IDX, MAX_ALLOCATED_NODES, MAX_SMOOTHERS, MAX_FB_DELAY_SIZE, FB_DELAY_TIME_US }; -use crate::dsp::{NodeId, Node, MAX_BLOCK_SIZE}; +use crate::dsp::{NodeId, Node, NodeContext, MAX_BLOCK_SIZE}; use crate::util::{Smoother, AtomicFloat}; use crate::monitor::{MonitorBackend, MON_SIG_CNT}; @@ -447,8 +447,11 @@ impl NodeExecutor { .process( ctx, exec_ctx, + &NodeContext { + out_connected: op.out_connected, + params: &prog.inp[inp.0..inp.1], + }, &prog.atoms[at.0..at.1], - &prog.inp[inp.0..inp.1], &prog.cur_inp[inp.0..inp.1], &mut prog.out[out.0..out.1], &ctx_vals[ctx_idx..ctx_idx + 2]); diff --git a/src/nodes/node_prog.rs b/src/nodes/node_prog.rs index 6f0994f..4f5b119 100644 --- a/src/nodes/node_prog.rs +++ b/src/nodes/node_prog.rs @@ -81,14 +81,34 @@ pub struct NodeOp { /// (, , /// (, )) pub inputs: Vec<(usize, usize, Option)>, + /// A bit mask which indicates which of the output ports are actually + /// used/connected to some input. + pub out_connected: u64, +} + +impl NodeOp { + pub fn out_idx_belongs_to_nodeop(&self, idx: usize) -> bool { + idx >= self.out_idxlen.0 + && idx < self.out_idxlen.1 + } + + pub fn set_out_idx_connected_flag(&mut self, global_idx: usize) { + if !self.out_idx_belongs_to_nodeop(global_idx) { + return; + } + + let local_idx = global_idx - self.out_idxlen.0; + self.out_connected |= 0x1 << local_idx; + } } impl std::fmt::Display for NodeOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Op(i={} out=({}-{}) in=({}-{}) at=({}-{}) mod=({}-{})", + write!(f, "Op(i={} out=({}-{}|{:x}) in=({}-{}) at=({}-{}) mod=({}-{})", self.idx, self.out_idxlen.0, self.out_idxlen.1, + self.out_connected, self.in_idxlen.0, self.in_idxlen.1, self.at_idxlen.0, @@ -167,7 +187,6 @@ impl Drop for NodeProg { } } - impl NodeProg { pub fn empty() -> Self { let out_fb = vec![]; @@ -237,13 +256,14 @@ impl NodeProg { &mut self.modops } - pub fn append_op(&mut self, node_op: NodeOp) { + pub fn append_op(&mut self, mut node_op: NodeOp) { for n_op in self.prog.iter_mut() { if n_op.idx == node_op.idx { return; } } + node_op.out_connected = 0x0; self.prog.push(node_op); } @@ -254,6 +274,12 @@ impl NodeProg { out_index: usize, mod_index: Option) { + for n_op in self.prog.iter_mut() { + if n_op.out_idx_belongs_to_nodeop(out_index) { + n_op.set_out_idx_connected_flag(out_index); + } + } + for n_op in self.prog.iter_mut() { if n_op.idx == node_op.idx { n_op.inputs.push((out_index, inp_index, mod_index)); diff --git a/tests/node_test.rs b/tests/node_test.rs index 96c4c9c..42ff0a3 100644 --- a/tests/node_test.rs +++ b/tests/node_test.rs @@ -28,3 +28,54 @@ fn check_node_test_1() { let res = run_for_ms(&mut node_exec, 1.0); assert_decimated_feq!(res.0, 1, vec![ 0.0; 10 ]); } + +#[test] +fn check_node_test_out_connected() { + let (node_conf, mut node_exec) = new_node_engine(); + let mut matrix = Matrix::new(node_conf, 6, 3); + + let test = NodeId::Test(0); + let out = NodeId::Out(0); + let sin = NodeId::Sin(0); + let sin2 = NodeId::Sin(1); + let sin3 = NodeId::Sin(2); + matrix.place(0, 0, Cell::empty(test) + .out(None, None, test.out("outc"))); + matrix.place(0, 1, Cell::empty(out) + .input(out.inp("ch1"), None, None)); + + matrix.place(1, 0, Cell::empty(test) + .out(None, None, test.out("out2"))); + matrix.place(1, 1, Cell::empty(out) + .input(out.inp("ch2"), None, None)); + + matrix.place(2, 0, Cell::empty(test) + .out(None, None, test.out("out3"))); + matrix.place(2, 1, Cell::empty(sin) + .input(sin.inp("freq"), None, None)); + + matrix.place(3, 0, Cell::empty(test) + .out(None, None, test.out("out4"))); + matrix.place(3, 1, Cell::empty(sin2) + .input(sin2.inp("freq"), None, None)); + + matrix.place(4, 0, Cell::empty(test) + .out(None, None, test.out("sig"))); + matrix.place(4, 1, Cell::empty(sin3) + .input(sin3.inp("freq"), None, None)); + matrix.sync().unwrap(); + + let res = run_for_ms(&mut node_exec, 2.0); + let mask = 0x01 | 0x04 | 0x08 | 0x10 | 0x20; + assert_decimated_feq!(res.0, 1, vec![ mask as f32; 10 ]); + assert_decimated_feq!(res.1, 1, vec![ 1.0; 10 ]); + + // Remove a connection for testing: + matrix.place(1, 1, Cell::empty(NodeId::Nop)); + matrix.sync().unwrap(); + + let res = run_for_ms(&mut node_exec, 2.0); + let mask = 0x01 | 0x08 | 0x10 | 0x20; + assert_decimated_feq!(res.0, 1, vec![ mask as f32; 10 ]); + assert_decimated_feq!(res.1, 1, vec![ 0.0; 10 ]); +}