Finished reverb translation and added as a node, but there are still bugs to fix.
This commit is contained in:
parent
43f1f6d7b9
commit
e45b8a5ebd
4 changed files with 336 additions and 9 deletions
|
@ -39,7 +39,7 @@ const DAT_LEFT_DELAY2_TIME_MS : f32 = 3720.0 / DAT_SAMPLES_PER_MS;
|
|||
const DAT_RIGHT_DELAY1_TIME_MS : f32 = 4217.0 / DAT_SAMPLES_PER_MS;
|
||||
const DAT_RIGHT_DELAY2_TIME_MS : f32 = 3163.0 / DAT_SAMPLES_PER_MS;
|
||||
|
||||
const DAT_TAPS_TIME_MS : [f32; 7] = [
|
||||
const DAT_LEFT_TAPS_TIME_MS : [f32; 7] = [
|
||||
266.0 / DAT_SAMPLES_PER_MS,
|
||||
2974.0 / DAT_SAMPLES_PER_MS,
|
||||
1913.0 / DAT_SAMPLES_PER_MS,
|
||||
|
@ -49,6 +49,16 @@ const DAT_TAPS_TIME_MS : [f32; 7] = [
|
|||
1066.0 / DAT_SAMPLES_PER_MS,
|
||||
];
|
||||
|
||||
const DAT_RIGHT_TAPS_TIME_MS : [f32; 7] = [
|
||||
353.0 / DAT_SAMPLES_PER_MS,
|
||||
3627.0 / DAT_SAMPLES_PER_MS,
|
||||
1228.0 / DAT_SAMPLES_PER_MS,
|
||||
2673.0 / DAT_SAMPLES_PER_MS,
|
||||
2111.0 / DAT_SAMPLES_PER_MS,
|
||||
335.0 / DAT_SAMPLES_PER_MS,
|
||||
121.0 / DAT_SAMPLES_PER_MS,
|
||||
];
|
||||
|
||||
const DAT_LFO_FREQS_HZ : [f32; 4] = [ 0.1, 0.15, 0.12, 0.18 ];
|
||||
|
||||
const DAT_INPUT_DIFFUSION1 : f32 = 0.75;
|
||||
|
@ -68,6 +78,7 @@ use crate::dsp::helpers::{
|
|||
DCBlockFilter
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DattorroReverb {
|
||||
last_scale: f32,
|
||||
|
||||
|
@ -97,7 +108,7 @@ pub trait DattorroReverbParams {
|
|||
/// Time for the pre-delay of the reverb. Any sensible `ms` that fits
|
||||
/// into a delay buffer of 5 seconds.
|
||||
fn pre_delay_time_ms(&self) -> f32;
|
||||
/// The size of the reverb, values go from 0.0025 to 4.0
|
||||
/// The size of the reverb, values go from 0.0 to 1.0.
|
||||
fn time_scale(&self) -> f32;
|
||||
/// High-pass input filter cutoff freq in Hz, range: 0.0 to 22000.0
|
||||
fn input_high_cutoff_hz(&self) -> f32;
|
||||
|
@ -118,6 +129,8 @@ pub trait DattorroReverbParams {
|
|||
fn input_diffusion_mix(&self) -> f32;
|
||||
/// The amount of plate diffusion going on, range: 0.0 to 1.0
|
||||
fn diffusion(&self) -> f32;
|
||||
/// Internal tank decay time, range: 0.0 to 1.0
|
||||
fn decay(&self) -> f32;
|
||||
}
|
||||
|
||||
impl DattorroReverb {
|
||||
|
@ -313,21 +326,26 @@ impl DattorroReverb {
|
|||
) -> (f32, f32)
|
||||
{
|
||||
// Some parameter setup...
|
||||
self.set_time_scale(params.time_scale());
|
||||
let timescale = 0.0025 + (4.0 - 0.0025) * params.time_scale();
|
||||
self.set_time_scale(timescale);
|
||||
|
||||
self.hpf[0].set_freq(params.reverb_high_cutoff_hz());
|
||||
self.hpf[1].set_freq(params.reverb_high_cutoff_hz());
|
||||
self.lpf[0].set_freq(params.reverb_low_cutoff_hz());
|
||||
self.lpf[1].set_freq(params.reverb_low_cutoff_hz());
|
||||
|
||||
let mod_speed = params.mod_speed();
|
||||
let mod_speed = mod_speed * mod_speed;
|
||||
let mod_speed = mod_speed * 99.0 + 1.0;
|
||||
|
||||
self.lfos[0].set(
|
||||
DAT_LFO_FREQS_HZ[0] * params.mod_speed(), params.mod_shape());
|
||||
DAT_LFO_FREQS_HZ[0] * mod_speed, params.mod_shape());
|
||||
self.lfos[1].set(
|
||||
DAT_LFO_FREQS_HZ[1] * params.mod_speed(), params.mod_shape());
|
||||
DAT_LFO_FREQS_HZ[1] * mod_speed, params.mod_shape());
|
||||
self.lfos[2].set(
|
||||
DAT_LFO_FREQS_HZ[2] * params.mod_speed(), params.mod_shape());
|
||||
DAT_LFO_FREQS_HZ[2] * mod_speed, params.mod_shape());
|
||||
self.lfos[3].set(
|
||||
DAT_LFO_FREQS_HZ[3] * params.mod_speed(), params.mod_shape());
|
||||
DAT_LFO_FREQS_HZ[3] * mod_speed, params.mod_shape());
|
||||
|
||||
self.apf1[0].2 = -DAT_PLATE_DIFFUSION1 * params.diffusion();
|
||||
self.apf1[1].2 = -DAT_PLATE_DIFFUSION1 * params.diffusion();
|
||||
|
@ -338,6 +356,8 @@ impl DattorroReverb {
|
|||
left_apf2_delay_ms, right_apf2_delay_ms)
|
||||
= self.calc_apf_delay_times(params);
|
||||
|
||||
// Parameter setup done!
|
||||
|
||||
// Input into their corresponding DC blockers
|
||||
let input_r = self.inp_dc_block[0].next(input_r);
|
||||
let input_l = self.inp_dc_block[1].next(input_l);
|
||||
|
@ -367,6 +387,58 @@ impl DattorroReverb {
|
|||
self.left_sum += tank_feed;
|
||||
self.right_sum += tank_feed;
|
||||
|
||||
(0.0, 0.0)
|
||||
// Calculate tank decay of the left/right signal channels.
|
||||
let decay = 1.0 - params.decay().clamp(0.1, 0.9999);
|
||||
let decay = 1.0 - (decay * decay);
|
||||
|
||||
// Left Sum => APF1 => Delay1 => LPF => HPF => APF2 => Delay2
|
||||
// And then send this over to the right sum.
|
||||
let left = self.left_sum;
|
||||
let left = self.apf1[0].0.next(left_apf1_delay_ms, self.apf1[0].2, left);
|
||||
let left_apf_tap = left;
|
||||
let left = self.delay1[0].0.next_cubic(self.delay1[0].1, left);
|
||||
let left = self.lpf[0].process(left);
|
||||
let left = self.hpf[0].process(left);
|
||||
let left = left * decay;
|
||||
let left = self.apf2[0].0.next(left_apf2_delay_ms, self.apf2[0].2, left);
|
||||
let left = self.delay2[0].0.next_cubic(self.delay2[0].1, left);
|
||||
|
||||
// Right Sum => APF1 => Delay1 => LPF => HPF => APF2 => Delay2
|
||||
// And then send this over to the left sum.
|
||||
let right = self.right_sum;
|
||||
let right = self.apf1[1].0.next(right_apf1_delay_ms, self.apf1[1].2, right);
|
||||
let right_apf_tap = right;
|
||||
let right = self.delay1[1].0.next_cubic(self.delay1[1].1, right);
|
||||
let right = self.lpf[1].process(right);
|
||||
let right = self.hpf[1].process(right);
|
||||
let right = right * decay;
|
||||
let right = self.apf2[1].0.next(right_apf2_delay_ms, self.apf2[1].2, right);
|
||||
let right = self.delay2[1].0.next_cubic(self.delay2[1].1, right);
|
||||
|
||||
self.right_sum = left * decay;
|
||||
self.left_sum = right * decay;
|
||||
|
||||
let mut left_accum = left_apf_tap;
|
||||
left_accum += self.delay1[0].0.tap( DAT_LEFT_TAPS_TIME_MS[0]);
|
||||
left_accum += self.delay1[0].0.tap( DAT_LEFT_TAPS_TIME_MS[1]);
|
||||
left_accum -= self.apf2[0].0.delay_tap(DAT_LEFT_TAPS_TIME_MS[2]);
|
||||
left_accum += self.delay2[0].0.tap( DAT_LEFT_TAPS_TIME_MS[3]);
|
||||
left_accum -= self.delay1[1].0.tap( DAT_LEFT_TAPS_TIME_MS[4]);
|
||||
left_accum -= self.apf2[1].0.delay_tap(DAT_LEFT_TAPS_TIME_MS[5]);
|
||||
left_accum -= self.delay2[1].0.tap( DAT_LEFT_TAPS_TIME_MS[6]);
|
||||
|
||||
let mut right_accum = right_apf_tap;
|
||||
right_accum += self.delay1[1].0.tap( DAT_RIGHT_TAPS_TIME_MS[0]);
|
||||
right_accum += self.delay1[1].0.tap( DAT_RIGHT_TAPS_TIME_MS[1]);
|
||||
right_accum -= self.apf2[1].0.delay_tap(DAT_RIGHT_TAPS_TIME_MS[2]);
|
||||
right_accum += self.delay2[1].0.tap( DAT_RIGHT_TAPS_TIME_MS[3]);
|
||||
right_accum -= self.delay1[0].0.tap( DAT_RIGHT_TAPS_TIME_MS[4]);
|
||||
right_accum -= self.apf2[0].0.delay_tap(DAT_RIGHT_TAPS_TIME_MS[5]);
|
||||
right_accum -= self.delay2[0].0.tap( DAT_RIGHT_TAPS_TIME_MS[6]);
|
||||
|
||||
let left_out = self.out_dc_block[0].next(left_accum);
|
||||
let right_out = self.out_dc_block[1].next(right_accum);
|
||||
|
||||
(left_out * 0.5, right_out * 0.5)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -740,6 +740,21 @@ impl DelayBuffer {
|
|||
self.wr = (self.wr + 1) % self.data.len();
|
||||
}
|
||||
|
||||
/// Combines [DelayBuffer::cubic_interpolate_at] and [DelayBuffer::feed]
|
||||
/// into one convenient function.
|
||||
#[inline]
|
||||
pub fn next_cubic(&mut self, delay_time_ms: f32, input: f32) -> f32 {
|
||||
let res = self.cubic_interpolate_at(delay_time_ms);
|
||||
self.feed(input);
|
||||
res
|
||||
}
|
||||
|
||||
/// Shorthand for [DelayBuffer::cubic_interpolate_at].
|
||||
#[inline]
|
||||
pub fn tap(&self, delay_time_ms: f32) -> f32 {
|
||||
self.cubic_interpolate_at(delay_time_ms)
|
||||
}
|
||||
|
||||
/// Fetch a sample from the delay buffer at the given time.
|
||||
///
|
||||
/// * `delay_time_ms` - Delay time in milliseconds.
|
||||
|
@ -814,6 +829,11 @@ impl AllPass {
|
|||
self.delay.reset();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delay_tap(&self, time: f32) -> f32 {
|
||||
self.delay.cubic_interpolate_at(time)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next(&mut self, time: f32, g: f32, v: f32) -> f32 {
|
||||
let s = self.delay.cubic_interpolate_at(time);
|
||||
|
@ -842,6 +862,11 @@ impl Comb {
|
|||
self.delay.reset();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn delay_tap(&self, time: f32) -> f32 {
|
||||
self.delay.cubic_interpolate_at(time)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next_feedback(&mut self, time: f32, g: f32, v: f32) -> f32 {
|
||||
let s = self.delay.cubic_interpolate_at(time);
|
||||
|
|
|
@ -42,6 +42,8 @@ mod node_biqfilt;
|
|||
mod node_comb;
|
||||
#[allow(non_upper_case_globals)]
|
||||
mod node_tslfo;
|
||||
#[allow(non_upper_case_globals)]
|
||||
mod node_pverb;
|
||||
|
||||
pub mod biquad;
|
||||
pub mod tracker;
|
||||
|
@ -101,6 +103,7 @@ use node_vosc::VOsc;
|
|||
use node_biqfilt::BiqFilt;
|
||||
use node_comb::Comb;
|
||||
use node_tslfo::TsLfo;
|
||||
use node_pverb::PVerb;
|
||||
|
||||
pub const MIDI_MAX_FREQ : f32 = 13289.75;
|
||||
|
||||
|
@ -770,6 +773,24 @@ macro_rules! node_list {
|
|||
{4 0 ftype setting(0) fa_biqfilt_type 0 1}
|
||||
{5 1 order setting(0) fa_biqfilt_ord 0 3}
|
||||
[0 sig],
|
||||
pverb => PVerb UIType::Generic UICategory::Signal
|
||||
( 0 in_l n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||
( 1 in_r n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||
( 2 predly n_time d_time r_tms f_ms stp_m 0.0, 1.0, 0.0)
|
||||
( 3 size n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
|
||||
( 4 dcy n_id d_id r_id f_def stp_d 0.0, 1.0, 0.25)
|
||||
( 5 ilpf n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 22050.0)
|
||||
( 6 ihpf n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 0.0)
|
||||
( 7 idif n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||
( 8 dmix n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||
( 9 mspeed n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0)
|
||||
(10 mshp n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||
(11 mdepth n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0)
|
||||
(12 rlpf n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 22050.0)
|
||||
(13 rhpf n_pit d_pit r_fq f_freq stp_d -1.0, 0.5647131, 0.0)
|
||||
(14 mix n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5)
|
||||
[0 sig_l]
|
||||
[1 sig_r],
|
||||
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}
|
||||
|
|
209
src/dsp/node_pverb.rs
Normal file
209
src/dsp/node_pverb.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2021 Weird Constructor <weirdconstructor@gmail.com>
|
||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
use crate::dsp::{
|
||||
NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals,
|
||||
GraphAtomData, GraphFun, NodeContext,
|
||||
denorm
|
||||
};
|
||||
use super::helpers::crossfade;
|
||||
use super::dattorro::{
|
||||
DattorroReverb,
|
||||
DattorroReverbParams
|
||||
};
|
||||
|
||||
pub struct DatParams {
|
||||
frame: usize,
|
||||
predly: ProcBuf,
|
||||
size: ProcBuf,
|
||||
dcy: ProcBuf,
|
||||
ilpf: ProcBuf,
|
||||
ihpf: ProcBuf,
|
||||
idif: ProcBuf,
|
||||
dmix: ProcBuf,
|
||||
mspeed: ProcBuf,
|
||||
mshp: ProcBuf,
|
||||
mdepth: ProcBuf,
|
||||
rlpf: ProcBuf,
|
||||
rhpf: ProcBuf,
|
||||
}
|
||||
|
||||
impl DatParams {
|
||||
#[inline]
|
||||
pub fn set_frame(&mut self, frame: usize) { self.frame = frame; }
|
||||
}
|
||||
|
||||
impl DattorroReverbParams for DatParams {
|
||||
fn pre_delay_time_ms(&self) -> f32 {
|
||||
denorm::PVerb::predly(&self.predly, self.frame)
|
||||
}
|
||||
fn time_scale(&self) -> f32 {
|
||||
denorm::PVerb::size(&self.size, self.frame)
|
||||
}
|
||||
fn decay(&self) -> f32 {
|
||||
denorm::PVerb::dcy(&self.dcy, self.frame)
|
||||
}
|
||||
fn input_low_cutoff_hz(&self) -> f32 {
|
||||
denorm::PVerb::ilpf(&self.ilpf, self.frame)
|
||||
}
|
||||
fn input_high_cutoff_hz(&self) -> f32 {
|
||||
denorm::PVerb::ihpf(&self.ihpf, self.frame)
|
||||
}
|
||||
fn diffusion(&self) -> f32 {
|
||||
denorm::PVerb::idif(&self.idif, self.frame)
|
||||
}
|
||||
fn input_diffusion_mix(&self) -> f32 {
|
||||
denorm::PVerb::dmix(&self.dmix, self.frame)
|
||||
}
|
||||
fn mod_speed(&self) -> f32 {
|
||||
denorm::PVerb::mspeed(&self.mspeed, self.frame)
|
||||
}
|
||||
fn mod_depth(&self) -> f32 {
|
||||
denorm::PVerb::mdepth(&self.mdepth, self.frame)
|
||||
}
|
||||
fn mod_shape(&self) -> f32 {
|
||||
denorm::PVerb::mshp(&self.mshp, self.frame)
|
||||
}
|
||||
fn reverb_low_cutoff_hz(&self) -> f32 {
|
||||
denorm::PVerb::rlpf(&self.rlpf, self.frame)
|
||||
}
|
||||
fn reverb_high_cutoff_hz(&self) -> f32 {
|
||||
denorm::PVerb::rhpf(&self.rhpf, self.frame)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PVerb {
|
||||
verb: Box<DattorroReverb>,
|
||||
}
|
||||
|
||||
impl PVerb {
|
||||
pub fn new(_nid: &NodeId) -> Self {
|
||||
Self {
|
||||
verb: Box::new(DattorroReverb::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const in_l : &'static str =
|
||||
"PVerb in_l\n\nRange: (-1..1)\n";
|
||||
pub const in_r : &'static str =
|
||||
"PVerb in_r\n\nRange: (-1..1)\n";
|
||||
pub const sig_l : &'static str =
|
||||
"PVerb sig_l\n\nRange: (0..1)";
|
||||
pub const sig_r : &'static str =
|
||||
"PVerb sig_r\n\nRange: (0..1)";
|
||||
pub const predly : &'static str =
|
||||
"PVerb predly\n\nRange: (0..1)";
|
||||
pub const size : &'static str =
|
||||
"PVerb size\n\nRange: (0..1)";
|
||||
pub const dcy : &'static str =
|
||||
"PVerb dcy\n\nRange: (0..1)";
|
||||
pub const ilpf : &'static str =
|
||||
"PVerb ilpf\n\nRange: (0..1)";
|
||||
pub const ihpf : &'static str =
|
||||
"PVerb ihpf\n\nRange: (0..1)";
|
||||
pub const idif : &'static str =
|
||||
"PVerb idif\n\nRange: (0..1)";
|
||||
pub const dmix : &'static str =
|
||||
"PVerb dmix\n\nRange: (0..1)";
|
||||
pub const mspeed : &'static str =
|
||||
"PVerb mspeed\n\nRange: (0..1)";
|
||||
pub const mshp : &'static str =
|
||||
"PVerb mshp\n\nRange: (0..1)";
|
||||
pub const mdepth : &'static str =
|
||||
"PVerb mdepth\n\nRange: (0..1)";
|
||||
pub const rlpf : &'static str =
|
||||
"PVerb rlpf\n\nRange: (0..1)";
|
||||
pub const rhpf : &'static str =
|
||||
"PVerb rhpf\n\nRange: (0..1)";
|
||||
pub const mix : &'static str =
|
||||
"PVerb mix\n\nRange: (0..1)";
|
||||
pub const DESC : &'static str =
|
||||
r#"Plate Reverb
|
||||
|
||||
This is a simple but yet powerful small plate reverb based on the design by Jon Dattorro. It should suit your needs from small rooms up to large athmospheric sound scapes.
|
||||
"#;
|
||||
pub const HELP : &'static str =
|
||||
r#"PVerb - Plate Reverb (by Jon Dattorro)
|
||||
|
||||
This is a simple but yet powerful small plate reverb based on the design
|
||||
by Jon Dattorro. It should suit your needs from small rooms up to large
|
||||
athmospheric sound scapes. It provides two inputs, and two outputs for
|
||||
stereo signals. You can also feed a monophonic input, and you will get
|
||||
a stereo output.
|
||||
|
||||
It provides simple low-pass and high-pass filters for the inputs
|
||||
and another set of them for the internal reverberation tank to control
|
||||
the bandwidth of the reverbs.
|
||||
|
||||
Internal modulation keeps the sound alive and spreads it even more.
|
||||
"#;
|
||||
|
||||
}
|
||||
|
||||
impl DspNode for PVerb {
|
||||
fn outputs() -> usize { 1 }
|
||||
|
||||
fn set_sample_rate(&mut self, srate: f32) {
|
||||
self.verb.set_sample_rate(srate);
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.verb.reset();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn process<T: NodeAudioContext>(
|
||||
&mut self, ctx: &mut T, _ectx: &mut NodeExecContext,
|
||||
_nctx: &NodeContext,
|
||||
_atoms: &[SAtom], inputs: &[ProcBuf],
|
||||
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
||||
{
|
||||
use crate::dsp::{out, inp, out_idx};
|
||||
|
||||
let in_l = inp::PVerb::in_l(inputs);
|
||||
let in_r = inp::PVerb::in_r(inputs);
|
||||
|
||||
let mut params = DatParams {
|
||||
frame: 0,
|
||||
predly: *inp::PVerb::predly(inputs),
|
||||
size: *inp::PVerb::size(inputs),
|
||||
dcy: *inp::PVerb::dcy(inputs),
|
||||
ilpf: *inp::PVerb::ilpf(inputs),
|
||||
ihpf: *inp::PVerb::ihpf(inputs),
|
||||
idif: *inp::PVerb::idif(inputs),
|
||||
dmix: *inp::PVerb::dmix(inputs),
|
||||
mspeed: *inp::PVerb::mspeed(inputs),
|
||||
mshp: *inp::PVerb::mshp(inputs),
|
||||
mdepth: *inp::PVerb::mdepth(inputs),
|
||||
rlpf: *inp::PVerb::rlpf(inputs),
|
||||
rhpf: *inp::PVerb::rhpf(inputs),
|
||||
};
|
||||
|
||||
let mix = inp::PVerb::mix(inputs);
|
||||
// let out_l = out::PVerb::sig_l(outputs);
|
||||
// let out_r = out::PVerb::sig_r(outputs);
|
||||
let out_i = out_idx::PVerb::sig_r();
|
||||
let (out_l, out_r) = outputs.split_at_mut(out_i);
|
||||
let out_l = &mut out_l[0];
|
||||
let out_r = &mut out_r[0];
|
||||
|
||||
let mut verb = &mut *self.verb;
|
||||
|
||||
for frame in 0..ctx.nframes() {
|
||||
let (i_l, i_r) = (in_l.read(frame), in_r.read(frame));
|
||||
|
||||
params.set_frame(frame);
|
||||
let (l, r) = verb.process(&mut params, i_l, i_r);
|
||||
|
||||
out_l.write(frame, crossfade(i_l, l, denorm::PVerb::mix(mix, frame)));
|
||||
out_r.write(frame, crossfade(i_r, r, denorm::PVerb::mix(mix, frame)));
|
||||
}
|
||||
|
||||
ctx_vals[0].set(
|
||||
out_l.read(ctx.nframes() - 1)
|
||||
+ out_r.read(ctx.nframes() - 1));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue