helpers for comb and allpass, and added allpass node

This commit is contained in:
Weird Constructor 2021-06-28 05:10:46 +02:00
parent a92ad0de35
commit 85ad5e7955
3 changed files with 201 additions and 1 deletions

View file

@ -619,6 +619,73 @@ impl DelayBuffer {
}
}
/// Default size of the delay buffer: 1 seconds at 8 times 48kHz
const DEFAULT_ALLPASS_COMB_SAMPLES : usize = 8 * 48000;
#[derive(Debug, Clone)]
pub struct AllPass {
delay: DelayBuffer,
}
impl AllPass {
pub fn new() -> Self {
Self {
delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES),
}
}
pub fn set_sample_rate(&mut self, srate: f32) {
self.delay.set_sample_rate(srate);
}
pub fn reset(&mut self) {
self.delay.reset();
}
#[inline]
pub fn next(&mut self, time: f32, g: f32, v: f32) -> f32 {
let s = self.delay.cubic_interpolate_at(time);
self.delay.feed(v + s * g);
s + -1.0 * g * v
}
}
#[derive(Debug, Clone)]
pub struct Comb {
delay: DelayBuffer,
}
impl Comb {
pub fn new() -> Self {
Self {
delay: DelayBuffer::new_with_size(DEFAULT_ALLPASS_COMB_SAMPLES),
}
}
pub fn set_sample_rate(&mut self, srate: f32) {
self.delay.set_sample_rate(srate);
}
pub fn reset(&mut self) {
self.delay.reset();
}
#[inline]
pub fn next_feedback(&mut self, time: f32, g: f32, v: f32) -> f32 {
let s = self.delay.cubic_interpolate_at(time);
self.delay.feed(v + s * g);
v
}
#[inline]
pub fn next_feedforward(&mut self, time: f32, g: f32, v: f32) -> f32 {
let s = self.delay.cubic_interpolate_at(time);
self.delay.feed(v);
v + s * g
}
}
// translated from Odin 2 Synthesizer Plugin
// Copyright (C) 2020 TheWaveWarden
// under GPLv3 or any later

View file

@ -20,6 +20,8 @@ mod node_fbwr_fbrd;
mod node_ad;
#[allow(non_upper_case_globals)]
mod node_delay;
#[allow(non_upper_case_globals)]
mod node_allp;
pub mod tracker;
mod satom;
@ -55,6 +57,7 @@ use node_fbwr_fbrd::FbWr;
use node_fbwr_fbrd::FbRd;
use node_ad::Ad;
use node_delay::Delay;
use node_allp::AllP;
pub const MIDI_MAX_FREQ : f32 = 13289.75;
@ -311,6 +314,20 @@ macro_rules! r_tms { ($x: expr, $coarse: expr) => {
}
} }
/// The rounding function for milliseconds knobs
macro_rules! r_fms { ($x: expr, $coarse: expr) => {
if $coarse {
if d_ftme!($x) > 1000.0 {
n_ftme!((d_ftme!($x) / 100.0).round() * 100.0)
} else if d_ftme!($x) > 100.0 {
n_ftme!((d_ftme!($x) / 10.0).round() * 10.0)
} else {
n_ftme!((d_ftme!($x)).round())
}
} else {
n_ftme!((d_ftme!($x) * 10.0).round() / 10.0)
}
} }
/// The default steps function:
macro_rules! stp_d { () => { (20.0, 100.0) } }
@ -370,7 +387,8 @@ define_exp!{n_declick d_declick 0.0, 50.0}
define_exp!{n_env d_env 0.0, 1000.0}
define_exp!{n_time d_time 0.5, 5000.0}
define_exp!{n_time d_time 0.5, 5000.0}
define_exp!{n_ftme d_ftme 0.25, 1000.0}
// Special linear gain factor for the Out node, to be able
// to reach more exact "1.0".
@ -471,6 +489,11 @@ macro_rules! node_list {
(4 mix n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
{5 0 mode setting(0) fa_delay_mode 0 1}
[0 sig],
allp => AllP UIType::Generic UICategory::Signal
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
(1 time n_ftme d_ftme r_fms f_ms stp_m 0.0, 1.0, 25.0)
(2 g n_id d_id r_id f_def stp_d -1.0, 1.0, 0.7)
[0 sig],
test => Test UIType::Generic UICategory::IOUtil
(0 f n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
{1 0 p param(0.0) fa_test_s 0 10}

110
src/dsp/node_allp.rs Normal file
View file

@ -0,0 +1,110 @@
// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
// This is a part of HexoDSP. Released under (A)GPLv3 or any later.
// See README.md and COPYING for details.
use crate::nodes::{NodeAudioContext, NodeExecContext};
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals};
use crate::dsp::helpers::AllPass;
/// A simple amplifier
#[derive(Debug, Clone)]
pub struct AllP {
allpass: Box<AllPass>,
}
impl AllP {
pub fn new(_nid: &NodeId) -> Self {
Self {
allpass: Box::new(AllPass::new()),
}
}
pub const inp : &'static str =
"AllP inp\nThe signal input for the allpass filter.\nRange: (-1..1)";
pub const g : &'static str =
"AllP g\nThe internal factor for the allpass filter.\nRange: (-1..1)";
pub const time : &'static str =
"AllP time\nThe allpass delay time.\nRange: (0..1)";
pub const sig : &'static str =
"AllP sig\nThe output of allpass filter.\nRange: (-1..1)";
pub const DESC : &'static str =
r#"Simple Single Allpass Filter
This is an allpass filter that can be used to build reverbs
or anything you might find it useful for.
"#;
pub const HELP : &'static str =
r#"AllP - A Simple Single Allpass Filter
This is an allpass filter that can be used to build reverbs
or anything you might find it useful for.
Typical arrangements are (Schroeder Reverb):
t=4.5ms
g=0.7 -> Comb
AllP -> AllP -> AllP -> -> Comb
t=42ms t=13.5ms -> Comb
g=0.7 g=0.7 -> Comb
Or:
Comb -> t=0.48ms
Comb -> g=0.7
Comb -> AllP -> AllP -> AllP
Comb -> t=5ms t=1.68ms
g=0.7 g=0.7
Typical values for the comb filters are in the range g=0.6 to 0.9
and time in the range of 30ms to 250ms.
Feel free to deviate from this and experiment around.
Building your own reverbs is fun!
(And don't forget that you can create feedback
using the FbWr and FbRd nodes!)
"#;
}
impl DspNode for AllP {
fn outputs() -> usize { 1 }
fn set_sample_rate(&mut self, srate: f32) {
self.allpass.set_sample_rate(srate);
}
fn reset(&mut self) {
self.allpass.reset();
}
#[inline]
fn process<T: NodeAudioContext>(
&mut self, ctx: &mut T, _ectx: &mut NodeExecContext,
atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf],
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
{
use crate::dsp::{at, out, inp, denorm};
let inp = inp::AllP::inp(inputs);
let time = inp::AllP::time(inputs);
let g = inp::AllP::g(inputs);
let out = out::AllP::sig(outputs);
let ap = &mut *self.allpass;
for frame in 0..ctx.nframes() {
let v = inp.read(frame);
out.write(frame,
ap.next(
denorm::AllP::time(time, frame),
denorm::AllP::g(g, frame),
v));
}
let last_frame = ctx.nframes() - 1;
ctx_vals[0].set(out.read(last_frame));
}
}