debugging FbWr/FbRd, the buffer is not read correctly
This commit is contained in:
parent
b7284b6b8a
commit
3bf1d62239
9 changed files with 101 additions and 25 deletions
|
@ -14,6 +14,8 @@ mod node_test;
|
||||||
mod node_tseq;
|
mod node_tseq;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
mod node_sampl;
|
mod node_sampl;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
mod node_fbwr_fbrd;
|
||||||
|
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
mod satom;
|
mod satom;
|
||||||
|
@ -35,6 +37,8 @@ use node_out::Out;
|
||||||
use node_test::Test;
|
use node_test::Test;
|
||||||
use node_tseq::TSeq;
|
use node_tseq::TSeq;
|
||||||
use node_sampl::Sampl;
|
use node_sampl::Sampl;
|
||||||
|
use node_fbwr_fbrd::FbWr;
|
||||||
|
use node_fbwr_fbrd::FbRd;
|
||||||
|
|
||||||
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
||||||
|
|
||||||
|
@ -303,6 +307,11 @@ macro_rules! node_list {
|
||||||
// | | | | defa/lt_/value
|
// | | | | defa/lt_/value
|
||||||
// | | | | | | /
|
// | | | | | | /
|
||||||
{2 0 mono setting(0) 0 1},
|
{2 0 mono setting(0) 0 1},
|
||||||
|
fbwr => FbWr UIType::Generic UICategory::IOUtil
|
||||||
|
(0 inp n_id d_id -1.0, 1.0, 0.0),
|
||||||
|
fbrd => FbRd UIType::Generic UICategory::IOUtil
|
||||||
|
(0 atv n_id d_id -1.0, 1.0, 1.0)
|
||||||
|
[0 sig],
|
||||||
test => Test UIType::Generic UICategory::IOUtil
|
test => Test UIType::Generic UICategory::IOUtil
|
||||||
(0 f n_id d_id 0.0, 1.0, 0.5)
|
(0 f n_id d_id 0.0, 1.0, 0.5)
|
||||||
{1 0 s setting(0) 0 10},
|
{1 0 s setting(0) 0 10},
|
||||||
|
@ -321,7 +330,7 @@ pub mod labels {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod Out {
|
pub mod Out {
|
||||||
pub const mono : [&'static str; 2] = ["Mono", "Stereo"];
|
pub const mono : [&'static str; 2] = ["Stereo", "Mono"];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod Amp {
|
pub mod Amp {
|
||||||
|
@ -1099,7 +1108,7 @@ pub fn node_factory(node_id: NodeId) -> Option<(Node, NodeInfo)> {
|
||||||
) => {
|
) => {
|
||||||
match node_id {
|
match node_id {
|
||||||
$(NodeId::$variant(_) => Some((
|
$(NodeId::$variant(_) => Some((
|
||||||
Node::$variant { node: $variant::new() },
|
Node::$variant { node: $variant::new(&node_id) },
|
||||||
NodeInfo::from_node_id(node_id),
|
NodeInfo::from_node_id(node_id),
|
||||||
)),)+
|
)),)+
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{SAtom, ProcBuf, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals};
|
||||||
|
|
||||||
/// A simple amplifier
|
/// A simple amplifier
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -11,7 +11,7 @@ pub struct Amp {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Amp {
|
impl Amp {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{SAtom, ProcBuf, inp, at, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, inp, at, DspNode, LedPhaseVals};
|
||||||
|
|
||||||
/// The (stereo) output port of the plugin
|
/// The (stereo) output port of the plugin
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -14,7 +14,7 @@ pub struct Out {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Out {
|
impl Out {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input: [0.0; 2],
|
input: [0.0; 2],
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{SAtom, ProcBuf, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals};
|
||||||
use crate::dsp::{out, at, inp, denorm}; //, inp, denorm, denorm_v, inp_dir, at};
|
use crate::dsp::{out, at, inp, denorm}; //, inp, denorm, denorm_v, inp_dir, at};
|
||||||
use super::helpers::Trigger;
|
use super::helpers::Trigger;
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ pub struct Sampl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sampl {
|
impl Sampl {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
phase: 0.0,
|
phase: 0.0,
|
||||||
srate: 44100.0,
|
srate: 44100.0,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{SAtom, ProcBuf, denorm, out, inp, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, denorm, out, inp, DspNode, LedPhaseVals};
|
||||||
use crate::dsp::helpers::fast_sin;
|
use crate::dsp::helpers::fast_sin;
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ pub struct Sin {
|
||||||
const TWOPI : f32 = 2.0 * std::f32::consts::PI;
|
const TWOPI : f32 = 2.0 * std::f32::consts::PI;
|
||||||
|
|
||||||
impl Sin {
|
impl Sin {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
srate: 44100.0,
|
srate: 44100.0,
|
||||||
phase: 0.0,
|
phase: 0.0,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{SAtom, ProcBuf, GraphFun, GraphAtomData, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, GraphFun, GraphAtomData, DspNode, LedPhaseVals};
|
||||||
|
|
||||||
/// A simple amplifier
|
/// A simple amplifier
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -11,7 +11,7 @@ pub struct Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Test {
|
impl Test {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::helpers::TriggerClock;
|
use crate::dsp::helpers::TriggerClock;
|
||||||
use crate::dsp::{SAtom, ProcBuf, DspNode, LedPhaseVals};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals};
|
||||||
use crate::dsp::tracker::TrackerBackend;
|
use crate::dsp::tracker::TrackerBackend;
|
||||||
|
|
||||||
use crate::dsp::MAX_BLOCK_SIZE;
|
use crate::dsp::MAX_BLOCK_SIZE;
|
||||||
|
@ -18,11 +18,11 @@ pub struct TSeq {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for TSeq {
|
impl Clone for TSeq {
|
||||||
fn clone(&self) -> Self { Self::new() }
|
fn clone(&self) -> Self { Self::new(&NodeId::Nop) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TSeq {
|
impl TSeq {
|
||||||
pub fn new() -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
backend: None,
|
backend: None,
|
||||||
srate: 48000.0,
|
srate: 48000.0,
|
||||||
|
|
|
@ -82,20 +82,34 @@ pub trait NodeAudioContext {
|
||||||
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
|
/// Implements a trivial buffer for the feedback nodes
|
||||||
/// FbWr and FbRd.
|
/// FbWr and FbRd.
|
||||||
pub struct FeedbackDelay {
|
///
|
||||||
|
/// Note that the previous audio period or even the first one ever may
|
||||||
|
/// produce less than 64 samples. This means we need to keep track
|
||||||
|
/// how many samples were actually written to the feedback buffer!
|
||||||
|
/// See also `sample_count` field.
|
||||||
|
pub struct FeedbackBuffer {
|
||||||
|
/// The feedback buffer that holds the samples of the previous period.
|
||||||
buffer: [f32; MAX_FB_DELAY_SIZE],
|
buffer: [f32; MAX_FB_DELAY_SIZE],
|
||||||
|
/// The write pointer.
|
||||||
write_ptr: usize,
|
write_ptr: usize,
|
||||||
|
/// Read pointer, is always behind write_ptr by an initial amount
|
||||||
read_ptr: usize,
|
read_ptr: usize,
|
||||||
|
/// The number of samples written into the buffer (to prevent overreads)
|
||||||
|
/// We need to keep track of the number of samples actually written into
|
||||||
|
/// the delay because the first or previous periods may have produced
|
||||||
|
/// not enough samples.
|
||||||
|
sample_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeedbackDelay {
|
impl FeedbackBuffer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer: [0.0; MAX_FB_DELAY_SIZE],
|
buffer: [0.0; MAX_FB_DELAY_SIZE],
|
||||||
write_ptr: 0,
|
write_ptr: 0,
|
||||||
read_ptr: (64 + MAX_FB_DELAY_SIZE) % MAX_FB_DELAY_SIZE,
|
read_ptr: (64 + MAX_FB_DELAY_SIZE) % MAX_FB_DELAY_SIZE,
|
||||||
|
sample_count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +120,7 @@ impl FeedbackDelay {
|
||||||
pub fn set_sample_rate(&mut self, sr: f32) {
|
pub fn set_sample_rate(&mut self, sr: f32) {
|
||||||
self.buffer = [0.0; MAX_FB_DELAY_SIZE];
|
self.buffer = [0.0; MAX_FB_DELAY_SIZE];
|
||||||
self.write_ptr = 0;
|
self.write_ptr = 0;
|
||||||
|
self.sample_count = 0;
|
||||||
// The delay sample count maximum is defined by MAX_FB_DELAY_SRATE,
|
// The delay sample count maximum is defined by MAX_FB_DELAY_SRATE,
|
||||||
// after that the feedback delays become shorter than they should be
|
// after that the feedback delays become shorter than they should be
|
||||||
// and things won't sound the same at sample rate
|
// and things won't sound the same at sample rate
|
||||||
|
@ -125,26 +140,35 @@ impl FeedbackDelay {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write(&mut self, s: f32) {
|
pub fn write(&mut self, s: f32) {
|
||||||
self.write_ptr = (self.write_ptr + 1) % MAX_FB_DELAY_SIZE;
|
self.write_ptr = (self.write_ptr + 1) % MAX_FB_DELAY_SIZE;
|
||||||
|
self.sample_count += 1;
|
||||||
self.buffer[self.write_ptr] = s;
|
self.buffer[self.write_ptr] = s;
|
||||||
|
println!("WRITE[{}]={}", self.write_ptr, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read(&mut self) -> f32 {
|
pub fn read(&mut self) -> f32 {
|
||||||
self.read_ptr = (self.read_ptr + 1) % MAX_FB_DELAY_SIZE;
|
if self.sample_count > 0 {
|
||||||
self.buffer[self.read_ptr]
|
self.sample_count - 1;
|
||||||
|
self.read_ptr = (self.read_ptr + 1) % MAX_FB_DELAY_SIZE;
|
||||||
|
let s = self.buffer[self.read_ptr];
|
||||||
|
println!("READ[{}]={}", self.read_ptr, s);
|
||||||
|
s
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains global state that all nodes can access.
|
/// Contains global state that all nodes can access.
|
||||||
/// This is used for instance to implement the feedbackd delay nodes.
|
/// This is used for instance to implement the feedbackd delay nodes.
|
||||||
pub struct NodeExecContext {
|
pub struct NodeExecContext {
|
||||||
pub feedback_delay_buffers: Vec<FeedbackDelay>,
|
pub feedback_delay_buffers: Vec<FeedbackBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeExecContext {
|
impl NodeExecContext {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let mut fbdb = vec![];
|
let mut fbdb = vec![];
|
||||||
fbdb.resize_with(MAX_ALLOCATED_NODES, || FeedbackDelay::new());
|
fbdb.resize_with(MAX_ALLOCATED_NODES, || FeedbackBuffer::new());
|
||||||
Self {
|
Self {
|
||||||
feedback_delay_buffers: fbdb,
|
feedback_delay_buffers: fbdb,
|
||||||
}
|
}
|
||||||
|
|
|
@ -916,3 +916,46 @@ fn check_matrix_output_feedback() {
|
||||||
assert_float_eq!(fo_amp.1, 0.11627);
|
assert_float_eq!(fo_amp.1, 0.11627);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_matrix_node_feedback() {
|
||||||
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
let mut matrix = Matrix::new(node_conf, 7, 7);
|
||||||
|
|
||||||
|
let sin = NodeId::Sin(0);
|
||||||
|
let sin2 = NodeId::Sin(1);
|
||||||
|
let wr = NodeId::FbWr(0);
|
||||||
|
let rd = NodeId::FbRd(0);
|
||||||
|
let wr2 = NodeId::FbWr(1);
|
||||||
|
let rd2 = NodeId::FbRd(1);
|
||||||
|
let out = NodeId::Out(0);
|
||||||
|
matrix.place(0, 0, Cell::empty(sin)
|
||||||
|
.out(None, None, sin.out("sig")));
|
||||||
|
matrix.place(0, 1, Cell::empty(wr)
|
||||||
|
.input(wr.inp("inp"), None, None));
|
||||||
|
matrix.place(1, 0, Cell::empty(rd)
|
||||||
|
.out(None, None, rd.out("sig")));
|
||||||
|
matrix.place(1, 1, Cell::empty(out)
|
||||||
|
.input(out.inp("ch1"), None, None));
|
||||||
|
|
||||||
|
matrix.place(0, 2, Cell::empty(sin2)
|
||||||
|
.out(None, None, sin2.out("sig")));
|
||||||
|
matrix.place(0, 3, Cell::empty(wr2)
|
||||||
|
.input(wr2.inp("inp"), None, None));
|
||||||
|
matrix.place(1, 2, Cell::empty(rd2)
|
||||||
|
.out(None, None, rd2.out("sig")));
|
||||||
|
matrix.place(1, 3, Cell::empty(out)
|
||||||
|
.input(out.inp("ch2"), None, None));
|
||||||
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
|
let freq_param = sin2.inp_param("freq").unwrap();
|
||||||
|
matrix.set_param(
|
||||||
|
freq_param,
|
||||||
|
SAtom::param(freq_param.norm(880.0)));
|
||||||
|
|
||||||
|
let (out_l, out_r) = run_for_ms(&mut node_exec, 5.0);
|
||||||
|
|
||||||
|
println!("L: {:?}", out_l);
|
||||||
|
println!("R: {:?}", out_r);
|
||||||
|
|
||||||
|
assert!(false);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue