synfx_dsp/
waveshapers.rs

1// Copyright (c) 2021-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//! A collection of wave shaping functions.
6
7use std::simd::f32x4;
8use std::simd::StdFloat;
9
10// Ported from LMMS under GPLv2
11// * DspEffectLibrary.h - library with template-based inline-effects
12// * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
13//
14// Original source seems to be musicdsp.org, Author: Bram de Jong
15// see also: https://www.musicdsp.org/en/latest/Effects/41-waveshaper.html
16// Notes:
17//     where x (in [-1..1] will be distorted and a is a distortion parameter
18//     that goes from 1 to infinity. The equation is valid for positive and
19//     negativ values. If a is 1, it results in a slight distortion and with
20//     bigger a's the signal get's more funky.
21//     A good thing about the shaper is that feeding it with bigger-than-one
22//     values, doesn't create strange fx. The maximum this function will reach
23//     is 1.2 for a=1.
24//
25//     f(x,a) = x*(abs(x) + a)/(x^2 + (a-1)*abs(x) + 1)
26/// Signal distortion by Bram de Jong.
27/// ```text
28/// gain:        0.1 - 5.0       default = 1.0
29/// threshold:   0.0 - 100.0     default = 0.8
30/// i:           signal
31/// ```
32#[inline]
33pub fn f_distort(gain: f32, threshold: f32, i: f32) -> f32 {
34    gain * (i * (i.abs() + threshold) / (i * i + (threshold - 1.0) * i.abs() + 1.0))
35}
36
37// Ported from LMMS under GPLv2
38// * DspEffectLibrary.h - library with template-based inline-effects
39// * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
40//
41/// Foldback Signal distortion
42/// ```text
43/// gain:        0.1 - 5.0       default = 1.0
44/// threshold:   0.0 - 100.0     default = 0.8
45/// i:           signal
46/// ```
47#[inline]
48pub fn f_fold_distort(gain: f32, threshold: f32, i: f32) -> f32 {
49    if i >= threshold || i < -threshold {
50        gain * ((((i - threshold) % threshold * 4.0).abs() - threshold * 2.0).abs() - threshold)
51    } else {
52        gain * i
53    }
54}
55
56/// Cheap 4 channel tanh to make the filter faster.
57// Taken from va-filter by Fredemus aka Frederik Halkjær aka RocketPhysician
58// https://github.com/Fredemus/va-filter
59// Under License GPL-3.0-or-later
60//
61// from a quick look it looks extremely good, max error of ~0.0002 or .02%
62// the error of 1 - tanh_levien^2 as the derivative is about .06%
63#[inline]
64pub fn tanh_levien(x: f32x4) -> f32x4 {
65    let x2 = x * x;
66    let x3 = x2 * x;
67    let x5 = x3 * x2;
68    let a = x + (f32x4::splat(0.16489087) * x3) + (f32x4::splat(0.00985468) * x5);
69    // println!("a: {:?}, b: {:?}", a, b);
70    a / (f32x4::splat(1.0) + (a * a)).sqrt()
71}
72
73/// Another tanh approximation. See also [tanh_levien].
74#[inline(always)]
75pub fn tanh_levien_f64(x: f64) -> f64 {
76    let x2 = x * x;
77    let x3 = x2 * x;
78    let x5 = x3 * x2;
79    let a = x + (0.16489087 * x3) + (0.00985468 * x5);
80    a / (1.0 + (a * a)).sqrt()
81}
82