Made sure synfx-dsp-jit is optional

This commit is contained in:
Weird Constructor 2022-08-05 06:50:38 +02:00
commit a0478f64e9
35 changed files with 88 additions and 3723 deletions

View file

@ -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"

View file

@ -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::*;

View file

@ -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::*;

View file

@ -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>> {

View file

@ -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
}
}

View file

@ -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)
}
}

File diff suppressed because it is too large Load diff

View file

@ -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
} }
} }

View file

@ -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,
}; };

View file

@ -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};

View file

@ -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};

View file

@ -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,
}; };

View file

@ -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,
}; };

View file

@ -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)";

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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,

View file

@ -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,
}; };

View file

@ -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,
}; };

View file

@ -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};

View file

@ -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,
}; };

View file

@ -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,
}; };

View file

@ -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,

View file

@ -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)
} }

View file

@ -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) {

View file

@ -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
]
);
}

View file

@ -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() {

View file

@ -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();