synfx_dsp/lib.rs
1// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5/*! synfx-dsp DSP real time audio synthesis, effect algorithms and utilities for Rust
6
7Most of the algorithms and implementations in this library have been
8implemented for [HexoDSP](https://github.com/WeirdConstructor/HexoDSP) and used
9in [HexoSynth](https://github.com/WeirdConstructor/HexoSynth). I factored them out, because
10they seem useful in other contexts too, for instance the [synfx-dsp-jit](https://github.com/WeirdConstructor/synfx-dsp-jit)
11crate.
12
13I collected most of the algorithms in this crate from various GPLv3 compatible
14sources. They also were partially translated from multiple different C++ projects.
15I tried to document the source and source license diligently in the comments of this crate.
16I apologize if any attribution is missing and would welcome patches or reports.
17
18Feel free to use these algorithms and utilities. Help, patches and additions are appreciated
19if they comply with the GPL-3.0-or-later license and don't break the test suite in HexoDSP.
20
21**Attention:** HexoDSP comes with a large test suite that also covers these algorithms. And that is the one
22that also has to pass if these algorithms are touched. The flip side is, that these implementations
23are actually covered by a test suite.
24
25**Requires Nightly as of 2022-10-02 due to std::simd!**
26
27Copyright, Licenses, Attribution, Contributions
28===============================================
29
30Here is a list of sources parts of this library copied or translated code from:
31
32- [crate::quicker_tanh64] / [crate::quicker_tanh]
33 ```text
34 quickerTanh / quickerTanh64 credits to mopo synthesis library:
35 Under GPLv3 or any later.
36 Little IO <littleioaudio@gmail.com>
37 Matt Tytel <matthewtytel@gmail.com>
38 ```
39- [crate::quick_tanh64] / [crate::quick_tanh]
40 ```text
41 quickTanh / quickTanh64 credits to mopo synthesis library:
42 Under GPLv3 or any later.
43 Little IO <littleioaudio@gmail.com>
44 Matt Tytel <matthewtytel@gmail.com>
45 ```
46- [crate::tanh_approx_drive]
47 ```text
48 Taken from ValleyAudio
49 Copyright Dale Johnson
50 https://github.dev/ValleyAudio/ValleyRackFree/tree/v2.0
51 Under GPLv3 license
52 ```
53- [crate::AtomicFloat]
54 ```text
55 Implementation from vst-rs
56 https://github.com/RustAudio/vst-rs/blob/master/src/util/atomic_float.rs
57 Under MIT License
58 Copyright (c) 2015 Marko Mijalkovic
59 ```
60- [crate::Biquad]
61 ```text
62 The implementation of this Biquad Filter has been adapted from
63 SamiPerttu, Copyright (c) 2020, under the MIT License.
64 See also: https://github.com/SamiPerttu/fundsp/blob/master/src/filter.rs
65 ```
66- [crate::DattorroReverb]
67 ```text
68 This file contains a reverb implementation that is based
69 on Jon Dattorro's 1997 reverb algorithm. It's also largely
70 based on the C++ implementation from ValleyAudio / ValleyRackFree
71
72 ValleyRackFree Copyright (C) 2020, Valley Audio Soft, Dale Johnson
73 Adapted under the GPL-3.0-or-later License.
74
75 See also: https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Plateau/Dattorro.cpp
76 and: https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Plateau/Dattorro.hpp
77
78 And: https://ccrma.stanford.edu/~dattorro/music.html
79 And: https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf
80 ```
81- [crate::process_1pole_lowpass] / [crate::process_1pole_highpass]
82 ```text
83 one pole lp from valley rack free:
84 https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Common/DSP/OnePoleFilters.cpp
85 ```
86- [crate::process_1pole_tpt_lowpass] / [crate::process_1pole_tpt_highpass]
87 ```text
88 one pole from:
89 http://www.willpirkle.com/Downloads/AN-4VirtualAnalogFilters.pdf
90 (page 5)
91 ```
92- [crate::FixedOnePole]
93 ```text
94 Fixed one pole with setable pole and gain.
95 Implementation taken from tubonitaub / alec-deason
96 from https://github.com/alec-deason/virtual_modular/blob/4025f1ef343c2eb9cd74eac07b5350c1e7ec9c09/src/simd_graph.rs#L4292
97 under MIT License
98 ```
99- [crate::process_hal_chamberlin_svf]
100 ```text
101 Hal Chamberlin's State Variable (12dB/oct) filter
102 https://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/
103 Inspired by SynthV1 by Rui Nuno Capela, under the terms of
104 GPLv2 or any later:
105 ```
106- [crate::process_simper_svf]
107 ```text
108 Simper SVF implemented from
109 https://cytomic.com/files/dsp/SvfLinearTrapezoidalSin.pdf
110 Big thanks go to Andrew Simper @ Cytomic for developing and publishing
111 the paper.
112 ```
113- [crate::process_stilson_moog]
114 ```text
115 Stilson/Moog implementation partly translated from here:
116 https://github.com/ddiakopoulos/MoogLadders/blob/master/src/MusicDSPModel.h
117 without any copyright as found on musicdsp.org
118 (http://www.musicdsp.org/showone.php?id=24).
119
120 It's also found on MusicDSP and has probably no proper license anyways.
121 See also: https://github.com/ddiakopoulos/MoogLadders
122 and https://github.com/rncbc/synthv1/blob/master/src/synthv1_filter.h#L103
123 and https://github.com/ddiakopoulos/MoogLadders/blob/master/src/MusicDSPModel.h
124 ```
125- [crate::DCBlockFilter]
126 ```text
127 translated from Odin 2 Synthesizer Plugin
128 Copyright (C) 2020 TheWaveWarden
129 under GPLv3 or any later
130 ```
131- [crate::cubic_interpolate]
132 ```text
133 Hermite interpolation, take from
134 https://github.com/eric-wood/delay/blob/main/src/delay.rs#L52
135
136 Thanks go to Eric Wood!
137
138 For the interpolation code:
139 MIT License, Copyright (c) 2021 Eric Wood
140 ```
141- [crate::TriSawLFO]
142 ```text
143 Adapted from https://github.com/ValleyAudio/ValleyRackFree/blob/v1.0/src/Common/DSP/LFO.hpp
144
145 ValleyRackFree Copyright (C) 2020, Valley Audio Soft, Dale Johnson
146 Adapted under the GPL-3.0-or-later License.
147 ```
148- [crate::PolyBlepOscillator]
149 ```text
150 PolyBLEP by Tale
151 (slightly modified)
152 http://www.kvraudio.com/forum/viewtopic.php?t=375517
153 from http://www.martin-finke.de/blog/articles/audio-plugins-018-polyblep-oscillator/
154 ```
155- [crate::VPSOscillator]
156 ```text
157 This oscillator is based on the work "VECTOR PHASESHAPING SYNTHESIS"
158 by: Jari Kleimola*, Victor Lazzarini†, Joseph Timoney†, Vesa Välimäki*
159 *Aalto University School of Electrical Engineering Espoo, Finland;
160 †National University of Ireland, Maynooth Ireland
161
162 See also this PDF: http://recherche.ircam.fr/pub/dafx11/Papers/55_e.pdf
163 ```
164- [crate::Oversampling]
165 ```text
166 Loosely adapted from https://github.com/VCVRack/Befaco/blob/v1/src/ChowDSP.hpp
167 Copyright (c) 2019-2020 Andrew Belt and Befaco contributors
168 Under GPLv-3.0-or-later
169
170 Which was originally taken from https://github.com/jatinchowdhury18/ChowDSP-VCV/blob/master/src/shared/AAFilter.hpp
171 Copyright (c) 2020 jatinchowdhury18
172 ```
173- [crate::next_xoroshiro128]
174 ```text
175 Taken from xoroshiro128 crate under MIT License
176 Implemented by Matthew Scharley (Copyright 2016)
177 https://github.com/mscharley/rust-xoroshiro128
178 ```
179- [crate::u64_to_open01]
180 ```text
181 Taken from rand::distributions
182 Licensed under the Apache License, Version 2.0
183 Copyright 2018 Developers of the Rand project.
184 ```
185- [crate::SplitMix64]
186 ```text
187 Copyright 2018 Developers of the Rand project.
188
189 Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
190 https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
191 <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
192 option. This file may not be copied, modified, or distributed
193 except according to those terms.
194 - splitmix64 (http://xoroshiro.di.unimi.it/splitmix64.c)
195 ```
196- [crate::f_distort] / [crate::f_fold_distort]
197 ```text
198 Ported from LMMS under GPLv2
199 * DspEffectLibrary.h - library with template-based inline-effects
200 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
201
202 Original source seems to be musicdsp.org, Author: Bram de Jong
203 see also: https://www.musicdsp.org/en/latest/Effects/41-waveshaper.html
204 ```
205- [crate::PolyIIRHalfbandFilter]
206 ```text
207 Taken from va-filter by Fredemus aka Frederik Halkjær aka RocketPhysician
208 https://github.com/Fredemus/va-filter
209 Under License GPL-3.0-or-later
210
211 originally translated from the freely available source code
212 at https://www.musicdsp.org/en/latest/Filters/39-polyphase-filters.html
213 ```
214- [crate::DCFilterX4]
215 ```text
216 Basic 4 channel SIMD DC-filter from Understanding Digital Signal Processing by Richard Lyons.
217
218 Taken from va-filter by Fredemus aka Frederik Halkjær aka RocketPhysician
219 https://github.com/Fredemus/va-filter
220 Under License GPL-3.0-or-later
221 ```
222- [crate::fh_va::LadderFilter] / [crate::fh_va::Svf] / [crate::fh_va::SallenKey]
223 ```text
224 Taken from va-filter by Fredemus aka Frederik Halkjær aka RocketPhysician
225 https://github.com/Fredemus/va-filter
226 Under License GPL-3.0-or-later
227 ```
228*/
229
230#![feature(portable_simd)]
231
232mod approx;
233mod atomic;
234mod biquad;
235mod dattorro;
236mod delay;
237mod env;
238pub mod fh_va;
239mod filters;
240mod interpolation;
241mod low_freq;
242mod oscillators;
243mod oversampling;
244mod rand;
245mod test;
246mod trig_clock;
247mod waveshapers;
248
249pub use approx::*;
250pub use atomic::*;
251pub use biquad::{Biquad, BiquadCoefs};
252pub use dattorro::{DattorroReverb, DattorroReverbParams};
253pub use delay::*;
254pub use env::*;
255pub use filters::*;
256pub use interpolation::*;
257pub use low_freq::*;
258pub use oscillators::*;
259pub use oversampling::Oversampling;
260pub use oversampling::PolyIIRHalfbandFilter;
261pub use rand::*;
262#[allow(unused)]
263pub use test::*;
264pub use trig_clock::*;
265pub use waveshapers::*;
266
267use num_traits::{cast::FromPrimitive, cast::ToPrimitive, Float, FloatConst};
268
269macro_rules! trait_alias {
270 ($name:ident = $base1:ident + $($base2:ident +)+) => {
271 pub trait $name: $base1 $(+ $base2)+ { }
272 impl<T: $base1 $(+ $base2)+> $name for T { }
273 };
274}
275
276trait_alias!(Flt = Float + FloatConst + ToPrimitive + FromPrimitive +);
277
278//macro_rules! fc {
279// ($F: ident, $e: expr) => {
280// F::from_f64($e).unwrap()
281// };
282//}
283
284#[allow(dead_code)]
285#[inline]
286fn f<F: Flt>(x: f64) -> F {
287 F::from_f64(x).unwrap()
288}
289
290#[allow(dead_code)]
291#[inline]
292fn fclamp<F: Flt>(x: F, mi: F, mx: F) -> F {
293 x.max(mi).min(mx)
294}
295
296#[allow(dead_code)]
297#[inline]
298fn fclampc<F: Flt>(x: F, mi: f64, mx: f64) -> F {
299 x.max(f(mi)).min(f(mx))
300}
301
302/// Converts a midi note (0 to 128) to a frequency
303///
304///```
305/// use synfx_dsp::*;
306///
307/// assert_eq!(note_to_freq(69.0) as i32, 440);
308/// assert_eq!(note_to_freq(69.0 + 12.0) as i32, 880);
309/// assert_eq!(note_to_freq(69.0 - 12.0) as i32, 220);
310///```
311pub fn note_to_freq(note: f32) -> f32 {
312 440.0 * (2.0_f32).powf((note - 69.0) / 12.0)
313}
314
315/// Converts gain in decibels to a factor/coeffient
316///
317/// ```
318/// use synfx_dsp::gain_db2coef;
319///
320/// assert!((gain_db2coef(-6.0) - 0.501187).abs() < 0.00001);
321/// assert!((gain_db2coef(-3.0) - 0.707945).abs() < 0.00001);
322/// assert!((gain_db2coef(0.0) - 1.0).abs() < 0.00001);
323/// assert!((gain_db2coef(6.0) - 1.99526).abs() < 0.00001);
324/// ```
325#[inline]
326pub fn gain_db2coef(gain_db: f32) -> f32 {
327 if gain_db < -89.9 {
328 0.0
329 } else {
330 10.0_f32.powf(gain_db * 0.05)
331 }
332}
333
334/// Converts a coefficient/factor to decibels
335///
336///```
337/// use synfx_dsp::coef2gain_db;
338///
339/// assert!((coef2gain_db(0.501187) - -6.0).abs() < 0.0001);
340/// assert!((coef2gain_db(0.707945) - -3.0).abs() < 0.0001);
341/// assert!(coef2gain_db(1.0).abs() < 0.00001);
342/// assert!((coef2gain_db(1.99526) - 6.0).abs() < 0.0001);
343///```
344#[inline]
345pub fn coef2gain_db(coef: f32) -> f32 {
346 if coef < 0.0000317 {
347 -90.0
348 } else {
349 20.0 * coef.log10()
350 }
351}
352
353/// A `pow` like shape function for exponential envelopes.
354/// It's a bit faster than calling the `pow` function of Rust.
355///
356/// * `x` the input value
357/// * `v' the shape value.
358/// Which is linear at `0.5`, the forth root of `x` at `1.0` and x to the power
359/// of 4 at `0.0`. You can vary `v` as you like.
360///
361///```
362/// use synfx_dsp::*;
363///
364/// assert!(((sqrt4_to_pow4(0.25, 0.0) - 0.25_f32 * 0.25 * 0.25 * 0.25)
365/// .abs() - 1.0)
366/// < 0.0001);
367///
368/// assert!(((sqrt4_to_pow4(0.25, 1.0) - (0.25_f32).sqrt().sqrt())
369/// .abs() - 1.0)
370/// < 0.0001);
371///
372/// assert!(((sqrt4_to_pow4(0.25, 0.5) - 0.25_f32).abs() - 1.0) < 0.0001);
373///```
374#[inline]
375pub fn sqrt4_to_pow4(x: f32, v: f32) -> f32 {
376 if v > 0.75 {
377 let xsq1 = x.sqrt();
378 let xsq = xsq1.sqrt();
379 let v = (v - 0.75) * 4.0;
380 xsq1 * (1.0 - v) + xsq * v
381 } else if v > 0.5 {
382 let xsq = x.sqrt();
383 let v = (v - 0.5) * 4.0;
384 x * (1.0 - v) + xsq * v
385 } else if v > 0.25 {
386 let xx = x * x;
387 let v = (v - 0.25) * 4.0;
388 x * v + xx * (1.0 - v)
389 } else {
390 let xx = x * x;
391 let xxxx = xx * xx;
392 let v = v * 4.0;
393 xx * v + xxxx * (1.0 - v)
394 }
395}
396
397/// Returns the name of the distortion selected by the `dist_type` parameter of the [apply_distortion]
398/// function.
399#[macro_export]
400macro_rules! fa_distort {
401 ($formatter: expr, $v: expr, $denorm_v: expr) => {{
402 let s = match ($v.round() as usize) {
403 0 => "Off",
404 1 => "TanH",
405 2 => "B.D.Jong",
406 3 => "Fold",
407 _ => "?",
408 };
409 write!($formatter, "{}", s)
410 }};
411}
412
413#[inline]
414pub fn apply_distortion(s: f32, damt: f32, dist_type: u8) -> f32 {
415 match dist_type {
416 1 => (damt.clamp(0.01, 1.0) * 100.0 * s).tanh(),
417 2 => f_distort(1.0, damt * damt * damt * 1000.0, s),
418 3 => {
419 let damt = damt.clamp(0.0, 0.99);
420 let damt = 1.0 - damt * damt;
421 f_fold_distort(1.0, damt, s) * (1.0 / damt)
422 }
423 _ => s,
424 }
425}