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.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
pub const MAX_ALLOCATED_NODES : usize = 256;
|
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_SMOOTHERS : usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs
|
||||||
pub const MAX_AVAIL_TRACKERS : usize = 128;
|
pub const MAX_AVAIL_TRACKERS : usize = 128;
|
||||||
pub const MAX_FB_DELAYS : usize = 256; // 256 feedback delays, thats roughly 1.2MB RAM
|
pub const MAX_FB_DELAYS : usize = 256; // 256 feedback delays, thats roughly 1.2MB RAM
|
||||||
|
|
|
@ -7,6 +7,7 @@ use super::{
|
||||||
NodeProg, NodeOp,
|
NodeProg, NodeOp,
|
||||||
FeedbackFilter,
|
FeedbackFilter,
|
||||||
MAX_ALLOCATED_NODES,
|
MAX_ALLOCATED_NODES,
|
||||||
|
MAX_INPUTS,
|
||||||
MAX_AVAIL_TRACKERS,
|
MAX_AVAIL_TRACKERS,
|
||||||
UNUSED_MONITOR_IDX
|
UNUSED_MONITOR_IDX
|
||||||
};
|
};
|
||||||
|
@ -40,20 +41,32 @@ pub struct NodeInstance {
|
||||||
in_end: usize,
|
in_end: usize,
|
||||||
at_start: usize,
|
at_start: usize,
|
||||||
at_end: 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 {
|
impl NodeInstance {
|
||||||
pub fn new(id: NodeId) -> Self {
|
pub fn new(id: NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
in_use: false,
|
in_use: false,
|
||||||
prog_idx: 0,
|
prog_idx: 0,
|
||||||
out_start: 0,
|
out_start: 0,
|
||||||
out_end: 0,
|
out_end: 0,
|
||||||
in_start: 0,
|
in_start: 0,
|
||||||
in_end: 0,
|
in_end: 0,
|
||||||
at_start: 0,
|
at_start: 0,
|
||||||
at_end: 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),
|
out_idxlen: (self.out_start, self.out_end),
|
||||||
in_idxlen: (self.in_start, self.in_end),
|
in_idxlen: (self.in_start, self.in_end),
|
||||||
at_idxlen: (self.at_start, self.at_end),
|
at_idxlen: (self.at_start, self.at_end),
|
||||||
|
mod_idxlen: (self.mod_start, self.mod_end),
|
||||||
inputs: vec![],
|
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> {
|
pub fn in_local2global(&self, idx: u8) -> Option<usize> {
|
||||||
let idx = self.in_start + idx as usize;
|
let idx = self.in_start + idx as usize;
|
||||||
if idx < self.in_end { Some(idx) }
|
if idx < self.in_end { Some(idx) }
|
||||||
|
@ -82,24 +103,35 @@ impl NodeInstance {
|
||||||
else { None }
|
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.prog_idx = idx;
|
||||||
self
|
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_start = s;
|
||||||
self.out_end = e;
|
self.out_end = e;
|
||||||
self
|
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_start = s;
|
||||||
self.in_end = e;
|
self.in_end = e;
|
||||||
self
|
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_start = s;
|
||||||
self.at_end = e;
|
self.at_end = e;
|
||||||
self
|
self
|
||||||
|
@ -111,6 +143,7 @@ struct NodeInputParam {
|
||||||
param_id: ParamId,
|
param_id: ParamId,
|
||||||
input_idx: usize,
|
input_idx: usize,
|
||||||
value: f32,
|
value: f32,
|
||||||
|
modamt: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -128,27 +161,27 @@ struct NodeInputAtom {
|
||||||
/// as facade for the executed node graph in the backend.
|
/// as facade for the executed node graph in the backend.
|
||||||
pub struct NodeConfigurator {
|
pub struct NodeConfigurator {
|
||||||
/// Holds all the nodes, their parameters and type.
|
/// 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.
|
/// An index of all nodes ever instanciated.
|
||||||
/// Be aware, that currently there is no cleanup implemented.
|
/// Be aware, that currently there is no cleanup implemented.
|
||||||
/// That means, any instanciated NodeId will persist throughout
|
/// That means, any instanciated NodeId will persist throughout
|
||||||
/// the whole runtime. A garbage collector might be implemented
|
/// the whole runtime. A garbage collector might be implemented
|
||||||
/// when saving presets.
|
/// when saving presets.
|
||||||
pub(crate) node2idx: HashMap<NodeId, usize>,
|
pub(crate) node2idx: HashMap<NodeId, usize>,
|
||||||
/// Holding the tracker sequencers
|
/// Holding the tracker sequencers
|
||||||
pub(crate) trackers: Vec<Tracker>,
|
pub(crate) trackers: Vec<Tracker>,
|
||||||
/// The shared parts of the [NodeConfigurator]
|
/// The shared parts of the [NodeConfigurator]
|
||||||
/// and the [crate::nodes::NodeExecutor].
|
/// 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
|
/// Loads and Caches audio samples that are set as parameters
|
||||||
/// for nodes.
|
/// for nodes.
|
||||||
sample_lib: SampleLibrary,
|
sample_lib: SampleLibrary,
|
||||||
|
|
||||||
/// Error messages:
|
/// Error messages:
|
||||||
errors: Vec<String>,
|
errors: Vec<String>,
|
||||||
|
|
||||||
/// Contains (automateable) parameters
|
/// Contains (automateable) parameters
|
||||||
params: std::collections::HashMap<ParamId, NodeInputParam>,
|
params: std::collections::HashMap<ParamId, NodeInputParam>,
|
||||||
|
@ -311,6 +344,13 @@ impl NodeConfigurator {
|
||||||
return false;
|
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
|
// Check if the modulation amount was already set, if not, we need
|
||||||
// to reconstruct the graph and upload an updated NodeProg.
|
// to reconstruct the graph and upload an updated NodeProg.
|
||||||
if let Some(_old_modamt) =
|
if let Some(_old_modamt) =
|
||||||
|
@ -322,11 +362,9 @@ impl NodeConfigurator {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
let modamt = v.unwrap();
|
let modamt = v.unwrap();
|
||||||
|
|
||||||
self.param_modamt.insert(param, v);
|
self.param_modamt.insert(param, v);
|
||||||
|
|
||||||
if let Some(nparam) = self.params.get_mut(¶m) {
|
if let Some(input_idx) = input_idx {
|
||||||
let input_idx = nparam.input_idx;
|
|
||||||
let _ =
|
let _ =
|
||||||
self.shared.quick_update_prod.push(
|
self.shared.quick_update_prod.push(
|
||||||
QuickMessage::ModamtUpdate {
|
QuickMessage::ModamtUpdate {
|
||||||
|
@ -744,6 +782,7 @@ impl NodeConfigurator {
|
||||||
let mut out_len = 0;
|
let mut out_len = 0;
|
||||||
let mut in_len = 0;
|
let mut in_len = 0;
|
||||||
let mut at_len = 0;
|
let mut at_len = 0;
|
||||||
|
let mut mod_len = 0;
|
||||||
|
|
||||||
for (i, (node_info, node_instance))
|
for (i, (node_info, node_instance))
|
||||||
in self.nodes.iter_mut().enumerate()
|
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].
|
/// Creates a new [NodeOp] and add it to the [NodeProg].
|
||||||
|
|
|
@ -5,6 +5,76 @@
|
||||||
use crate::dsp::{ProcBuf, SAtom};
|
use crate::dsp::{ProcBuf, SAtom};
|
||||||
use triple_buffer::{Input, Output, TripleBuffer};
|
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
|
/// Step in a `NodeProg` that stores the to be
|
||||||
/// executed node and output operations.
|
/// executed node and output operations.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -17,6 +87,8 @@ pub struct NodeOp {
|
||||||
pub in_idxlen: (usize, usize),
|
pub in_idxlen: (usize, usize),
|
||||||
/// Atom data index and length of the node:
|
/// Atom data index and length of the node:
|
||||||
pub at_idxlen: (usize, usize),
|
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>)
|
/// Input indices, (<out vec index>, <own node input index>)
|
||||||
pub inputs: Vec<(usize, usize)>,
|
pub inputs: Vec<(usize, usize)>,
|
||||||
}
|
}
|
||||||
|
@ -68,6 +140,9 @@ pub struct NodeProg {
|
||||||
/// vector.
|
/// vector.
|
||||||
pub prog: Vec<NodeOp>,
|
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
|
/// A marker, that checks if we can still swap buffers with
|
||||||
/// with other NodeProg instances. This is usally set if the ProcBuf pointers
|
/// with other NodeProg instances. This is usally set if the ProcBuf pointers
|
||||||
/// have been copied into `cur_inp`. You can call `unlock_buffers` to
|
/// have been copied into `cur_inp`. You can call `unlock_buffers` to
|
||||||
|
@ -107,13 +182,14 @@ impl NodeProg {
|
||||||
params: vec![],
|
params: vec![],
|
||||||
atoms: vec![],
|
atoms: vec![],
|
||||||
prog: vec![],
|
prog: vec![],
|
||||||
|
modops: vec![],
|
||||||
out_feedback: input_fb,
|
out_feedback: input_fb,
|
||||||
out_fb_cons: Some(output_fb),
|
out_fb_cons: Some(output_fb),
|
||||||
locked_buffers: false,
|
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![];
|
let mut out = vec![];
|
||||||
out.resize_with(out_len, ProcBuf::new);
|
out.resize_with(out_len, ProcBuf::new);
|
||||||
|
|
||||||
|
@ -130,6 +206,8 @@ impl NodeProg {
|
||||||
params.resize(inp_len, 0.0);
|
params.resize(inp_len, 0.0);
|
||||||
let mut atoms = vec![];
|
let mut atoms = vec![];
|
||||||
atoms.resize(at_len, SAtom::setting(0));
|
atoms.resize(at_len, SAtom::setting(0));
|
||||||
|
let mut modops = vec![];
|
||||||
|
modops.resize_with(mod_len, ModOp::new);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
out,
|
out,
|
||||||
|
@ -137,6 +215,7 @@ impl NodeProg {
|
||||||
cur_inp,
|
cur_inp,
|
||||||
params,
|
params,
|
||||||
atoms,
|
atoms,
|
||||||
|
modops,
|
||||||
prog: vec![],
|
prog: vec![],
|
||||||
out_feedback: input_fb,
|
out_feedback: input_fb,
|
||||||
out_fb_cons: Some(output_fb),
|
out_fb_cons: Some(output_fb),
|
||||||
|
@ -156,6 +235,10 @@ impl NodeProg {
|
||||||
&mut self.atoms
|
&mut self.atoms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn modops_mut(&mut self) -> &mut [ModOp] {
|
||||||
|
&mut self.modops
|
||||||
|
}
|
||||||
|
|
||||||
pub fn append_op(&mut self, node_op: NodeOp) {
|
pub fn append_op(&mut self, node_op: NodeOp) {
|
||||||
for n_op in self.prog.iter_mut() {
|
for n_op in self.prog.iter_mut() {
|
||||||
if n_op.idx == node_op.idx {
|
if n_op.idx == node_op.idx {
|
||||||
|
@ -164,11 +247,6 @@ impl NodeProg {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.prog.push(node_op);
|
self.prog.push(node_op);
|
||||||
// TODO: MOD AMOUNT:
|
|
||||||
// self.mods.push(Option<Vec<(f32, sigtype,
|
|
||||||
// ProcBuf,
|
|
||||||
// ProcBuf,
|
|
||||||
// ProcBuf)>>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: MOD AMOUNT CHANGE:
|
// TODO: MOD AMOUNT CHANGE:
|
||||||
|
@ -220,6 +298,9 @@ impl NodeProg {
|
||||||
for buf in self.cur_inp.iter_mut() {
|
for buf in self.cur_inp.iter_mut() {
|
||||||
*buf = ProcBuf::null();
|
*buf = ProcBuf::null();
|
||||||
}
|
}
|
||||||
|
for modop in self.modops.iter_mut() {
|
||||||
|
modop.unlock();
|
||||||
|
}
|
||||||
self.locked_buffers = false;
|
self.locked_buffers = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue