2021-08-07 14:56:59 +00:00
|
|
|
// 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.
|
|
|
|
|
2022-08-05 04:45:06 +00:00
|
|
|
use synfx_dsp::{DattorroReverb, DattorroReverbParams, crossfade};
|
2022-07-17 09:58:28 +00:00
|
|
|
use crate::dsp::{denorm, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
|
|
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
pub struct DatParams {
|
2022-07-17 09:58:28 +00:00
|
|
|
frame: usize,
|
2021-08-07 14:56:59 +00:00
|
|
|
predly: ProcBuf,
|
2022-07-17 09:58:28 +00:00
|
|
|
size: ProcBuf,
|
|
|
|
dcy: ProcBuf,
|
|
|
|
ilpf: ProcBuf,
|
|
|
|
ihpf: ProcBuf,
|
|
|
|
dif: ProcBuf,
|
|
|
|
dmix: ProcBuf,
|
2021-08-07 14:56:59 +00:00
|
|
|
mspeed: ProcBuf,
|
2022-07-17 09:58:28 +00:00
|
|
|
mshp: ProcBuf,
|
2021-08-07 14:56:59 +00:00
|
|
|
mdepth: ProcBuf,
|
2022-07-17 09:58:28 +00:00
|
|
|
rlpf: ProcBuf,
|
|
|
|
rhpf: ProcBuf,
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DatParams {
|
|
|
|
#[inline]
|
2022-07-17 09:58:28 +00:00
|
|
|
pub fn set_frame(&mut self, frame: usize) {
|
|
|
|
self.frame = frame;
|
|
|
|
}
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DattorroReverbParams for DatParams {
|
2021-08-07 22:56:10 +00:00
|
|
|
fn pre_delay_time_ms(&self) -> f64 {
|
|
|
|
denorm::PVerb::predly(&self.predly, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn time_scale(&self) -> f64 {
|
|
|
|
denorm::PVerb::size(&self.size, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn decay(&self) -> f64 {
|
|
|
|
denorm::PVerb::dcy(&self.dcy, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn input_low_cutoff_hz(&self) -> f64 {
|
|
|
|
denorm::PVerb::ilpf(&self.ilpf, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn input_high_cutoff_hz(&self) -> f64 {
|
|
|
|
denorm::PVerb::ihpf(&self.ihpf, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn diffusion(&self) -> f64 {
|
2021-08-08 10:51:31 +00:00
|
|
|
denorm::PVerb::dif(&self.dif, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn input_diffusion_mix(&self) -> f64 {
|
|
|
|
denorm::PVerb::dmix(&self.dmix, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn mod_speed(&self) -> f64 {
|
|
|
|
denorm::PVerb::mspeed(&self.mspeed, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn mod_depth(&self) -> f64 {
|
|
|
|
denorm::PVerb::mdepth(&self.mdepth, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn mod_shape(&self) -> f64 {
|
|
|
|
denorm::PVerb::mshp(&self.mshp, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn reverb_low_cutoff_hz(&self) -> f64 {
|
|
|
|
denorm::PVerb::rlpf(&self.rlpf, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
2021-08-07 22:56:10 +00:00
|
|
|
fn reverb_high_cutoff_hz(&self) -> f64 {
|
|
|
|
denorm::PVerb::rhpf(&self.rhpf, self.frame) as f64
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct PVerb {
|
2022-07-17 09:58:28 +00:00
|
|
|
verb: Box<DattorroReverb>,
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PVerb {
|
|
|
|
pub fn new(_nid: &NodeId) -> Self {
|
2022-07-17 09:58:28 +00:00
|
|
|
Self { verb: Box::new(DattorroReverb::new()) }
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const in_l: &'static str = "PVerb in_l\nLeft input channel, will be summed with the right \
|
2021-08-14 09:05:26 +00:00
|
|
|
channel. So you can just feed in a mono signal \
|
|
|
|
without harm.\nRange: (-1..1)\n";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const in_r: &'static str = "PVerb in_r\nRight input channel, will be summed with the \
|
2021-08-14 09:05:26 +00:00
|
|
|
left channel.\nRange: (-1..1)\n";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const sig_l: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb sig_l\nThe left channel of the output signal.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const sig_r: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb sig_r\nThe right channel of the output signal.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const predly: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb predly\nThe pre-delay length for the first reflection.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const size: &'static str = "PVerb size\nThe size of the simulated room. Goes from a small \
|
2021-08-14 09:05:26 +00:00
|
|
|
chamber to a huge hall.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const dcy: &'static str = "PVerb dcy\nThe decay of the sound. If you set this to '1.0' the
|
2021-08-14 09:05:26 +00:00
|
|
|
sound will infinitively be sustained. Just be careful feeding in more \
|
|
|
|
sound with that.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const ilpf: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb ilpf\nInput low-pass filter cutoff frequency, for filtering \
|
|
|
|
the input before it's fed into the pre-delay.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const ihpf: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb ihpf\nInput high-pass filter cutoff frequency, for filtering \
|
|
|
|
the input before it's fed into the pre-delay.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const dif: &'static str = "PVerb dif\nThe amount of diffusion inside the reverb tank. \
|
2021-08-14 09:05:26 +00:00
|
|
|
Setting this to 0 will disable any kind of diffusion and the reverb \
|
|
|
|
will become a more or less simple echo effect.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const dmix: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb dmix\nThe mix between input diffusion and clean output of the \
|
|
|
|
pre-delay. Setting this to 0 will not diffuse any input.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const mspeed: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb mspeed\nThe internal LFO speed, that modulates the internal \
|
|
|
|
diffusion inside the reverb tank. Keeping this low (< 0.2) will sound \
|
|
|
|
a bit more natural than a fast LFO.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const mshp: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb mshp\nThe shape of the LFO. 0.0 is a down ramp, 1.0 is an up \
|
|
|
|
ramp and 0.0 is a triangle. Setting this to 0.5 is a good choise. The \
|
|
|
|
extreme values of 0.0 and 1.0 can lead to audible artifacts.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const mdepth: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb mdepth\nThe depth of the LFO change that is applied to the \
|
|
|
|
diffusion inside the reverb tank. More extreme values (above 0.2) will \
|
|
|
|
lead to more detuned sounds reverbing inside the tank.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const rlpf: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb rlpf\nReverb tank low-pass filter cutoff frequency.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const rhpf: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb rhpf\nReverb tank high-pass filter cutoff frequency.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const mix: &'static str =
|
2021-08-14 09:05:26 +00:00
|
|
|
"PVerb mix\nDry/Wet mix between the input and the diffused output.\nRange: (0..1)";
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const DESC: &'static str = r#"Plate Reverb
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
"#;
|
2022-07-17 09:58:28 +00:00
|
|
|
pub const HELP: &'static str = r#"PVerb - Plate Reverb (by Jon Dattorro)
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
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.
|
2021-08-14 09:05:26 +00:00
|
|
|
|
|
|
|
Structure of the reverb is:
|
|
|
|
|
|
|
|
Left Right
|
|
|
|
| |
|
|
|
|
\----+----/
|
|
|
|
v
|
|
|
|
'ilpf' 'ihpf' 'predly'
|
|
|
|
Input Low-Pass -> Input High-Pass -> Pre-Delay
|
|
|
|
|
|
|
|
|
o------------------o--------------\ |
|
|
|
|
+------\ +----------\ | |
|
|
|
|
v | v | | v--o----> All-Pass Diffusor
|
|
|
|
[Left Channel] [Right Channel] | \--x 'dmix' |
|
|
|
|
/> Diffusor 1 |'size' Diffusor 1 <-\ | ^------------/
|
|
|
|
| Delay 1 |'size' Delay 1 | |
|
|
|
|
| LPF/HPF | LPF/HPF 'rlpf'/'rhpf'
|
|
|
|
| [x Decay] |'dcy' [x Decay] | | 'mspeed'
|
|
|
|
o> Diffusor 2 |'size' Diffusor 2 <-o----o-x-----LFO 'mshp'
|
|
|
|
| Delay 2 |'size' Delay 2 | | 'mdepth'
|
|
|
|
| | | | | |
|
|
|
|
| x 'dcy'| x | |
|
|
|
|
| | \-[feedback]-/ | |
|
|
|
|
| \--------[feedback]-----------/ |
|
|
|
|
\--------------------------------------/
|
|
|
|
|
|
|
|
Multiple Taps into Left/Right Diffusors 1/2 and Delays 1/2
|
|
|
|
are then fed to the left and right output channels.
|
2021-08-07 14:56:59 +00:00
|
|
|
"#;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DspNode for PVerb {
|
2022-07-17 09:58:28 +00:00
|
|
|
fn outputs() -> usize {
|
|
|
|
1
|
|
|
|
}
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
fn set_sample_rate(&mut self, srate: f32) {
|
2021-08-07 22:56:10 +00:00
|
|
|
self.verb.set_sample_rate(srate as f64);
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn reset(&mut self) {
|
|
|
|
self.verb.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn process<T: NodeAudioContext>(
|
2022-07-17 09:58:28 +00:00
|
|
|
&mut self,
|
|
|
|
ctx: &mut T,
|
|
|
|
_ectx: &mut NodeExecContext,
|
2021-10-30 17:40:11 +00:00
|
|
|
nctx: &NodeContext,
|
2022-07-17 09:58:28 +00:00
|
|
|
_atoms: &[SAtom],
|
|
|
|
inputs: &[ProcBuf],
|
|
|
|
outputs: &mut [ProcBuf],
|
|
|
|
ctx_vals: LedPhaseVals,
|
|
|
|
) {
|
2021-08-08 07:43:16 +00:00
|
|
|
use crate::dsp::{inp, out_idx};
|
2021-08-07 14:56:59 +00:00
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
let mut in_l = inp::PVerb::in_l(inputs);
|
|
|
|
let mut in_r = inp::PVerb::in_r(inputs);
|
2021-10-30 17:40:11 +00:00
|
|
|
|
|
|
|
if (nctx.in_connected & 0x03) != 0x03 {
|
|
|
|
if nctx.in_connected & 0x01 == 0x01 {
|
|
|
|
in_r = in_l;
|
|
|
|
} else if nctx.in_connected & 0x02 == 0x02 {
|
|
|
|
in_l = in_r;
|
|
|
|
}
|
|
|
|
}
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
let mut params = DatParams {
|
|
|
|
frame: 0,
|
|
|
|
predly: *inp::PVerb::predly(inputs),
|
2022-07-17 09:58:28 +00:00
|
|
|
size: *inp::PVerb::size(inputs),
|
|
|
|
dcy: *inp::PVerb::dcy(inputs),
|
|
|
|
ilpf: *inp::PVerb::ilpf(inputs),
|
|
|
|
ihpf: *inp::PVerb::ihpf(inputs),
|
|
|
|
dif: *inp::PVerb::dif(inputs),
|
|
|
|
dmix: *inp::PVerb::dmix(inputs),
|
2021-08-07 14:56:59 +00:00
|
|
|
mspeed: *inp::PVerb::mspeed(inputs),
|
2022-07-17 09:58:28 +00:00
|
|
|
mshp: *inp::PVerb::mshp(inputs),
|
2021-08-07 14:56:59 +00:00
|
|
|
mdepth: *inp::PVerb::mdepth(inputs),
|
2022-07-17 09:58:28 +00:00
|
|
|
rlpf: *inp::PVerb::rlpf(inputs),
|
|
|
|
rhpf: *inp::PVerb::rhpf(inputs),
|
2021-08-07 14:56:59 +00:00
|
|
|
};
|
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
let mix = inp::PVerb::mix(inputs);
|
|
|
|
let out_i = out_idx::PVerb::sig_r();
|
2021-08-07 14:56:59 +00:00
|
|
|
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];
|
|
|
|
|
2021-08-17 01:53:43 +00:00
|
|
|
let verb = &mut *self.verb;
|
2021-08-07 14:56:59 +00:00
|
|
|
|
|
|
|
for frame in 0..ctx.nframes() {
|
|
|
|
let (i_l, i_r) = (in_l.read(frame), in_r.read(frame));
|
|
|
|
|
|
|
|
params.set_frame(frame);
|
2021-08-07 22:56:10 +00:00
|
|
|
let (l, r) = verb.process(&mut params, i_l as f64, i_r as f64);
|
2021-08-07 14:56:59 +00:00
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
out_l.write(frame, crossfade(i_l, l as f32, denorm::PVerb::mix(mix, frame)));
|
|
|
|
out_r.write(frame, crossfade(i_r, r as f32, denorm::PVerb::mix(mix, frame)));
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
|
2022-07-17 09:58:28 +00:00
|
|
|
ctx_vals[0].set(out_l.read(ctx.nframes() - 1) + out_r.read(ctx.nframes() - 1));
|
2021-08-07 14:56:59 +00:00
|
|
|
}
|
|
|
|
}
|