helpers for comb and allpass, and added allpass node
This commit is contained in:
parent
a92ad0de35
commit
85ad5e7955
3 changed files with 201 additions and 1 deletions
|
@ -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
|
// translated from Odin 2 Synthesizer Plugin
|
||||||
// Copyright (C) 2020 TheWaveWarden
|
// Copyright (C) 2020 TheWaveWarden
|
||||||
// under GPLv3 or any later
|
// under GPLv3 or any later
|
||||||
|
|
|
@ -20,6 +20,8 @@ mod node_fbwr_fbrd;
|
||||||
mod node_ad;
|
mod node_ad;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
mod node_delay;
|
mod node_delay;
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
mod node_allp;
|
||||||
|
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
mod satom;
|
mod satom;
|
||||||
|
@ -55,6 +57,7 @@ use node_fbwr_fbrd::FbWr;
|
||||||
use node_fbwr_fbrd::FbRd;
|
use node_fbwr_fbrd::FbRd;
|
||||||
use node_ad::Ad;
|
use node_ad::Ad;
|
||||||
use node_delay::Delay;
|
use node_delay::Delay;
|
||||||
|
use node_allp::AllP;
|
||||||
|
|
||||||
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
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:
|
/// The default steps function:
|
||||||
macro_rules! stp_d { () => { (20.0, 100.0) } }
|
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_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
|
// Special linear gain factor for the Out node, to be able
|
||||||
// to reach more exact "1.0".
|
// 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)
|
(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}
|
{5 0 mode setting(0) fa_delay_mode 0 1}
|
||||||
[0 sig],
|
[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
|
test => Test UIType::Generic UICategory::IOUtil
|
||||||
(0 f n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
|
(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}
|
{1 0 p param(0.0) fa_test_s 0 10}
|
||||||
|
|
110
src/dsp/node_allp.rs
Normal file
110
src/dsp/node_allp.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue