started implementing feedback delays
This commit is contained in:
parent
2350db8e60
commit
297360e398
3 changed files with 102 additions and 6 deletions
|
@ -20,6 +20,7 @@ mod satom;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
|
||||||
use crate::nodes::NodeAudioContext;
|
use crate::nodes::NodeAudioContext;
|
||||||
|
use crate::nodes::NodeExecContext;
|
||||||
|
|
||||||
use crate::util::AtomicFloat;
|
use crate::util::AtomicFloat;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -1139,8 +1140,10 @@ impl Node {
|
||||||
/// display some kind of position indicator.
|
/// display some kind of position indicator.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn process<T: NodeAudioContext>(
|
pub fn process<T: NodeAudioContext>(
|
||||||
&mut self, ctx: &mut T, atoms: &[SAtom], params: &[ProcBuf],
|
&mut self, ctx: &mut T, ectx: &mut NodeExecContext,
|
||||||
inputs: &[ProcBuf], outputs: &mut [ProcBuf], led: LedPhaseVals)
|
atoms: &[SAtom], params: &[ProcBuf],
|
||||||
|
inputs: &[ProcBuf], outputs: &mut [ProcBuf],
|
||||||
|
led: LedPhaseVals)
|
||||||
{
|
{
|
||||||
macro_rules! make_node_process {
|
macro_rules! make_node_process {
|
||||||
($s1: ident => $v1: ident,
|
($s1: ident => $v1: ident,
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
// This is a part of HexoDSP. Released under (A)GPLv3 or any later.
|
// This is a part of HexoDSP. Released under (A)GPLv3 or any later.
|
||||||
// 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_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 FB_DELAY_TIME_US : usize = 1500; // 1.5ms
|
||||||
|
// This means, until 384000 sample rate the times are accurate.
|
||||||
|
pub const MAX_FB_DELAY_SRATE : usize = 48000 * 8;
|
||||||
|
pub const MAX_FB_DELAY_SIZE : usize =
|
||||||
|
(MAX_FB_DELAY_SRATE * FB_DELAY_TIME_US) / 1000000;
|
||||||
|
|
||||||
mod node_prog;
|
mod node_prog;
|
||||||
mod node_exec;
|
mod node_exec;
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
GraphMessage, QuickMessage, DropMsg, NodeProg,
|
GraphMessage, QuickMessage, DropMsg, NodeProg,
|
||||||
UNUSED_MONITOR_IDX, MAX_ALLOCATED_NODES, MAX_SMOOTHERS
|
UNUSED_MONITOR_IDX, MAX_ALLOCATED_NODES, MAX_SMOOTHERS,
|
||||||
|
MAX_FB_DELAY_SIZE, FB_DELAY_TIME_US,
|
||||||
};
|
};
|
||||||
use crate::dsp::{NodeId, Node};
|
use crate::dsp::{NodeId, Node};
|
||||||
use crate::util::{Smoother, AtomicFloat};
|
use crate::util::{Smoother, AtomicFloat};
|
||||||
|
@ -48,6 +49,9 @@ pub struct NodeExecutor {
|
||||||
/// The sample rate
|
/// The sample rate
|
||||||
pub(crate) sample_rate: f32,
|
pub(crate) sample_rate: f32,
|
||||||
|
|
||||||
|
/// Context that can be accessed by all (executed) nodes at runtime.
|
||||||
|
pub(crate) exec_ctx: NodeExecContext,
|
||||||
|
|
||||||
/// The connection with the [crate::nodes::NodeConfigurator].
|
/// The connection with the [crate::nodes::NodeConfigurator].
|
||||||
shared: SharedNodeExec,
|
shared: SharedNodeExec,
|
||||||
}
|
}
|
||||||
|
@ -69,12 +73,90 @@ pub(crate) struct SharedNodeExec {
|
||||||
pub(crate) monitor_backend: MonitorBackend,
|
pub(crate) monitor_backend: MonitorBackend,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains audio driver context informations. Such as the number
|
||||||
|
/// of frames of the current buffer period and allows
|
||||||
|
/// writing output samples and reading input samples.
|
||||||
pub trait NodeAudioContext {
|
pub trait NodeAudioContext {
|
||||||
fn nframes(&self) -> usize;
|
fn nframes(&self) -> usize;
|
||||||
fn output(&mut self, channel: usize, frame: usize, v: f32);
|
fn output(&mut self, channel: usize, frame: usize, v: f32);
|
||||||
fn input(&mut self, channel: usize, frame: usize) -> f32;
|
fn input(&mut self, channel: usize, frame: usize) -> f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements a trivial delay buffer for the feedback nodes
|
||||||
|
/// FbWr and FbRd.
|
||||||
|
pub struct FeedbackDelay {
|
||||||
|
buffer: [f32; MAX_FB_DELAY_SIZE],
|
||||||
|
write_ptr: usize,
|
||||||
|
read_ptr: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FeedbackDelay {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: [0.0; MAX_FB_DELAY_SIZE],
|
||||||
|
write_ptr: 0,
|
||||||
|
read_ptr: (64 + MAX_FB_DELAY_SIZE) % MAX_FB_DELAY_SIZE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.buffer = [0.0; MAX_FB_DELAY_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sample_rate(&mut self, sr: f32) {
|
||||||
|
self.buffer = [0.0; MAX_FB_DELAY_SIZE];
|
||||||
|
self.write_ptr = 0;
|
||||||
|
// The delay sample count maximum is defined by MAX_FB_DELAY_SRATE,
|
||||||
|
// after that the feedback delays become shorter than they should be
|
||||||
|
// and things won't sound the same at sample rate
|
||||||
|
// exceeding MAX_FB_DELAY_SRATE.
|
||||||
|
//
|
||||||
|
// This is a tradeoff of wasted memory and not having to reallocate
|
||||||
|
// these delays for sample rate changes and providing delay buffers
|
||||||
|
// for all 256 theoretical feedback delay nodes.
|
||||||
|
//
|
||||||
|
// For more elaborate and longer delays an extra delay node should
|
||||||
|
// be used before FbWr or after FbRd.
|
||||||
|
let delay_sample_count = (sr as usize * FB_DELAY_TIME_US) / 1000000;
|
||||||
|
self.read_ptr = (delay_sample_count + FB_DELAY_TIME_US)
|
||||||
|
% FB_DELAY_TIME_US;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn write(&mut self, s: f32) {
|
||||||
|
self.write_ptr = (self.write_ptr + 1) % MAX_FB_DELAY_SIZE;
|
||||||
|
self.buffer[self.write_ptr] = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&mut self) -> f32 {
|
||||||
|
self.read_ptr = (self.read_ptr + 1) % MAX_FB_DELAY_SIZE;
|
||||||
|
self.buffer[self.read_ptr]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains global state that all nodes can access.
|
||||||
|
/// This is used for instance to implement the feedbackd delay nodes.
|
||||||
|
pub struct NodeExecContext {
|
||||||
|
pub feedback_delay_buffers: Vec<FeedbackDelay>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeExecContext {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut fbdb = vec![];
|
||||||
|
fbdb.resize_with(MAX_ALLOCATED_NODES, || FeedbackDelay::new());
|
||||||
|
Self {
|
||||||
|
feedback_delay_buffers: fbdb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
for b in self.feedback_delay_buffers.iter_mut() {
|
||||||
|
b.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NodeExecutor {
|
impl NodeExecutor {
|
||||||
pub(crate) fn new(shared: SharedNodeExec) -> Self {
|
pub(crate) fn new(shared: SharedNodeExec) -> Self {
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
|
@ -92,6 +174,7 @@ impl NodeExecutor {
|
||||||
sample_rate: 44100.0,
|
sample_rate: 44100.0,
|
||||||
prog: NodeProg::empty(),
|
prog: NodeProg::empty(),
|
||||||
monitor_signal_cur_inp_indices: [UNUSED_MONITOR_IDX; MON_SIG_CNT],
|
monitor_signal_cur_inp_indices: [UNUSED_MONITOR_IDX; MON_SIG_CNT],
|
||||||
|
exec_ctx: NodeExecContext::new(),
|
||||||
shared,
|
shared,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +203,8 @@ impl NodeExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.exec_ctx.clear();
|
||||||
|
|
||||||
self.monitor_signal_cur_inp_indices =
|
self.monitor_signal_cur_inp_indices =
|
||||||
[UNUSED_MONITOR_IDX; MON_SIG_CNT];
|
[UNUSED_MONITOR_IDX; MON_SIG_CNT];
|
||||||
|
|
||||||
|
@ -312,6 +397,7 @@ impl NodeExecutor {
|
||||||
let nodes = &mut self.nodes;
|
let nodes = &mut self.nodes;
|
||||||
let ctx_vals = &mut self.shared.node_ctx_values;
|
let ctx_vals = &mut self.shared.node_ctx_values;
|
||||||
let prog = &mut self.prog;
|
let prog = &mut self.prog;
|
||||||
|
let exec_ctx = &mut self.exec_ctx;
|
||||||
|
|
||||||
let prog_out_fb = prog.out_feedback.input_buffer();
|
let prog_out_fb = prog.out_feedback.input_buffer();
|
||||||
|
|
||||||
|
@ -325,6 +411,7 @@ impl NodeExecutor {
|
||||||
nodes[op.idx as usize]
|
nodes[op.idx as usize]
|
||||||
.process(
|
.process(
|
||||||
ctx,
|
ctx,
|
||||||
|
exec_ctx,
|
||||||
&prog.atoms[at.0..at.1],
|
&prog.atoms[at.0..at.1],
|
||||||
&prog.inp[inp.0..inp.1],
|
&prog.inp[inp.0..inp.1],
|
||||||
&prog.cur_inp[inp.0..inp.1],
|
&prog.cur_inp[inp.0..inp.1],
|
||||||
|
|
Loading…
Reference in a new issue