From b932b55a8d77ae7148d97377b7e9fc9e30f2f42c Mon Sep 17 00:00:00 2001 From: Weird Constructor Date: Tue, 6 Jul 2021 19:55:03 +0200 Subject: [PATCH] WIP: mod amounts in NodeProg --- src/nodes/mod.rs | 1 + src/nodes/node_conf.rs | 85 +++++++++++++++++++++++++++----------- src/nodes/node_prog.rs | 93 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 150 insertions(+), 29 deletions(-) diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 54cf001..52fd9f2 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -3,6 +3,7 @@ // See README.md and COPYING for details. pub const MAX_ALLOCATED_NODES : usize = 256; +pub const MAX_INPUTS : usize = 32; pub const MAX_SMOOTHERS : usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs pub const MAX_AVAIL_TRACKERS : usize = 128; pub const MAX_FB_DELAYS : usize = 256; // 256 feedback delays, thats roughly 1.2MB RAM diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index 7d77a43..dd3b457 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -7,6 +7,7 @@ use super::{ NodeProg, NodeOp, FeedbackFilter, MAX_ALLOCATED_NODES, + MAX_INPUTS, MAX_AVAIL_TRACKERS, UNUSED_MONITOR_IDX }; @@ -40,20 +41,32 @@ pub struct NodeInstance { in_end: usize, at_start: usize, at_end: usize, + mod_start: usize, + mod_end: usize, + /// A mapping array, to map from input index of the node + /// to the modulator index. Because not every input has an + /// associated modulator. + /// This is used later to send [QuickMessage::ModamtUpdate]. + /// The input index into this array is the index returned from + /// routines like [NodeId::inp_param]. + in2mod_map: [Option; MAX_INPUTS], } 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, + 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], } } @@ -66,10 +79,18 @@ impl NodeInstance { 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![], } } + pub fn mod_in_local2global(&self, idx: u8) -> Option { + if idx > self.in2mod_map.len() { + return None; + } + self.in2mod_map[idx as usize] + } + pub fn in_local2global(&self, idx: u8) -> Option { let idx = self.in_start + idx as usize; if idx < self.in_end { Some(idx) } @@ -82,24 +103,35 @@ impl NodeInstance { else { None } } - pub fn set_index(mut self, idx: usize) -> Self { + pub fn set_index(&mut self, idx: usize) -> &mut Self { self.prog_idx = idx; self } - pub fn set_output(mut self, s: usize, e: usize) -> Self { + pub fn set_output(&mut self, s: usize, e: usize) -> &mut Self { self.out_start = s; self.out_end = e; self } - pub fn set_input(mut self, s: usize, e: usize) -> Self { + pub fn set_input(&mut self, s: usize, e: usize) -> &mut Self { self.in_start = s; self.in_end = e; self } - pub fn set_atom(mut self, s: usize, e: usize) -> Self { + pub fn set_mod(&mut self, s: usize, e: usize) -> &mut Self { + self.mod_start = s; + self.mod_end = e; + self + } + + pub fn set_mod_idx(&mut self, idx: u8, i: usize) -> &mut Self { + self.in2mod_map[idx as usize] = Some(i); + self + } + + pub fn set_atom(&mut self, s: usize, e: usize) -> &mut Self { self.at_start = s; self.at_end = e; self @@ -111,6 +143,7 @@ struct NodeInputParam { param_id: ParamId, input_idx: usize, value: f32, + modamt: Option, } #[derive(Debug, Clone)] @@ -128,27 +161,27 @@ struct NodeInputAtom { /// as facade for the executed node graph in the backend. pub struct NodeConfigurator { /// Holds all the nodes, their parameters and type. - pub(crate) nodes: Vec<(NodeInfo, Option)>, + pub(crate) nodes: Vec<(NodeInfo, Option)>, /// An index of all nodes ever instanciated. /// Be aware, that currently there is no cleanup implemented. /// That means, any instanciated NodeId will persist throughout /// the whole runtime. A garbage collector might be implemented /// when saving presets. - pub(crate) node2idx: HashMap, + pub(crate) node2idx: HashMap, /// Holding the tracker sequencers - pub(crate) trackers: Vec, + pub(crate) trackers: Vec, /// The shared parts of the [NodeConfigurator] /// and the [crate::nodes::NodeExecutor]. - pub(crate) shared: SharedNodeConf, + pub(crate) shared: SharedNodeConf, - feedback_filter: FeedbackFilter, + feedback_filter: FeedbackFilter, /// Loads and Caches audio samples that are set as parameters /// for nodes. - sample_lib: SampleLibrary, + sample_lib: SampleLibrary, /// Error messages: - errors: Vec, + errors: Vec, /// Contains (automateable) parameters params: std::collections::HashMap, @@ -311,6 +344,13 @@ impl NodeConfigurator { return false; } + let mut input_idx = None; + + if let Some(nparam) = self.params.get_mut(¶m) { + input_idx = Some(nparam.input_idx); + nparam.modamt = v; + } + // Check if the modulation amount was already set, if not, we need // to reconstruct the graph and upload an updated NodeProg. if let Some(_old_modamt) = @@ -322,11 +362,9 @@ impl NodeConfigurator { } else { let modamt = v.unwrap(); - self.param_modamt.insert(param, v); - if let Some(nparam) = self.params.get_mut(¶m) { - let input_idx = nparam.input_idx; + if let Some(input_idx) = input_idx { let _ = self.shared.quick_update_prod.push( QuickMessage::ModamtUpdate { @@ -744,6 +782,7 @@ impl NodeConfigurator { let mut out_len = 0; let mut in_len = 0; let mut at_len = 0; + let mut mod_len = 0; for (i, (node_info, node_instance)) in self.nodes.iter_mut().enumerate() @@ -823,7 +862,7 @@ impl NodeConfigurator { } } - NodeProg::new(out_len, in_len, at_len) + NodeProg::new(out_len, in_len, at_len, mod_len) } /// Creates a new [NodeOp] and add it to the [NodeProg]. diff --git a/src/nodes/node_prog.rs b/src/nodes/node_prog.rs index c26ce7e..4f69ddb 100644 --- a/src/nodes/node_prog.rs +++ b/src/nodes/node_prog.rs @@ -5,6 +5,76 @@ use crate::dsp::{ProcBuf, SAtom}; use triple_buffer::{Input, Output, TripleBuffer}; +#[derive(Debug, Clone, Copy)] +pub enum ModOpSigRange { + Unipolar, + Bipolar, +} + +#[derive(Debug, Clone)] +pub struct ModOp { + amount: f32, + range: ModOpSigRange, + modbuf: ProcBuf, + outbuf: ProcBuf, + inbuf: ProcBuf, +} + +impl Drop for ModOp { + fn drop(&mut self) { + self.modbuf.free(); + } +} + +impl ModOp { + pub fn new() -> Self { + Self { + range: ModOpSigRange::Unipolar, + amount: 0.0, + modbuf: ProcBuf::new(), + outbuf: ProcBuf::null(), + inbuf: ProcBuf::null(), + } + } + + pub fn lock(&mut self, inbuf: ProcBuf, outbuf: ProcBuf) { + self.inbuf = inbuf; + self.outbuf = outbuf; + } + + pub fn unlock(&mut self) { + self.outbuf = ProcBuf::null(); + self.inbuf = ProcBuf::null(); + } + + #[inline] + pub fn process(&mut self, nframes: usize) { + let modbuf = &mut self.inbuf; + let inbuf = &mut self.inbuf; + let outbuf = &mut self.outbuf; + + match self.range { + ModOpSigRange::Bipolar => { + for frame in 0..nframes { + modbuf.write(frame, + modbuf.read(frame) + * ((outbuf.read(frame) + 1.0) * 0.5) + .clamp(0.0, 1.0) + + inbuf.read(frame)); + } + }, + ModOpSigRange::Unipolar => { + for frame in 0..nframes { + modbuf.write(frame, + modbuf.read(frame) + * outbuf.read(frame).clamp(0.0, 1.0) + + inbuf.read(frame)); + } + }, + } + } +} + /// Step in a `NodeProg` that stores the to be /// executed node and output operations. #[derive(Debug, Clone)] @@ -17,6 +87,8 @@ pub struct NodeOp { pub in_idxlen: (usize, usize), /// Atom data index and length of the node: pub at_idxlen: (usize, usize), + /// ModOp index and length of the node: + pub mod_idxlen: (usize, usize), /// Input indices, (, ) pub inputs: Vec<(usize, usize)>, } @@ -68,6 +140,9 @@ pub struct NodeProg { /// vector. pub prog: Vec, + /// The modulators for the input parameters. + pub modops: Vec, + /// A marker, that checks if we can still swap buffers with /// with other NodeProg instances. This is usally set if the ProcBuf pointers /// have been copied into `cur_inp`. You can call `unlock_buffers` to @@ -107,13 +182,14 @@ impl NodeProg { params: vec![], atoms: vec![], prog: vec![], + modops: vec![], out_feedback: input_fb, out_fb_cons: Some(output_fb), locked_buffers: false, } } - pub fn new(out_len: usize, inp_len: usize, at_len: usize) -> Self { + pub fn new(out_len: usize, inp_len: usize, at_len: usize, mod_len: usize) -> Self { let mut out = vec![]; out.resize_with(out_len, ProcBuf::new); @@ -130,6 +206,8 @@ impl NodeProg { params.resize(inp_len, 0.0); let mut atoms = vec![]; atoms.resize(at_len, SAtom::setting(0)); + let mut modops = vec![]; + modops.resize_with(mod_len, ModOp::new); Self { out, @@ -137,6 +215,7 @@ impl NodeProg { cur_inp, params, atoms, + modops, prog: vec![], out_feedback: input_fb, out_fb_cons: Some(output_fb), @@ -156,6 +235,10 @@ impl NodeProg { &mut self.atoms } + pub fn modops_mut(&mut self) -> &mut [ModOp] { + &mut self.modops + } + pub fn append_op(&mut self, node_op: NodeOp) { for n_op in self.prog.iter_mut() { if n_op.idx == node_op.idx { @@ -164,11 +247,6 @@ impl NodeProg { } self.prog.push(node_op); - // TODO: MOD AMOUNT: - // self.mods.push(Option>) } // TODO: MOD AMOUNT CHANGE: @@ -220,6 +298,9 @@ impl NodeProg { for buf in self.cur_inp.iter_mut() { *buf = ProcBuf::null(); } + for modop in self.modops.iter_mut() { + modop.unlock(); + } self.locked_buffers = false; }