Refactored out the DSP helper stuff into the synfx-dsp crate
This commit is contained in:
parent
1b89fed67d
commit
808547dc16
30 changed files with 36 additions and 3707 deletions
|
@ -18,7 +18,8 @@ ringbuf = "0.2.2"
|
|||
triple_buffer = "5.0.6"
|
||||
lazy_static = "1.4.0"
|
||||
hound = "3.4.0"
|
||||
num-traits = "0.2.14"
|
||||
synfx-dsp = "0.5.1"
|
||||
#synfx-dsp = { git = "https://github.com/WeirdConstructor/synfx-dsp" }
|
||||
|
||||
[dev-dependencies]
|
||||
num-complex = "0.2"
|
||||
|
|
|
@ -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`
|
||||
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
|
||||
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.
|
||||
|
||||
```ignore
|
||||
// node_tslfo.rs
|
||||
use super::helpers::{TriSawLFO, Trigger};
|
||||
use synfx_dsp::{TriSawLFO, Trigger};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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
|
||||
|
||||
|
@ -538,9 +539,6 @@ mod node_tslfo;
|
|||
#[allow(non_upper_case_globals)]
|
||||
mod node_vosc;
|
||||
|
||||
pub mod biquad;
|
||||
pub mod dattorro;
|
||||
pub mod helpers;
|
||||
mod satom;
|
||||
pub mod tracker;
|
||||
|
||||
|
@ -564,7 +562,7 @@ use crate::fa_cqnt;
|
|||
use crate::fa_cqnt_omax;
|
||||
use crate::fa_cqnt_omin;
|
||||
use crate::fa_delay_mode;
|
||||
use crate::fa_distort;
|
||||
use synfx_dsp::fa_distort;
|
||||
use crate::fa_map_clip;
|
||||
use crate::fa_mux9_in_cnt;
|
||||
use crate::fa_noise_mode;
|
||||
|
@ -1586,7 +1584,7 @@ fn rand_node_satisfies_spec(nid: NodeId, sel: RandNodeSelector) -> bool {
|
|||
}
|
||||
|
||||
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 cnt = 0;
|
||||
|
@ -1966,7 +1964,7 @@ macro_rules! make_node_info_enum {
|
|||
1 => 0.05,
|
||||
2 => 0.1,
|
||||
// 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.
|
||||
// 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::{
|
||||
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.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::dsp::helpers::PolyBlepOscillator;
|
||||
use synfx_dsp::PolyBlepOscillator;
|
||||
use crate::dsp::{
|
||||
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.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::dsp::biquad::Biquad;
|
||||
use crate::dsp::helpers::{DelayBuffer, FixedOnePole};
|
||||
use synfx_dsp::{DelayBuffer, FixedOnePole, Biquad};
|
||||
use crate::dsp::{
|
||||
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.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
@ -21,12 +21,12 @@ macro_rules! fa_comb_mode {
|
|||
/// A simple amplifier
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Comb {
|
||||
comb: Box<helpers::Comb>,
|
||||
comb: Box<synfx_dsp::Comb>,
|
||||
}
|
||||
|
||||
impl Comb {
|
||||
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)";
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use super::dattorro::{DattorroReverb, DattorroReverbParams};
|
||||
use super::helpers::crossfade;
|
||||
use synfx_dsp::{DattorroReverb, DattorroReverbParams, crossfade};
|
||||
use crate::dsp::{denorm, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// Copyright by Andrew Belt, 2021
|
||||
|
||||
//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::nodes::SCOPE_SAMPLES;
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// 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_tpt_lowpass, process_hal_chamberlin_svf, process_simper_svf,
|
||||
process_stilson_moog,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::dsp::helpers::fast_sin;
|
||||
use synfx_dsp::fast_sin;
|
||||
use crate::dsp::{
|
||||
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.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::dsp::helpers::TrigSignal;
|
||||
use synfx_dsp::TrigSignal;
|
||||
use crate::dsp::{
|
||||
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.
|
||||
// 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::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use super::helpers::{TriSawLFO, Trigger};
|
||||
use synfx_dsp::{TriSawLFO, Trigger};
|
||||
use crate::dsp::{
|
||||
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.
|
||||
// See README.md and COPYING for details.
|
||||
|
||||
use crate::dsp::biquad::Oversampling;
|
||||
use crate::dsp::helpers::{apply_distortion, VPSOscillator};
|
||||
use synfx_dsp::{Oversampling, apply_distortion, VPSOscillator};
|
||||
use crate::dsp::{
|
||||
DspNode, GraphAtomData, GraphFun, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use super::MAX_COLS;
|
||||
use super::MAX_PATTERN_LEN;
|
||||
use crate::dsp::helpers::SplitMix64;
|
||||
use synfx_dsp::SplitMix64;
|
||||
|
||||
pub struct PatternSequencer {
|
||||
rows: usize,
|
||||
|
|
|
@ -89,7 +89,7 @@ pub fn new_node_engine() -> (NodeConfigurator, NodeExecutor) {
|
|||
|
||||
// XXX: This is one of the earliest and most consistent points
|
||||
// in runtime to do this kind of initialization:
|
||||
crate::dsp::helpers::init_cos_tab();
|
||||
synfx_dsp::init_cos_tab();
|
||||
|
||||
(nc, ne)
|
||||
}
|
||||
|
|
|
@ -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 |