From 5d975c91a4b66571c07fe316d309c056dec0d1a8 Mon Sep 17 00:00:00 2001 From: Weird Constructor Date: Wed, 7 Jul 2021 05:03:28 +0200 Subject: [PATCH] implemented more of the parameter modulation amount --- src/matrix.rs | 14 +++---- src/nodes/mod.rs | 2 +- src/nodes/node_conf.rs | 95 +++++++++++++++++++++++++++++++++--------- src/nodes/node_prog.rs | 66 +++++++++++++++++++++-------- 4 files changed, 132 insertions(+), 45 deletions(-) diff --git a/src/matrix.rs b/src/matrix.rs index ad2a84e..34a2320 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))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-2) in=(2-4) at=(0-0) cpy=(o0 => i2))"); - assert_eq!(prog.prog[2].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0) cpy=(o1 => i4))"); + 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))"); } #[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))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-1) in=(2-5) at=(0-1) cpy=(o0 => i2))"); + 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))"); } #[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))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-3) in=(6-9) at=(0-1) cpy=(o2 => i6))"); + 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))"); } #[test] diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 52fd9f2..fa6f45b 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -56,7 +56,7 @@ pub enum GraphMessage { pub enum QuickMessage { AtomUpdate { at_idx: usize, value: SAtom }, ParamUpdate { input_idx: usize, value: f32 }, - ModamtUpdate { input_idx: usize, modamt: f32 }, + ModamtUpdate { input_idx: usize, mod_idx: usize, modamt: f32 }, /// Sets the buffer indices to monitor with the FeedbackProcessor. SetMonitor { bufs: [usize; MON_SIG_CNT], }, } diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index dd3b457..9f15f11 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -4,7 +4,7 @@ use super::{ GraphMessage, QuickMessage, - NodeProg, NodeOp, + NodeProg, ModOpSigRange, NodeOp, FeedbackFilter, MAX_ALLOCATED_NODES, MAX_INPUTS, @@ -85,7 +85,7 @@ impl NodeInstance { } pub fn mod_in_local2global(&self, idx: u8) -> Option { - if idx > self.in2mod_map.len() { + if (idx as usize) > self.in2mod_map.len() { return None; } self.in2mod_map[idx as usize] @@ -126,8 +126,12 @@ impl NodeInstance { self } - pub fn set_mod_idx(&mut self, idx: u8, i: usize) -> &mut Self { - self.in2mod_map[idx as usize] = Some(i); + /// Sets the modulator index mapping: `idx` is the + /// index of the parameter like in [NodeId::inp_param_by_idx], + /// and `i` is the absolute index of the modulator that belongs + /// to this parameter. + pub fn set_mod_idx(&mut self, idx: usize, i: usize) -> &mut Self { + self.in2mod_map[idx] = Some(i); self } @@ -143,7 +147,7 @@ struct NodeInputParam { param_id: ParamId, input_idx: usize, value: f32, - modamt: Option, + modamt: Option<(usize, f32)>, } #[derive(Debug, Clone)] @@ -345,10 +349,15 @@ impl NodeConfigurator { } let mut input_idx = None; + let mut mod_idx = None; if let Some(nparam) = self.params.get_mut(¶m) { - input_idx = Some(nparam.input_idx); - nparam.modamt = v; + input_idx = Some(nparam.input_idx); + + if let Some(modamt) = &mut nparam.modamt { + mod_idx = Some(modamt.0); + modamt.1 = v.unwrap_or(0.0); + } } // Check if the modulation amount was already set, if not, we need @@ -364,12 +373,17 @@ impl NodeConfigurator { let modamt = v.unwrap(); self.param_modamt.insert(param, v); + // TODO: Check if the ModamtUpdate message really needs + // the input_idx once the NodeExecutor backend + // code for that message has been implemented! if let Some(input_idx) = input_idx { - let _ = - self.shared.quick_update_prod.push( - QuickMessage::ModamtUpdate { - input_idx, modamt - }); + if let Some(mod_idx) = mod_idx { + let _ = + self.shared.quick_update_prod.push( + QuickMessage::ModamtUpdate { + input_idx, mod_idx, modamt + }); + } } false @@ -801,18 +815,22 @@ impl NodeConfigurator { let at_idx = at_len; at_len += node_info.at_count(); + // - hold the mod start index of this node. + let mod_idx = mod_len; + if id == NodeId::Nop { break; } + let mut ni = NodeInstance::new(id); + ni.set_index(i) + .set_output(out_idx, out_len) + .set_input(in_idx, in_len) + .set_atom(at_idx, at_len); + // - save offset and length of each node's // allocation in the output vector. - *node_instance = Some( - NodeInstance::new(id) - .set_index(i) - .set_output(out_idx, out_len) - .set_input(in_idx, in_len) - .set_atom(at_idx, at_len)); + *node_instance = Some(ni); println!("INSERT[{}]: {:?} outidx: {},{} inidx: {},{} atidx: {},{}", i, id, out_idx, out_len, in_idx, in_len, at_idx, at_len); @@ -820,7 +838,9 @@ impl NodeConfigurator { // Create new parameters and initialize them if they did not // already exist previously for param_idx in in_idx..in_len { - if let Some(param_id) = id.inp_param_by_idx(param_idx - in_idx) { + let input_idx = param_idx - in_idx; + + if let Some(param_id) = id.inp_param_by_idx(input_idx) { let value = if let Some(value) = self.param_values.get(¶m_id) { *value @@ -828,15 +848,39 @@ impl NodeConfigurator { param_id.norm_def() }; + // If we have a modulation, store the absolute + // index of it in the [NodeProg::modops] vector later: + let ma = self.param_modamt.get(¶m_id).copied().flatten(); + let modamt = + if ma.is_some() { + let mod_idx = mod_len; + node_instance + .as_mut() + .unwrap() + .set_mod_idx(input_idx, mod_idx); + mod_len += 1; + Some((mod_idx, ma.unwrap())) + } else { + None + }; + self.param_values.insert(param_id, value); self.params.insert(param_id, NodeInputParam { param_id, value, input_idx: param_idx, + modamt, }); } } + // After iterating through the parameters we can + // store the range of the indices of this node. + node_instance + .as_mut() + .unwrap() + .set_mod(mod_idx, mod_len); + // Create new atom data and initialize it if it did not // already exist from a previous matrix instance. for atom_idx in at_idx..at_len { @@ -910,10 +954,17 @@ impl NodeConfigurator { let op = node_instance.as_op(); let input_index = node_instance.in_local2global(node_input.1); + let mod_index = node_instance.mod_in_local2global(node_input.1); if let (Some(input_index), Some(output_index)) = (input_index, output_index) { - prog.append_edge(op, input_index, output_index); + // TODO: Fetch output signal range from adjacent_output! + + prog.append_edge( + op, + input_index, + output_index, + mod_index.map(|amt| (amt, ModOpSigRange::Unipolar))); } } } @@ -960,6 +1011,10 @@ impl NodeConfigurator { // reset the inp[] input value vector. for (_param_id, param) in self.params.iter() { prog.params_mut()[param.input_idx] = param.value; + + if let Some((mod_idx, amt)) = param.modamt { + prog.modops_mut()[mod_idx].set_amt(amt); + } } // The atoms are referred to directly on process() call. diff --git a/src/nodes/node_prog.rs b/src/nodes/node_prog.rs index 4f69ddb..5f1c3a7 100644 --- a/src/nodes/node_prog.rs +++ b/src/nodes/node_prog.rs @@ -11,6 +11,15 @@ pub enum ModOpSigRange { Bipolar, } +impl std::fmt::Display for ModOpSigRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ModOpSigRange::Unipolar => write!(f, "SigRange::Unipolar"), + ModOpSigRange::Bipolar => write!(f, "SigRange::Bipolar"), + } + } +} + #[derive(Debug, Clone)] pub struct ModOp { amount: f32, @@ -37,6 +46,14 @@ impl ModOp { } } + pub fn set_amt(&mut self, amt: f32) { + self.amount = amt; + } + + pub fn set_range(&mut self, range: ModOpSigRange) { + self.range = range; + } + pub fn lock(&mut self, inbuf: ProcBuf, outbuf: ProcBuf) { self.inbuf = inbuf; self.outbuf = outbuf; @@ -49,7 +66,7 @@ impl ModOp { #[inline] pub fn process(&mut self, nframes: usize) { - let modbuf = &mut self.inbuf; + let modbuf = &mut self.modbuf; let inbuf = &mut self.inbuf; let outbuf = &mut self.outbuf; @@ -89,23 +106,31 @@ pub struct NodeOp { pub at_idxlen: (usize, usize), /// ModOp index and length of the node: pub mod_idxlen: (usize, usize), - /// Input indices, (, ) - pub inputs: Vec<(usize, usize)>, + /// Input indices, + /// (, , + /// (, , )) + pub inputs: Vec<(usize, usize, Option<(usize, f32, ModOpSigRange)>)>, } impl std::fmt::Display for NodeOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Op(i={} out=({}-{}) in=({}-{}) at=({}-{})", + write!(f, "Op(i={} out=({}-{}) in=({}-{}) at=({}-{}) mod=({}-{})", self.idx, self.out_idxlen.0, self.out_idxlen.1, self.in_idxlen.0, self.in_idxlen.1, self.at_idxlen.0, - self.at_idxlen.1)?; + self.at_idxlen.1, + self.mod_idxlen.0, + self.mod_idxlen.1)?; for i in self.inputs.iter() { write!(f, " cpy=(o{} => i{})", i.0, i.1)?; + + if let Some((idx, amt, rng)) = i.2 { + write!(f, " mod=({:6.4}/{} @{})", amt, rng, idx)?; + } } write!(f, ")") @@ -249,25 +274,31 @@ impl NodeProg { self.prog.push(node_op); } - // TODO: MOD AMOUNT CHANGE: pub fn append_edge( &mut self, node_op: NodeOp, inp_index: usize, - out_index: usize) // mod_amt: Option<(f32, sigtype)> + out_index: usize, + mod_amt: Option<(usize, ModOpSigRange)>) { - // If we have a modulation: - // self.mod_bufs.push(ProcBuf::new()) - // modbuf_idx = self.mod_bufs.len(); for n_op in self.prog.iter_mut() { if n_op.idx == node_op.idx { - n_op.inputs.push((out_index, inp_index - /* Option<(f32, sigtype, modbuf_idx>*/)); + if let Some((idx, range)) = mod_amt { + self.modops[idx].set_range(range); + } + + n_op.inputs.push( + (out_index, + inp_index, + mod_amt.map(|(idx, sigrng)| (idx, 0.0, sigrng)))); return; } } } + /// This is called right after the [crate::nodes::NodeExecutor] + /// received this NodeProg from the [crate::nodes::NodeConfigurator]. + /// It initializes internal buffers with parameter data. pub fn initialize_input_buffers(&mut self) { for param_idx in 0..self.params.len() { let param_val = self.params[param_idx]; @@ -331,14 +362,15 @@ impl NodeProg { input_bufs[inp.0..inp.1] .copy_from_slice(&self.inp[inp.0..inp.1]); - // TODO: self.mods.clear() (ProcBufs are in modbufs) - // Second step (assign outputs): for io in op.inputs.iter() { - // TODO: MOD AMOUNT: take from mod_bufs assign to input_bufs, - // add entry to self.mods???? we need indices...?! - // Create mods entries which contain all the info for node_exec. input_bufs[io.1] = out_bufs[io.0]; + + if let Some((idx, _, _)) = io.2 { + self.modops[idx].lock( + self.inp[io.1], + out_bufs[io.0]); + } } }