Made sure synfx-dsp-jit is optional
This commit is contained in:
commit
a0478f64e9
35 changed files with 88 additions and 3723 deletions
|
@ -18,8 +18,9 @@ ringbuf = "0.2.2"
|
||||||
triple_buffer = "5.0.6"
|
triple_buffer = "5.0.6"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
hound = "3.4.0"
|
hound = "3.4.0"
|
||||||
num-traits = "0.2.14"
|
|
||||||
synfx-dsp-jit = { path = "../synfx-dsp-jit", optional = true }
|
synfx-dsp-jit = { path = "../synfx-dsp-jit", optional = true }
|
||||||
|
synfx-dsp = "0.5.1"
|
||||||
|
#synfx-dsp = { git = "https://github.com/WeirdConstructor/synfx-dsp" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
num-complex = "0.2"
|
num-complex = "0.2"
|
||||||
|
|
|
@ -7,6 +7,7 @@ use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::blocklang::*;
|
use crate::blocklang::*;
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
use synfx_dsp_jit::{ASTNode, JITCompileError};
|
use synfx_dsp_jit::{ASTNode, JITCompileError};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -176,6 +177,7 @@ impl BlkASTNode {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BlkJITCompileError {
|
pub enum BlkJITCompileError {
|
||||||
UnknownError,
|
UnknownError,
|
||||||
|
NoSynfxDSPJit,
|
||||||
BadTree(ASTNodeRef),
|
BadTree(ASTNodeRef),
|
||||||
NoOutputAtIdx(String, usize),
|
NoOutputAtIdx(String, usize),
|
||||||
ASTMissingOutputLabel(usize),
|
ASTMissingOutputLabel(usize),
|
||||||
|
@ -186,6 +188,7 @@ pub enum BlkJITCompileError {
|
||||||
TooManyInputs(String, usize),
|
TooManyInputs(String, usize),
|
||||||
WrongNumberOfChilds(String, usize, usize),
|
WrongNumberOfChilds(String, usize, usize),
|
||||||
UnassignedInput(String, usize, String),
|
UnassignedInput(String, usize, String),
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
JITCompileError(JITCompileError),
|
JITCompileError(JITCompileError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +203,11 @@ pub struct Block2JITCompiler {
|
||||||
// - make references where IDs go
|
// - make references where IDs go
|
||||||
// - add a use count to each node, so that we know when to make temporary variables
|
// - add a use count to each node, so that we know when to make temporary variables
|
||||||
|
|
||||||
|
#[cfg(not(feature = "synfx-dsp-jit"))]
|
||||||
|
enum ASTNode {
|
||||||
|
NoSynfxDSPJit
|
||||||
|
}
|
||||||
|
|
||||||
impl Block2JITCompiler {
|
impl Block2JITCompiler {
|
||||||
pub fn new(lang: Rc<RefCell<BlockLanguage>>) -> Self {
|
pub fn new(lang: Rc<RefCell<BlockLanguage>>) -> Self {
|
||||||
Self { id_node_map: HashMap::new(), idout_var_map: HashMap::new(), lang, tmpvar_counter: 0 }
|
Self { id_node_map: HashMap::new(), idout_var_map: HashMap::new(), lang, tmpvar_counter: 0 }
|
||||||
|
@ -255,10 +263,8 @@ impl Block2JITCompiler {
|
||||||
"<r>" => {
|
"<r>" => {
|
||||||
if let Some((_in, out, first)) = node.first_child() {
|
if let Some((_in, out, first)) = node.first_child() {
|
||||||
let out = if out.len() > 0 { Some(out) } else { None };
|
let out = if out.len() > 0 { Some(out) } else { None };
|
||||||
let childs = vec![
|
let childs =
|
||||||
self.trans2bjit(&first, out)?,
|
vec![self.trans2bjit(&first, out)?, BlkASTNode::new_get(0, "_res_")];
|
||||||
BlkASTNode::new_get(0, "_res_")
|
|
||||||
];
|
|
||||||
Ok(BlkASTNode::new_area(childs))
|
Ok(BlkASTNode::new_area(childs))
|
||||||
} else {
|
} else {
|
||||||
Err(BlkJITCompileError::BadTree(node.clone()))
|
Err(BlkJITCompileError::BadTree(node.clone()))
|
||||||
|
@ -385,6 +391,7 @@ impl Block2JITCompiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
pub fn bjit2jit(&mut self, ast: &BlkASTRef) -> Result<Box<ASTNode>, BlkJITCompileError> {
|
pub fn bjit2jit(&mut self, ast: &BlkASTRef) -> Result<Box<ASTNode>, BlkJITCompileError> {
|
||||||
use synfx_dsp_jit::build::*;
|
use synfx_dsp_jit::build::*;
|
||||||
|
|
||||||
|
@ -487,16 +494,24 @@ impl Block2JITCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&mut self, fun: &BlockFun) -> Result<Box<ASTNode>, BlkJITCompileError> {
|
pub fn compile(&mut self, fun: &BlockFun) -> Result<Box<ASTNode>, BlkJITCompileError> {
|
||||||
let tree = fun.generate_tree::<ASTNodeRef>("zero").unwrap();
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
println!("{}", tree.walk_dump("", "", 0));
|
{
|
||||||
|
let tree = fun.generate_tree::<ASTNodeRef>("zero").unwrap();
|
||||||
|
println!("{}", tree.walk_dump("", "", 0));
|
||||||
|
|
||||||
let blkast = self.trans2bjit(&tree, None)?;
|
let blkast = self.trans2bjit(&tree, None)?;
|
||||||
println!("R: {}", blkast.dump(0, None));
|
println!("R: {}", blkast.dump(0, None));
|
||||||
|
|
||||||
self.bjit2jit(&blkast)
|
self.bjit2jit(&blkast)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "synfx-dsp-jit"))]
|
||||||
|
{
|
||||||
|
Err(BlkJITCompileError::NoSynfxDSPJit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1808,6 +1808,7 @@ impl BlockCodeView for BlockFun {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
use crate::blocklang::{BlockLanguage, BlockType, BlockUserInput};
|
use crate::blocklang::{BlockLanguage, BlockType, BlockUserInput};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
use synfx_dsp_jit::DSPNodeTypeLibrary;
|
use synfx_dsp_jit::DSPNodeTypeLibrary;
|
||||||
|
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
pub fn setup_hxdsp_block_language(
|
pub fn setup_hxdsp_block_language(
|
||||||
dsp_lib: Rc<RefCell<DSPNodeTypeLibrary>>,
|
dsp_lib: Rc<RefCell<DSPNodeTypeLibrary>>,
|
||||||
) -> Rc<RefCell<BlockLanguage>> {
|
) -> Rc<RefCell<BlockLanguage>> {
|
||||||
|
|
|
@ -1,272 +0,0 @@
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// The implementation of this Biquad Filter has been adapted from
|
|
||||||
// SamiPerttu, Copyright (c) 2020, under the MIT License.
|
|
||||||
// See also: https://github.com/SamiPerttu/fundsp/blob/master/src/filter.rs
|
|
||||||
//
|
|
||||||
// You will find a float type agnostic version in SamiPerttu's code.
|
|
||||||
// I converted this to pure f32 for no good reason, other than making
|
|
||||||
// the code more readable (for me).
|
|
||||||
|
|
||||||
use std::f32::consts::*;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
pub struct BiquadCoefs {
|
|
||||||
pub a1: f32,
|
|
||||||
pub a2: f32,
|
|
||||||
pub b0: f32,
|
|
||||||
pub b1: f32,
|
|
||||||
pub b2: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// https://github.com/VCVRack/Befaco/blob/v1/src/ChowDSP.hpp#L339
|
|
||||||
// more coeffs from there ^^^^^^^^^^^^^ ?
|
|
||||||
impl BiquadCoefs {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
|
|
||||||
Self { b0, b1, b2, a1, a2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns settings for a Butterworth lowpass filter.
|
|
||||||
/// Cutoff is the -3 dB point of the filter in Hz.
|
|
||||||
#[inline]
|
|
||||||
pub fn butter_lowpass(sample_rate: f32, cutoff: f32) -> BiquadCoefs {
|
|
||||||
let f = (cutoff * PI / sample_rate).tan();
|
|
||||||
let a0r = 1.0 / (1.0 + SQRT_2 * f + f * f);
|
|
||||||
let a1 = (2.0 * f * f - 2.0) * a0r;
|
|
||||||
let a2 = (1.0 - SQRT_2 * f + f * f) * a0r;
|
|
||||||
let b0 = f * f * a0r;
|
|
||||||
let b1 = 2.0 * b0;
|
|
||||||
let b2 = b0;
|
|
||||||
BiquadCoefs { a1, a2, b0, b1, b2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the Q for cascading a butterworth filter:
|
|
||||||
fn calc_cascaded_butter_q(order: usize, casc_idx: usize) -> f32 {
|
|
||||||
let order = order as f32;
|
|
||||||
let casc_idx = casc_idx as f32;
|
|
||||||
|
|
||||||
let b = -2.0 * ((2.0 * casc_idx + order - 1.0) * PI / (2.0 * order)).cos();
|
|
||||||
|
|
||||||
1.0 / b
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns settings for a lowpass filter with a specific q
|
|
||||||
#[inline]
|
|
||||||
pub fn lowpass(sample_rate: f32, q: f32, cutoff: f32) -> BiquadCoefs {
|
|
||||||
let f = (cutoff * PI / sample_rate).tan();
|
|
||||||
let a0r = 1.0 / (1.0 + f / q + f * f);
|
|
||||||
|
|
||||||
/*
|
|
||||||
float norm = 1.f / (1.f + K / Q + K * K);
|
|
||||||
this->b[0] = K * K * norm;
|
|
||||||
this->b[1] = 2.f * this->b[0];
|
|
||||||
this->b[2] = this->b[0];
|
|
||||||
this->a[1] = 2.f * (K * K - 1.f) * norm;
|
|
||||||
this->a[2] = (1.f - K / Q + K * K) * norm;
|
|
||||||
*/
|
|
||||||
|
|
||||||
let b0 = f * f * a0r;
|
|
||||||
let b1 = 2.0 * b0;
|
|
||||||
let b2 = b0;
|
|
||||||
let a1 = 2.0 * (f * f - 1.0) * a0r;
|
|
||||||
let a2 = (1.0 - f / q + f * f) * a0r;
|
|
||||||
|
|
||||||
BiquadCoefs { a1, a2, b0, b1, b2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns settings for a constant-gain bandpass resonator.
|
|
||||||
/// The center frequency is given in Hz.
|
|
||||||
/// Bandwidth is the difference in Hz between -3 dB points of the filter response.
|
|
||||||
/// The overall gain of the filter is independent of bandwidth.
|
|
||||||
pub fn resonator(sample_rate: f32, center: f32, bandwidth: f32) -> BiquadCoefs {
|
|
||||||
let r = (-PI * bandwidth / sample_rate).exp();
|
|
||||||
let a1 = -2.0 * r * (TAU * center / sample_rate).cos();
|
|
||||||
let a2 = r * r;
|
|
||||||
let b0 = (1.0 - r * r).sqrt() * 0.5;
|
|
||||||
let b1 = 0.0;
|
|
||||||
let b2 = -b0;
|
|
||||||
BiquadCoefs { a1, a2, b0, b1, b2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// /// Frequency response at frequency `omega` expressed as fraction of sampling rate.
|
|
||||||
// pub fn response(&self, omega: f64) -> Complex64 {
|
|
||||||
// let z1 = Complex64::from_polar(1.0, -TAU * omega);
|
|
||||||
// let z2 = Complex64::from_polar(1.0, -2.0 * TAU * omega);
|
|
||||||
// (re(self.b0) + re(self.b1) * z1 + re(self.b2) * z2)
|
|
||||||
// / (re(1.0) + re(self.a1) * z1 + re(self.a2) * z2)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2nd order IIR filter implemented in normalized Direct Form I.
|
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
|
||||||
pub struct Biquad {
|
|
||||||
coefs: BiquadCoefs,
|
|
||||||
x1: f32,
|
|
||||||
x2: f32,
|
|
||||||
y1: f32,
|
|
||||||
y2: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Biquad {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new_with(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
|
|
||||||
let mut s = Self::new();
|
|
||||||
s.set_coefs(BiquadCoefs::new(b0, b1, b2, a1, a2));
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn coefs(&self) -> &BiquadCoefs {
|
|
||||||
&self.coefs
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_coefs(&mut self, coefs: BiquadCoefs) {
|
|
||||||
self.coefs = coefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.x1 = 0.0;
|
|
||||||
self.x2 = 0.0;
|
|
||||||
self.y1 = 0.0;
|
|
||||||
self.y2 = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn tick(&mut self, input: f32) -> f32 {
|
|
||||||
let x0 = input;
|
|
||||||
let y0 = self.coefs.b0 * x0 + self.coefs.b1 * self.x1 + self.coefs.b2 * self.x2
|
|
||||||
- self.coefs.a1 * self.y1
|
|
||||||
- self.coefs.a2 * self.y2;
|
|
||||||
self.x2 = self.x1;
|
|
||||||
self.x1 = x0;
|
|
||||||
self.y2 = self.y1;
|
|
||||||
self.y1 = y0;
|
|
||||||
y0
|
|
||||||
|
|
||||||
// Transposed Direct Form II would be:
|
|
||||||
// y0 = b0 * x0 + s1
|
|
||||||
// s1 = s2 + b1 * x0 - a1 * y0
|
|
||||||
// s2 = b2 * x0 - a2 * y0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ButterLowpass {
|
|
||||||
biquad: Biquad,
|
|
||||||
sample_rate: f32,
|
|
||||||
cutoff: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl ButterLowpass {
|
|
||||||
pub fn new(sample_rate: f32, cutoff: f32) -> Self {
|
|
||||||
let mut this = ButterLowpass { biquad: Biquad::new(), sample_rate, cutoff: 0.0 };
|
|
||||||
this.set_cutoff(cutoff);
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cutoff(&mut self, cutoff: f32) {
|
|
||||||
self.biquad.set_coefs(BiquadCoefs::butter_lowpass(self.sample_rate, cutoff));
|
|
||||||
self.cutoff = cutoff;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_sample_rate(&mut self, srate: f32) {
|
|
||||||
self.sample_rate = srate;
|
|
||||||
self.reset();
|
|
||||||
self.biquad.reset();
|
|
||||||
self.set_cutoff(self.cutoff);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.biquad.reset();
|
|
||||||
self.set_cutoff(self.cutoff);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn tick(&mut self, input: f32) -> f32 {
|
|
||||||
self.biquad.tick(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loosely adapted from https://github.com/VCVRack/Befaco/blob/v1/src/ChowDSP.hpp
|
|
||||||
// Copyright (c) 2019-2020 Andrew Belt and Befaco contributors
|
|
||||||
// Under GPLv-3.0-or-later
|
|
||||||
//
|
|
||||||
// Which was originally taken from https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp
|
|
||||||
// Copyright (c) 2020 jatinchowdhury18
|
|
||||||
/// Implements oversampling with a ratio of N and a 4 times cascade
|
|
||||||
/// of Butterworth lowpass filters (~48dB?).
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Oversampling<const N: usize> {
|
|
||||||
filters: [Biquad; 4],
|
|
||||||
buffer: [f32; N],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<const N: usize> Oversampling<N> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut this = Self { filters: [Biquad::new(); 4], buffer: [0.0; N] };
|
|
||||||
|
|
||||||
this.set_sample_rate(44100.0);
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.buffer = [0.0; N];
|
|
||||||
for filt in &mut self.filters {
|
|
||||||
filt.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_sample_rate(&mut self, srate: f32) {
|
|
||||||
let cutoff = 0.98 * (0.5 * srate);
|
|
||||||
|
|
||||||
let ovr_srate = (N as f32) * srate;
|
|
||||||
let filters_len = self.filters.len();
|
|
||||||
|
|
||||||
for (i, filt) in self.filters.iter_mut().enumerate() {
|
|
||||||
let q = BiquadCoefs::calc_cascaded_butter_q(2 * 4, filters_len - i);
|
|
||||||
|
|
||||||
filt.set_coefs(BiquadCoefs::lowpass(ovr_srate, q, cutoff));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn upsample(&mut self, v: f32) {
|
|
||||||
self.buffer.fill(0.0);
|
|
||||||
self.buffer[0] = (N as f32) * v;
|
|
||||||
|
|
||||||
for s in &mut self.buffer {
|
|
||||||
for filt in &mut self.filters {
|
|
||||||
*s = filt.tick(*s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn resample_buffer(&mut self) -> &mut [f32; N] {
|
|
||||||
&mut self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn downsample(&mut self) -> f32 {
|
|
||||||
let mut ret = 0.0;
|
|
||||||
for s in &mut self.buffer {
|
|
||||||
ret = *s;
|
|
||||||
for filt in &mut self.filters {
|
|
||||||
ret = filt.tick(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,443 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// This file contains a reverb implementation that is based
|
|
||||||
// on Jon Dattorro's 1997 reverb algorithm. It's also largely
|
|
||||||
// based on the C++ implementation from ValleyAudio / ValleyRackFree
|
|
||||||
//
|
|
||||||
// ValleyRackFree Copyright (C) 2020, Valley Audio Soft, Dale Johnson
|
|
||||||
// Adapted under the GPL-3.0-or-later License.
|
|
||||||
//
|
|
||||||
// See also: https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Plateau/Dattorro.cpp
|
|
||||||
// and: https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Plateau/Dattorro.hpp
|
|
||||||
//
|
|
||||||
// And: https://ccrma.stanford.edu/~dattorro/music.html
|
|
||||||
// And: https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf
|
|
||||||
|
|
||||||
use crate::dsp::helpers::crossfade;
|
|
||||||
|
|
||||||
const DAT_SAMPLE_RATE: f64 = 29761.0;
|
|
||||||
const DAT_SAMPLES_PER_MS: f64 = DAT_SAMPLE_RATE / 1000.0;
|
|
||||||
|
|
||||||
const DAT_INPUT_APF_TIMES_MS: [f64; 4] = [
|
|
||||||
141.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
107.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
379.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
277.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
];
|
|
||||||
|
|
||||||
const DAT_LEFT_APF1_TIME_MS: f64 = 672.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
const DAT_LEFT_APF2_TIME_MS: f64 = 1800.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
|
|
||||||
const DAT_RIGHT_APF1_TIME_MS: f64 = 908.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
const DAT_RIGHT_APF2_TIME_MS: f64 = 2656.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
|
|
||||||
const DAT_LEFT_DELAY1_TIME_MS: f64 = 4453.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
const DAT_LEFT_DELAY2_TIME_MS: f64 = 3720.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
|
|
||||||
const DAT_RIGHT_DELAY1_TIME_MS: f64 = 4217.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
const DAT_RIGHT_DELAY2_TIME_MS: f64 = 3163.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
|
|
||||||
const DAT_LEFT_TAPS_TIME_MS: [f64; 7] = [
|
|
||||||
266.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
2974.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
1913.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
1996.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
1990.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
187.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
1066.0 / DAT_SAMPLES_PER_MS,
|
|
||||||
];
|
|
||||||
|
|
||||||
const DAT_RIGHT_TAPS_TIME_MS: [f64; 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: [f64; 4] = [0.1, 0.15, 0.12, 0.18];
|
|
||||||
|
|
||||||
const DAT_INPUT_DIFFUSION1: f64 = 0.75;
|
|
||||||
const DAT_INPUT_DIFFUSION2: f64 = 0.625;
|
|
||||||
const DAT_PLATE_DIFFUSION1: f64 = 0.7;
|
|
||||||
const DAT_PLATE_DIFFUSION2: f64 = 0.5;
|
|
||||||
|
|
||||||
const DAT_LFO_EXCURSION_MS: f64 = 16.0 / DAT_SAMPLES_PER_MS;
|
|
||||||
const DAT_LFO_EXCURSION_MOD_MAX: f64 = 16.0;
|
|
||||||
|
|
||||||
use crate::dsp::helpers::{AllPass, DCBlockFilter, DelayBuffer, OnePoleHPF, OnePoleLPF, TriSawLFO};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct DattorroReverb {
|
|
||||||
last_scale: f64,
|
|
||||||
|
|
||||||
inp_dc_block: [DCBlockFilter<f64>; 2],
|
|
||||||
out_dc_block: [DCBlockFilter<f64>; 2],
|
|
||||||
|
|
||||||
lfos: [TriSawLFO<f64>; 4],
|
|
||||||
|
|
||||||
input_hpf: OnePoleHPF<f64>,
|
|
||||||
input_lpf: OnePoleLPF<f64>,
|
|
||||||
|
|
||||||
pre_delay: DelayBuffer<f64>,
|
|
||||||
input_apfs: [(AllPass<f64>, f64, f64); 4],
|
|
||||||
|
|
||||||
apf1: [(AllPass<f64>, f64, f64); 2],
|
|
||||||
hpf: [OnePoleHPF<f64>; 2],
|
|
||||||
lpf: [OnePoleLPF<f64>; 2],
|
|
||||||
apf2: [(AllPass<f64>, f64, f64); 2],
|
|
||||||
delay1: [(DelayBuffer<f64>, f64); 2],
|
|
||||||
delay2: [(DelayBuffer<f64>, f64); 2],
|
|
||||||
|
|
||||||
left_sum: f64,
|
|
||||||
right_sum: f64,
|
|
||||||
|
|
||||||
dbg_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
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) -> f64;
|
|
||||||
/// The size of the reverb, values go from 0.0 to 1.0.
|
|
||||||
fn time_scale(&self) -> f64;
|
|
||||||
/// High-pass input filter cutoff freq in Hz, range: 0.0 to 22000.0
|
|
||||||
fn input_high_cutoff_hz(&self) -> f64;
|
|
||||||
/// Low-pass input filter cutoff freq in Hz, range: 0.0 to 22000.0
|
|
||||||
fn input_low_cutoff_hz(&self) -> f64;
|
|
||||||
/// High-pass reverb filter cutoff freq in Hz, range: 0.0 to 22000.0
|
|
||||||
fn reverb_high_cutoff_hz(&self) -> f64;
|
|
||||||
/// Low-pass reverb filter cutoff freq in Hz, range: 0.0 to 22000.0
|
|
||||||
fn reverb_low_cutoff_hz(&self) -> f64;
|
|
||||||
/// Modulation speed factor, range: 0.0 to 1.0
|
|
||||||
fn mod_speed(&self) -> f64;
|
|
||||||
/// Modulation depth from the LFOs, range: 0.0 to 1.0
|
|
||||||
fn mod_depth(&self) -> f64;
|
|
||||||
/// Modulation shape (from saw to tri to saw), range: 0.0 to 1.0
|
|
||||||
fn mod_shape(&self) -> f64;
|
|
||||||
/// The mix between output from the pre-delay and the input diffusion.
|
|
||||||
/// range: 0.0 to 1.0. Default should be 1.0
|
|
||||||
fn input_diffusion_mix(&self) -> f64;
|
|
||||||
/// The amount of plate diffusion going on, range: 0.0 to 1.0
|
|
||||||
fn diffusion(&self) -> f64;
|
|
||||||
/// Internal tank decay time, range: 0.0 to 1.0
|
|
||||||
fn decay(&self) -> f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DattorroReverb {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut this = Self {
|
|
||||||
last_scale: 1.0,
|
|
||||||
|
|
||||||
inp_dc_block: [DCBlockFilter::new(); 2],
|
|
||||||
out_dc_block: [DCBlockFilter::new(); 2],
|
|
||||||
|
|
||||||
lfos: [TriSawLFO::new(); 4],
|
|
||||||
|
|
||||||
input_hpf: OnePoleHPF::new(),
|
|
||||||
input_lpf: OnePoleLPF::new(),
|
|
||||||
|
|
||||||
pre_delay: DelayBuffer::new(),
|
|
||||||
input_apfs: Default::default(),
|
|
||||||
|
|
||||||
apf1: Default::default(),
|
|
||||||
hpf: [OnePoleHPF::new(); 2],
|
|
||||||
lpf: [OnePoleLPF::new(); 2],
|
|
||||||
apf2: Default::default(),
|
|
||||||
delay1: Default::default(),
|
|
||||||
delay2: Default::default(),
|
|
||||||
|
|
||||||
left_sum: 0.0,
|
|
||||||
right_sum: 0.0,
|
|
||||||
|
|
||||||
dbg_count: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
|
||||||
self.input_lpf.reset();
|
|
||||||
self.input_hpf.reset();
|
|
||||||
|
|
||||||
self.input_lpf.set_freq(22000.0);
|
|
||||||
self.input_hpf.set_freq(0.0);
|
|
||||||
|
|
||||||
self.input_apfs[0] = (AllPass::new(), DAT_INPUT_APF_TIMES_MS[0], DAT_INPUT_DIFFUSION1);
|
|
||||||
self.input_apfs[1] = (AllPass::new(), DAT_INPUT_APF_TIMES_MS[1], DAT_INPUT_DIFFUSION1);
|
|
||||||
self.input_apfs[2] = (AllPass::new(), DAT_INPUT_APF_TIMES_MS[2], DAT_INPUT_DIFFUSION2);
|
|
||||||
self.input_apfs[3] = (AllPass::new(), DAT_INPUT_APF_TIMES_MS[3], DAT_INPUT_DIFFUSION2);
|
|
||||||
|
|
||||||
self.apf1[0] = (AllPass::new(), DAT_LEFT_APF1_TIME_MS, -DAT_PLATE_DIFFUSION1);
|
|
||||||
self.apf1[1] = (AllPass::new(), DAT_RIGHT_APF1_TIME_MS, -DAT_PLATE_DIFFUSION1);
|
|
||||||
self.apf2[0] = (AllPass::new(), DAT_LEFT_APF2_TIME_MS, -DAT_PLATE_DIFFUSION2);
|
|
||||||
self.apf2[1] = (AllPass::new(), DAT_RIGHT_APF2_TIME_MS, -DAT_PLATE_DIFFUSION2);
|
|
||||||
|
|
||||||
self.delay1[0] = (DelayBuffer::new(), DAT_LEFT_DELAY1_TIME_MS);
|
|
||||||
self.delay1[1] = (DelayBuffer::new(), DAT_RIGHT_DELAY1_TIME_MS);
|
|
||||||
self.delay2[0] = (DelayBuffer::new(), DAT_LEFT_DELAY2_TIME_MS);
|
|
||||||
self.delay2[1] = (DelayBuffer::new(), DAT_RIGHT_DELAY2_TIME_MS);
|
|
||||||
|
|
||||||
self.lpf[0].reset();
|
|
||||||
self.lpf[1].reset();
|
|
||||||
self.lpf[0].set_freq(10000.0);
|
|
||||||
self.lpf[1].set_freq(10000.0);
|
|
||||||
|
|
||||||
self.hpf[0].reset();
|
|
||||||
self.hpf[1].reset();
|
|
||||||
self.hpf[0].set_freq(0.0);
|
|
||||||
self.hpf[1].set_freq(0.0);
|
|
||||||
|
|
||||||
self.lfos[0].set(DAT_LFO_FREQS_HZ[0], 0.5);
|
|
||||||
self.lfos[0].set_phase_offs(0.0);
|
|
||||||
self.lfos[0].reset();
|
|
||||||
self.lfos[1].set(DAT_LFO_FREQS_HZ[1], 0.5);
|
|
||||||
self.lfos[1].set_phase_offs(0.25);
|
|
||||||
self.lfos[1].reset();
|
|
||||||
self.lfos[2].set(DAT_LFO_FREQS_HZ[2], 0.5);
|
|
||||||
self.lfos[2].set_phase_offs(0.5);
|
|
||||||
self.lfos[2].reset();
|
|
||||||
self.lfos[3].set(DAT_LFO_FREQS_HZ[3], 0.5);
|
|
||||||
self.lfos[3].set_phase_offs(0.75);
|
|
||||||
self.lfos[3].reset();
|
|
||||||
|
|
||||||
self.inp_dc_block[0].reset();
|
|
||||||
self.inp_dc_block[1].reset();
|
|
||||||
self.out_dc_block[0].reset();
|
|
||||||
self.out_dc_block[1].reset();
|
|
||||||
|
|
||||||
self.pre_delay.reset();
|
|
||||||
|
|
||||||
self.left_sum = 0.0;
|
|
||||||
self.right_sum = 0.0;
|
|
||||||
|
|
||||||
self.set_time_scale(1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_time_scale(&mut self, scale: f64) {
|
|
||||||
if (self.last_scale - scale).abs() > std::f64::EPSILON {
|
|
||||||
let scale = scale.max(0.1);
|
|
||||||
self.last_scale = scale;
|
|
||||||
|
|
||||||
self.apf1[0].1 = DAT_LEFT_APF1_TIME_MS * scale;
|
|
||||||
self.apf1[1].1 = DAT_RIGHT_APF1_TIME_MS * scale;
|
|
||||||
self.apf2[0].1 = DAT_LEFT_APF2_TIME_MS * scale;
|
|
||||||
self.apf2[1].1 = DAT_RIGHT_APF2_TIME_MS * scale;
|
|
||||||
|
|
||||||
self.delay1[0].1 = DAT_LEFT_DELAY1_TIME_MS * scale;
|
|
||||||
self.delay1[1].1 = DAT_RIGHT_DELAY1_TIME_MS * scale;
|
|
||||||
self.delay2[0].1 = DAT_LEFT_DELAY2_TIME_MS * scale;
|
|
||||||
self.delay2[1].1 = DAT_RIGHT_DELAY2_TIME_MS * scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_sample_rate(&mut self, srate: f64) {
|
|
||||||
self.inp_dc_block[0].set_sample_rate(srate);
|
|
||||||
self.inp_dc_block[1].set_sample_rate(srate);
|
|
||||||
self.out_dc_block[0].set_sample_rate(srate);
|
|
||||||
self.out_dc_block[1].set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.lfos[0].set_sample_rate(srate);
|
|
||||||
self.lfos[1].set_sample_rate(srate);
|
|
||||||
self.lfos[2].set_sample_rate(srate);
|
|
||||||
self.lfos[3].set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.input_hpf.set_sample_rate(srate);
|
|
||||||
self.input_lpf.set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.pre_delay.set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.input_apfs[0].0.set_sample_rate(srate);
|
|
||||||
self.input_apfs[1].0.set_sample_rate(srate);
|
|
||||||
self.input_apfs[2].0.set_sample_rate(srate);
|
|
||||||
self.input_apfs[3].0.set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.apf1[0].0.set_sample_rate(srate);
|
|
||||||
self.apf1[1].0.set_sample_rate(srate);
|
|
||||||
self.apf2[0].0.set_sample_rate(srate);
|
|
||||||
self.apf2[1].0.set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.hpf[0].set_sample_rate(srate);
|
|
||||||
self.hpf[1].set_sample_rate(srate);
|
|
||||||
self.lpf[0].set_sample_rate(srate);
|
|
||||||
self.lpf[1].set_sample_rate(srate);
|
|
||||||
|
|
||||||
self.delay1[0].0.set_sample_rate(srate);
|
|
||||||
self.delay1[1].0.set_sample_rate(srate);
|
|
||||||
self.delay2[0].0.set_sample_rate(srate);
|
|
||||||
self.delay2[1].0.set_sample_rate(srate);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn calc_apf_delay_times(
|
|
||||||
&mut self,
|
|
||||||
params: &mut dyn DattorroReverbParams,
|
|
||||||
) -> (f64, f64, f64, f64) {
|
|
||||||
let left_apf1_delay_ms = self.apf1[0].1
|
|
||||||
+ (self.lfos[0].next_bipolar() as f64
|
|
||||||
* DAT_LFO_EXCURSION_MS
|
|
||||||
* DAT_LFO_EXCURSION_MOD_MAX
|
|
||||||
* params.mod_depth());
|
|
||||||
let right_apf1_delay_ms = self.apf1[1].1
|
|
||||||
+ (self.lfos[1].next_bipolar() as f64
|
|
||||||
* DAT_LFO_EXCURSION_MS
|
|
||||||
* DAT_LFO_EXCURSION_MOD_MAX
|
|
||||||
* params.mod_depth());
|
|
||||||
let left_apf2_delay_ms = self.apf2[0].1
|
|
||||||
+ (self.lfos[2].next_bipolar() as f64
|
|
||||||
* DAT_LFO_EXCURSION_MS
|
|
||||||
* DAT_LFO_EXCURSION_MOD_MAX
|
|
||||||
* params.mod_depth());
|
|
||||||
let right_apf2_delay_ms = self.apf2[1].1
|
|
||||||
+ (self.lfos[3].next_bipolar() as f64
|
|
||||||
* DAT_LFO_EXCURSION_MS
|
|
||||||
* DAT_LFO_EXCURSION_MOD_MAX
|
|
||||||
* params.mod_depth());
|
|
||||||
|
|
||||||
(left_apf1_delay_ms, right_apf1_delay_ms, left_apf2_delay_ms, right_apf2_delay_ms)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process(
|
|
||||||
&mut self,
|
|
||||||
params: &mut dyn DattorroReverbParams,
|
|
||||||
input_l: f64,
|
|
||||||
input_r: f64,
|
|
||||||
) -> (f64, f64) {
|
|
||||||
// Some parameter setup...
|
|
||||||
let timescale = 0.1 + (4.0 - 0.1) * 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] * mod_speed, params.mod_shape());
|
|
||||||
self.lfos[1].set(DAT_LFO_FREQS_HZ[1] * mod_speed, params.mod_shape());
|
|
||||||
self.lfos[2].set(DAT_LFO_FREQS_HZ[2] * mod_speed, params.mod_shape());
|
|
||||||
self.lfos[3].set(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();
|
|
||||||
self.apf2[0].2 = DAT_PLATE_DIFFUSION2 * params.diffusion();
|
|
||||||
self.apf2[1].2 = DAT_PLATE_DIFFUSION2 * params.diffusion();
|
|
||||||
|
|
||||||
let (left_apf1_delay_ms, right_apf1_delay_ms, 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);
|
|
||||||
|
|
||||||
// Sum of DC outputs => LPF => HPF
|
|
||||||
self.input_lpf.set_freq(params.input_low_cutoff_hz());
|
|
||||||
self.input_hpf.set_freq(params.input_high_cutoff_hz());
|
|
||||||
let out_lpf = self.input_lpf.process(input_r + input_l);
|
|
||||||
let out_hpf = self.input_hpf.process(out_lpf);
|
|
||||||
|
|
||||||
// HPF => Pre-Delay
|
|
||||||
let out_pre_delay = if params.pre_delay_time_ms() < 0.1 {
|
|
||||||
out_hpf
|
|
||||||
} else {
|
|
||||||
self.pre_delay.next_cubic(params.pre_delay_time_ms(), out_hpf)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pre-Delay => 4 All-Pass filters
|
|
||||||
let mut diffused = out_pre_delay;
|
|
||||||
for (apf, time, g) in &mut self.input_apfs {
|
|
||||||
diffused = apf.next(*time, *g, diffused);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mix between diffused and pre-delayed intput for further processing
|
|
||||||
let tank_feed = crossfade(out_pre_delay, diffused, params.input_diffusion_mix());
|
|
||||||
|
|
||||||
// First tap for the output
|
|
||||||
self.left_sum += tank_feed;
|
|
||||||
self.right_sum += tank_feed;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// if self.dbg_count % 48 == 0 {
|
|
||||||
// println!("APFS dcy={:8.6}; {:8.6} {:8.6} {:8.6} {:8.6} | {:8.6} {:8.6} {:8.6} {:8.6}",
|
|
||||||
// decay,
|
|
||||||
// self.apf1[0].2,
|
|
||||||
// self.apf1[1].2,
|
|
||||||
// self.apf2[0].2,
|
|
||||||
// self.apf2[1].2,
|
|
||||||
// left_apf1_delay_ms, right_apf1_delay_ms,
|
|
||||||
// left_apf2_delay_ms, right_apf2_delay_ms);
|
|
||||||
// println!("DELY1/2 {:8.6} / {:8.6} | {:8.6} / {:8.6}",
|
|
||||||
// self.delay1[0].1,
|
|
||||||
// self.delay2[0].1,
|
|
||||||
// self.delay1[1].1,
|
|
||||||
// self.delay2[1].1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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_n(DAT_LEFT_TAPS_TIME_MS[0]);
|
|
||||||
left_accum += self.delay1[0].0.tap_n(DAT_LEFT_TAPS_TIME_MS[1]);
|
|
||||||
left_accum -= self.apf2[0].0.delay_tap_n(DAT_LEFT_TAPS_TIME_MS[2]);
|
|
||||||
left_accum += self.delay2[0].0.tap_n(DAT_LEFT_TAPS_TIME_MS[3]);
|
|
||||||
left_accum -= self.delay1[1].0.tap_n(DAT_LEFT_TAPS_TIME_MS[4]);
|
|
||||||
left_accum -= self.apf2[1].0.delay_tap_n(DAT_LEFT_TAPS_TIME_MS[5]);
|
|
||||||
left_accum -= self.delay2[1].0.tap_n(DAT_LEFT_TAPS_TIME_MS[6]);
|
|
||||||
|
|
||||||
let mut right_accum = right_apf_tap;
|
|
||||||
right_accum += self.delay1[1].0.tap_n(DAT_RIGHT_TAPS_TIME_MS[0]);
|
|
||||||
right_accum += self.delay1[1].0.tap_n(DAT_RIGHT_TAPS_TIME_MS[1]);
|
|
||||||
right_accum -= self.apf2[1].0.delay_tap_n(DAT_RIGHT_TAPS_TIME_MS[2]);
|
|
||||||
right_accum += self.delay2[1].0.tap_n(DAT_RIGHT_TAPS_TIME_MS[3]);
|
|
||||||
right_accum -= self.delay1[0].0.tap_n(DAT_RIGHT_TAPS_TIME_MS[4]);
|
|
||||||
right_accum -= self.apf2[0].0.delay_tap_n(DAT_RIGHT_TAPS_TIME_MS[5]);
|
|
||||||
right_accum -= self.delay2[0].0.tap_n(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);
|
|
||||||
|
|
||||||
self.dbg_count += 1;
|
|
||||||
|
|
||||||
(left_out * 0.5, right_out * 0.5)
|
|
||||||
}
|
|
||||||
}
|
|
2697
src/dsp/helpers.rs
2697
src/dsp/helpers.rs
File diff suppressed because it is too large
Load diff
|
@ -200,14 +200,15 @@ the help documentation:
|
||||||
For non trivial DSP nodes, the DSP code itself should be separate from it's `dsp/node_*.rs`
|
For non trivial DSP nodes, the DSP code itself should be separate from it's `dsp/node_*.rs`
|
||||||
file. That file should only care about interfacing the DSP code with HexoDSP, but not implement
|
file. That file should only care about interfacing the DSP code with HexoDSP, but not implement
|
||||||
all the complicated DSP code. It's good practice to factor out the DSP code into
|
all the complicated DSP code. It's good practice to factor out the DSP code into
|
||||||
a separate module or file.
|
a separate module or file. It is preferable to add your custom DSP code to the `synfx-dsp`
|
||||||
|
crate [synfx-dsp](https://github.com/WeirdConstructor/synfx-dsp).
|
||||||
|
|
||||||
Look at `node_tslfo.rs` for instance. It wires up the `TriSawLFO` from `dsp/helpers.rs`
|
Look at `node_tslfo.rs` for instance. It wires up the `TriSawLFO` from `synfx-dsp`
|
||||||
to the HexoDSP node interface.
|
to the HexoDSP node interface.
|
||||||
|
|
||||||
```ignore
|
```ignore
|
||||||
// node_tslfo.rs
|
// node_tslfo.rs
|
||||||
use super::helpers::{TriSawLFO, Trigger};
|
use synfx_dsp::{TriSawLFO, Trigger};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TsLFO {
|
pub struct TsLFO {
|
||||||
|
@ -233,7 +234,7 @@ to the HexoDSP node interface.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The code for `TriSawLFO` in `dsp/helpers.rs` is then independent and reusable else where.
|
The code for `TriSawLFO` in `synfx-dsp` is then independent and reusable else where.
|
||||||
|
|
||||||
### Node Parameter/Inputs
|
### Node Parameter/Inputs
|
||||||
|
|
||||||
|
@ -540,9 +541,6 @@ mod node_vosc;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
mod node_code;
|
mod node_code;
|
||||||
|
|
||||||
pub mod biquad;
|
|
||||||
pub mod dattorro;
|
|
||||||
pub mod helpers;
|
|
||||||
mod satom;
|
mod satom;
|
||||||
pub mod tracker;
|
pub mod tracker;
|
||||||
|
|
||||||
|
@ -566,7 +564,7 @@ use crate::fa_cqnt;
|
||||||
use crate::fa_cqnt_omax;
|
use crate::fa_cqnt_omax;
|
||||||
use crate::fa_cqnt_omin;
|
use crate::fa_cqnt_omin;
|
||||||
use crate::fa_delay_mode;
|
use crate::fa_delay_mode;
|
||||||
use crate::fa_distort;
|
use synfx_dsp::fa_distort;
|
||||||
use crate::fa_map_clip;
|
use crate::fa_map_clip;
|
||||||
use crate::fa_mux9_in_cnt;
|
use crate::fa_mux9_in_cnt;
|
||||||
use crate::fa_noise_mode;
|
use crate::fa_noise_mode;
|
||||||
|
@ -1599,7 +1597,7 @@ fn rand_node_satisfies_spec(nid: NodeId, sel: RandNodeSelector) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_rand_node_id(count: usize, sel: RandNodeSelector) -> Vec<NodeId> {
|
pub fn get_rand_node_id(count: usize, sel: RandNodeSelector) -> Vec<NodeId> {
|
||||||
let mut sm = crate::dsp::helpers::SplitMix64::new_time_seed();
|
let mut sm = synfx_dsp::SplitMix64::new_time_seed();
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
let mut cnt = 0;
|
let mut cnt = 0;
|
||||||
|
@ -1979,7 +1977,7 @@ macro_rules! make_node_info_enum {
|
||||||
1 => 0.05,
|
1 => 0.05,
|
||||||
2 => 0.1,
|
2 => 0.1,
|
||||||
// 0.25 just to protect against sine cancellation
|
// 0.25 just to protect against sine cancellation
|
||||||
_ => crate::dsp::helpers::rand_01() * 0.25
|
_ => synfx_dsp::rand_01() * 0.25
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
|
use synfx_dsp::{sqrt4_to_pow4, TrigSignal, Trigger};
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::AllPass;
|
use synfx_dsp::AllPass;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::biquad::*;
|
use synfx_dsp::*;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::PolyBlepOscillator;
|
use synfx_dsp::PolyBlepOscillator;
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::biquad::Biquad;
|
use synfx_dsp::{DelayBuffer, FixedOnePole, Biquad};
|
||||||
use crate::dsp::helpers::{DelayBuffer, FixedOnePole};
|
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
denorm, denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
denorm, denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers;
|
use synfx_dsp;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
@ -21,12 +21,12 @@ macro_rules! fa_comb_mode {
|
||||||
/// A simple amplifier
|
/// A simple amplifier
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Comb {
|
pub struct Comb {
|
||||||
comb: Box<helpers::Comb>,
|
comb: Box<synfx_dsp::Comb>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Comb {
|
impl Comb {
|
||||||
pub fn new(_nid: &NodeId) -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self { comb: Box::new(helpers::Comb::new()) }
|
Self { comb: Box::new(synfx_dsp::Comb::new()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const inp: &'static str = "Comb inp\nThe signal input for the comb filter.\nRange: (-1..1)";
|
pub const inp: &'static str = "Comb inp\nThe signal input for the comb filter.\nRange: (-1..1)";
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{ChangeTrig, CtrlPitchQuantizer};
|
use synfx_dsp::{ChangeTrig, CtrlPitchQuantizer};
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{crossfade, DelayBuffer, TriggerSampleClock};
|
use synfx_dsp::{crossfade, DelayBuffer, TriggerSampleClock};
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::Trigger;
|
use synfx_dsp::Trigger;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::Rng;
|
use synfx_dsp::Rng;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use super::dattorro::{DattorroReverb, DattorroReverbParams};
|
use synfx_dsp::{DattorroReverb, DattorroReverbParams, crossfade};
|
||||||
use super::helpers::crossfade;
|
|
||||||
use crate::dsp::{denorm, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{denorm, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{ChangeTrig, Quantizer};
|
use synfx_dsp::{ChangeTrig, Quantizer};
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{Rng, SlewValue, Trigger};
|
use synfx_dsp::{Rng, SlewValue, Trigger};
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use super::helpers::{cubic_interpolate, Trigger};
|
use synfx_dsp::{cubic_interpolate, Trigger};
|
||||||
use crate::dsp::{at, denorm, denorm_offs, inp, out}; //, inp, denorm, denorm_v, inp_dir, at};
|
use crate::dsp::{at, denorm, denorm_offs, inp, out}; //, inp, denorm, denorm_v, inp_dir, at};
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
// Copyright by Andrew Belt, 2021
|
// Copyright by Andrew Belt, 2021
|
||||||
|
|
||||||
//use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
|
//use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
|
||||||
use crate::dsp::helpers::CustomTrigger;
|
use synfx_dsp::CustomTrigger;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::SCOPE_SAMPLES;
|
use crate::nodes::SCOPE_SAMPLES;
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{
|
use synfx_dsp::{
|
||||||
process_1pole_highpass, process_1pole_lowpass, process_1pole_tpt_highpass,
|
process_1pole_highpass, process_1pole_lowpass, process_1pole_tpt_highpass,
|
||||||
process_1pole_tpt_lowpass, process_hal_chamberlin_svf, process_simper_svf,
|
process_1pole_tpt_lowpass, process_hal_chamberlin_svf, process_simper_svf,
|
||||||
process_stilson_moog,
|
process_stilson_moog,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::fast_sin;
|
use synfx_dsp::fast_sin;
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
denorm_offs, inp, out, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::TrigSignal;
|
use synfx_dsp::TrigSignal;
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::helpers::{Trigger, TriggerPhaseClock};
|
use synfx_dsp::{Trigger, TriggerPhaseClock};
|
||||||
use crate::dsp::tracker::TrackerBackend;
|
use crate::dsp::tracker::TrackerBackend;
|
||||||
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use super::helpers::{TriSawLFO, Trigger};
|
use synfx_dsp::{TriSawLFO, Trigger};
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use crate::dsp::biquad::Oversampling;
|
use synfx_dsp::{Oversampling, apply_distortion, VPSOscillator};
|
||||||
use crate::dsp::helpers::{apply_distortion, VPSOscillator};
|
|
||||||
use crate::dsp::{
|
use crate::dsp::{
|
||||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use super::MAX_COLS;
|
use super::MAX_COLS;
|
||||||
use super::MAX_PATTERN_LEN;
|
use super::MAX_PATTERN_LEN;
|
||||||
use crate::dsp::helpers::SplitMix64;
|
use synfx_dsp::SplitMix64;
|
||||||
|
|
||||||
pub struct PatternSequencer {
|
pub struct PatternSequencer {
|
||||||
rows: usize,
|
rows: usize,
|
||||||
|
|
|
@ -90,7 +90,7 @@ pub fn new_node_engine() -> (NodeConfigurator, NodeExecutor) {
|
||||||
|
|
||||||
// XXX: This is one of the earliest and most consistent points
|
// XXX: This is one of the earliest and most consistent points
|
||||||
// in runtime to do this kind of initialization:
|
// in runtime to do this kind of initialization:
|
||||||
crate::dsp::helpers::init_cos_tab();
|
synfx_dsp::init_cos_tab();
|
||||||
|
|
||||||
(nc, ne)
|
(nc, ne)
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,6 +191,7 @@ pub struct NodeConfigurator {
|
||||||
/// Holds the block functions that are JIT compiled to DSP code
|
/// Holds the block functions that are JIT compiled to DSP code
|
||||||
/// for the `Code` nodes. The code is then sent via the [CodeEngine]
|
/// for the `Code` nodes. The code is then sent via the [CodeEngine]
|
||||||
/// in [check_block_function].
|
/// in [check_block_function].
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
pub(crate) block_functions: Vec<(u64, Arc<Mutex<BlockFun>>)>,
|
pub(crate) block_functions: Vec<(u64, Arc<Mutex<BlockFun>>)>,
|
||||||
/// The shared parts of the [NodeConfigurator]
|
/// The shared parts of the [NodeConfigurator]
|
||||||
/// and the [crate::nodes::NodeExecutor].
|
/// and the [crate::nodes::NodeExecutor].
|
||||||
|
@ -281,13 +282,18 @@ impl NodeConfigurator {
|
||||||
let mut scopes = vec![];
|
let mut scopes = vec![];
|
||||||
scopes.resize_with(MAX_SCOPES, || ScopeHandle::new_shared());
|
scopes.resize_with(MAX_SCOPES, || ScopeHandle::new_shared());
|
||||||
|
|
||||||
let code_engines = vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES];
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
|
let (code_engines, block_functions) = {
|
||||||
|
let code_engines = vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES];
|
||||||
|
|
||||||
let lang = blocklang_def::setup_hxdsp_block_language(code_engines[0].get_lib());
|
let lang = blocklang_def::setup_hxdsp_block_language(code_engines[0].get_lib());
|
||||||
let mut block_functions = vec![];
|
let mut block_functions = vec![];
|
||||||
block_functions.resize_with(MAX_AVAIL_CODE_ENGINES, || {
|
block_functions.resize_with(MAX_AVAIL_CODE_ENGINES, || {
|
||||||
(0, Arc::new(Mutex::new(BlockFun::new(lang.clone()))))
|
(0, Arc::new(Mutex::new(BlockFun::new(lang.clone()))))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(code_engines, block_functions)
|
||||||
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
NodeConfigurator {
|
NodeConfigurator {
|
||||||
|
@ -307,6 +313,7 @@ impl NodeConfigurator {
|
||||||
trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS],
|
trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS],
|
||||||
#[cfg(feature = "synfx-dsp-jit")]
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
code_engines,
|
code_engines,
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
block_functions,
|
block_functions,
|
||||||
scopes,
|
scopes,
|
||||||
},
|
},
|
||||||
|
@ -698,6 +705,7 @@ impl NodeConfigurator {
|
||||||
/// updates are then sent to the audio thread.
|
/// updates are then sent to the audio thread.
|
||||||
/// See also [get_block_function].
|
/// See also [get_block_function].
|
||||||
pub fn check_block_function(&mut self, id: usize) -> Result<(), BlkJITCompileError> {
|
pub fn check_block_function(&mut self, id: usize) -> Result<(), BlkJITCompileError> {
|
||||||
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
if let Some((generation, block_fun)) = self.block_functions.get_mut(id) {
|
if let Some((generation, block_fun)) = self.block_functions.get_mut(id) {
|
||||||
if let Ok(block_fun) = block_fun.lock() {
|
if let Ok(block_fun) = block_fun.lock() {
|
||||||
if *generation != block_fun.generation() {
|
if *generation != block_fun.generation() {
|
||||||
|
@ -723,7 +731,14 @@ impl NodeConfigurator {
|
||||||
/// Retrieve a handle to the block function `id`. In case you modify the block function,
|
/// Retrieve a handle to the block function `id`. In case you modify the block function,
|
||||||
/// make sure to call [check_block_function].
|
/// make sure to call [check_block_function].
|
||||||
pub fn get_block_function(&self, id: usize) -> Option<Arc<Mutex<BlockFun>>> {
|
pub fn get_block_function(&self, id: usize) -> Option<Arc<Mutex<BlockFun>>> {
|
||||||
self.block_functions.get(id).map(|pair| pair.1.clone())
|
#[cfg(feature = "synfx-dsp-jit")]
|
||||||
|
{
|
||||||
|
self.block_functions.get(id).map(|pair| pair.1.clone())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "synfx-dsp-jit"))]
|
||||||
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_nodes(&mut self) {
|
pub fn delete_nodes(&mut self) {
|
||||||
|
|
|
@ -1,255 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
mod common;
|
|
||||||
use common::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_delaybuffer_linear_interpolation() {
|
|
||||||
let mut buf = crate::helpers::DelayBuffer::new();
|
|
||||||
|
|
||||||
buf.feed(0.0);
|
|
||||||
buf.feed(0.1);
|
|
||||||
buf.feed(0.2);
|
|
||||||
buf.feed(0.3);
|
|
||||||
buf.feed(0.4);
|
|
||||||
buf.feed(0.5);
|
|
||||||
buf.feed(0.6);
|
|
||||||
buf.feed(0.7);
|
|
||||||
buf.feed(0.8);
|
|
||||||
buf.feed(0.9);
|
|
||||||
buf.feed(1.0);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.5;
|
|
||||||
for _ in 0..20 {
|
|
||||||
samples_out.push(buf.linear_interpolate_at_s(pos));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7, 0.65, 0.6, 0.55, 0.5, 0.45, 0.4, 0.35000002, 0.3,
|
|
||||||
0.25, 0.2, 0.15, 0.1, 0.05
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.2;
|
|
||||||
for _ in 0..30 {
|
|
||||||
samples_out.push(buf.linear_interpolate_at_s(pos));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 0.98, 0.96, 0.94, 0.91999996, 0.9, 0.88, 0.85999995, 0.84, 0.82, 0.8, 0.78, 0.76,
|
|
||||||
0.73999995, 0.71999997, 0.6999999, 0.67999995, 0.65999997, 0.6399999, 0.61999995,
|
|
||||||
0.59999996, 0.58, 0.56, 0.54, 0.52000004, 0.50000006, 0.48000008, 0.4600001,
|
|
||||||
0.44000012, 0.42000014
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_delaybuffer_nearest() {
|
|
||||||
let mut buf = crate::helpers::DelayBuffer::new();
|
|
||||||
|
|
||||||
buf.feed(0.0);
|
|
||||||
buf.feed(0.1);
|
|
||||||
buf.feed(0.2);
|
|
||||||
buf.feed(0.3);
|
|
||||||
buf.feed(0.4);
|
|
||||||
buf.feed(0.5);
|
|
||||||
buf.feed(0.6);
|
|
||||||
buf.feed(0.7);
|
|
||||||
buf.feed(0.8);
|
|
||||||
buf.feed(0.9);
|
|
||||||
buf.feed(1.0);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.5;
|
|
||||||
for _ in 0..20 {
|
|
||||||
samples_out.push(buf.at(pos as usize));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 1.0, 0.9, 0.9, 0.8, 0.8, 0.7, 0.7, 0.6, 0.6, 0.5, 0.5, 0.4, 0.4, 0.3, 0.3, 0.2,
|
|
||||||
0.2, 0.1, 0.1
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.2;
|
|
||||||
for _ in 0..30 {
|
|
||||||
samples_out.push(buf.at(pos as usize));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.8, 0.8, 0.8, 0.8, 0.7, 0.7,
|
|
||||||
0.7, 0.7, 0.7, 0.6, 0.6, 0.6, 0.6, 0.6, 0.5, 0.5, 0.5, 0.5, 0.5
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_cubic_interpolate() {
|
|
||||||
use crate::helpers::cubic_interpolate;
|
|
||||||
let data = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0];
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0_f32;
|
|
||||||
let pos_inc = 0.5_f32;
|
|
||||||
for _ in 0..30 {
|
|
||||||
let i = pos.floor() as usize;
|
|
||||||
let f = pos.fract();
|
|
||||||
samples_out.push(cubic_interpolate(&data[..], data.len(), i, f));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0,
|
|
||||||
1.01875,
|
|
||||||
0.9,
|
|
||||||
0.85,
|
|
||||||
0.8,
|
|
||||||
0.75,
|
|
||||||
0.7,
|
|
||||||
0.65,
|
|
||||||
0.6,
|
|
||||||
0.55,
|
|
||||||
0.5,
|
|
||||||
0.45,
|
|
||||||
0.4,
|
|
||||||
0.35000002,
|
|
||||||
0.3,
|
|
||||||
0.25,
|
|
||||||
0.2,
|
|
||||||
0.15,
|
|
||||||
0.1,
|
|
||||||
-0.018750004,
|
|
||||||
0.0,
|
|
||||||
0.49999997,
|
|
||||||
1.0,
|
|
||||||
1.01875,
|
|
||||||
0.9,
|
|
||||||
0.85,
|
|
||||||
0.8,
|
|
||||||
0.75,
|
|
||||||
0.7,
|
|
||||||
0.65
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0_f32;
|
|
||||||
let pos_inc = 0.1_f32;
|
|
||||||
for _ in 0..30 {
|
|
||||||
let i = pos.floor() as usize;
|
|
||||||
let f = pos.fract();
|
|
||||||
samples_out.push(cubic_interpolate(&data[..], data.len(), i, f));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 1.03455, 1.0504, 1.05085, 1.0392, 1.01875, 0.99279994, 0.9646499, 0.9375999,
|
|
||||||
0.91494995, 0.9, 0.89, 0.87999994, 0.86999995, 0.85999995, 0.84999996, 0.84, 0.83,
|
|
||||||
0.82, 0.80999994, 0.8, 0.79, 0.78000003, 0.77000004, 0.76, 0.75, 0.74, 0.73, 0.72,
|
|
||||||
0.71000004
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_delaybuffer_cubic_interpolation() {
|
|
||||||
let mut buf = crate::helpers::DelayBuffer::new();
|
|
||||||
|
|
||||||
buf.feed(0.0);
|
|
||||||
buf.feed(0.1);
|
|
||||||
buf.feed(0.2);
|
|
||||||
buf.feed(0.3);
|
|
||||||
buf.feed(0.4);
|
|
||||||
buf.feed(0.5);
|
|
||||||
buf.feed(0.6);
|
|
||||||
buf.feed(0.7);
|
|
||||||
buf.feed(0.8);
|
|
||||||
buf.feed(0.9);
|
|
||||||
buf.feed(1.0);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.1;
|
|
||||||
for _ in 0..30 {
|
|
||||||
samples_out.push(buf.cubic_interpolate_at_s(pos));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0, 1.03455, 1.0504, 1.05085, 1.0392, 1.01875, 0.99279994, 0.9646499, 0.9375999,
|
|
||||||
0.91494995, 0.9, 0.89, 0.87999994, 0.86999995, 0.85999995, 0.84999996, 0.84, 0.83,
|
|
||||||
0.82, 0.80999994, 0.8, 0.79, 0.78000003, 0.77000004, 0.76, 0.75, 0.74, 0.73, 0.72,
|
|
||||||
0.71000004
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut samples_out = vec![];
|
|
||||||
let mut pos = 0.0;
|
|
||||||
let pos_inc = 0.5;
|
|
||||||
for _ in 0..30 {
|
|
||||||
samples_out.push(buf.cubic_interpolate_at_s(pos));
|
|
||||||
pos += pos_inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_vec_feq!(
|
|
||||||
samples_out,
|
|
||||||
vec![
|
|
||||||
1.0,
|
|
||||||
1.01875,
|
|
||||||
0.9,
|
|
||||||
0.85,
|
|
||||||
0.8,
|
|
||||||
0.75,
|
|
||||||
0.7,
|
|
||||||
0.65,
|
|
||||||
0.6,
|
|
||||||
0.55,
|
|
||||||
0.5,
|
|
||||||
0.45,
|
|
||||||
0.4,
|
|
||||||
0.35000002,
|
|
||||||
0.3,
|
|
||||||
0.25,
|
|
||||||
0.2,
|
|
||||||
0.15,
|
|
||||||
0.1,
|
|
||||||
0.043750003,
|
|
||||||
0.0,
|
|
||||||
-0.00625,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ mod common;
|
||||||
//use common::*;
|
//use common::*;
|
||||||
|
|
||||||
use hexodsp::d_pit;
|
use hexodsp::d_pit;
|
||||||
use hexodsp::dsp::helpers::Quantizer;
|
use synfx_dsp::Quantizer;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_quant_pos_neg_exact() {
|
fn check_quant_pos_neg_exact() {
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
use common::*;
|
use common::*;
|
||||||
|
#[cfg(feature="synfx-dsp-jit")]
|
||||||
use hexodsp::wblockdsp::*;
|
use hexodsp::wblockdsp::*;
|
||||||
|
|
||||||
|
#[cfg(feature="synfx-dsp-jit")]
|
||||||
#[test]
|
#[test]
|
||||||
fn check_wblockdsp_init() {
|
fn check_wblockdsp_init() {
|
||||||
let mut engine = CodeEngine::new();
|
let mut engine = CodeEngine::new();
|
||||||
|
@ -13,6 +15,7 @@ fn check_wblockdsp_init() {
|
||||||
let backend = engine.get_backend();
|
let backend = engine.get_backend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature="synfx-dsp-jit")]
|
||||||
#[test]
|
#[test]
|
||||||
fn check_wblockdsp_code_node() {
|
fn check_wblockdsp_code_node() {
|
||||||
let (node_conf, mut node_exec) = new_node_engine();
|
let (node_conf, mut node_exec) = new_node_engine();
|
||||||
|
|
Loading…
Reference in a new issue