WIP: mod amounts in NodeProg
This commit is contained in:
parent
196d094673
commit
b932b55a8d
3 changed files with 150 additions and 29 deletions
|
@ -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
|
||||
|
|
|
@ -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(¶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].
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue