WIP: mod amounts in NodeProg

This commit is contained in:
Weird Constructor 2021-07-06 19:55:03 +02:00
parent 196d094673
commit b932b55a8d
3 changed files with 150 additions and 29 deletions

View file

@ -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

View file

@ -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<usize>; 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<usize> {
if idx > self.in2mod_map.len() {
return None;
}
self.in2mod_map[idx as usize]
}
pub fn in_local2global(&self, idx: u8) -> Option<usize> {
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<f32>,
}
#[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<NodeInstance>)>,
pub(crate) nodes: Vec<(NodeInfo, Option<NodeInstance>)>,
/// 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<NodeId, usize>,
pub(crate) node2idx: HashMap<NodeId, usize>,
/// Holding the tracker sequencers
pub(crate) trackers: Vec<Tracker>,
pub(crate) trackers: Vec<Tracker>,
/// 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<String>,
errors: Vec<String>,
/// Contains (automateable) parameters
params: std::collections::HashMap<ParamId, NodeInputParam>,
@ -311,6 +344,13 @@ impl NodeConfigurator {
return false;
}
let mut input_idx = None;
if let Some(nparam) = self.params.get_mut(&param) {
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(&param) {
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].

View file

@ -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, (<out vec index>, <own node input index>)
pub inputs: Vec<(usize, usize)>,
}
@ -68,6 +140,9 @@ pub struct NodeProg {
/// vector.
pub prog: Vec<NodeOp>,
/// The modulators for the input parameters.
pub modops: Vec<ModOp>,
/// 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<Vec<(f32, sigtype,
// ProcBuf,
// ProcBuf,
// ProcBuf)>>)
}
// 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;
}