1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
// Copyright (c) 2021-2022 Weird Constructor <weirdconstructor@gmail.com>
// This file is a part of synfx-dsp. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
//! Various approximations and faster implementations of trigonometric functions.
///
/// Note: The [fast_cos] and [fast_sin] functions are only barely faster than
/// the Rust builtin `sin` and `cos` functions.
/// Logarithmic table size of the table in [fast_cos] / [fast_sin].
static FAST_COS_TAB_LOG2_SIZE: usize = 9;
/// Table size of the table in [fast_cos] / [fast_sin].
static FAST_COS_TAB_SIZE: usize = 1 << FAST_COS_TAB_LOG2_SIZE; // =512
/// The wave table of [fast_cos] / [fast_sin].
static mut FAST_COS_TAB: [f32; 513] = [0.0; 513];
/// Initializes the cosine wave table for [fast_cos] and [fast_sin].
pub fn init_cos_tab() {
for i in 0..(FAST_COS_TAB_SIZE + 1) {
let phase: f32 = (i as f32) * ((std::f32::consts::TAU) / (FAST_COS_TAB_SIZE as f32));
unsafe {
// XXX: note: mutable statics can be mutated by multiple
// threads: aliasing violations or data races
// will cause undefined behavior
FAST_COS_TAB[i] = phase.cos();
}
}
}
/// Internal phase increment/scaling for [fast_cos].
const PHASE_SCALE: f32 = 1.0_f32 / (std::f32::consts::TAU);
/// A faster implementation of cosine. It's not that much faster than
/// Rust's built in cosine function. But YMMV.
///
/// Don't forget to call [init_cos_tab] before using this!
///
///```
/// use synfx_dsp::*;
/// init_cos_tab(); // Once on process initialization.
///
/// // ...
/// assert!((fast_cos(std::f32::consts::PI) - -1.0).abs() < 0.001);
///```
pub fn fast_cos(mut x: f32) -> f32 {
x = x.abs(); // cosine is symmetrical around 0, let's get rid of negative values
// normalize range from 0..2PI to 1..2
let phase = x * PHASE_SCALE;
let index = FAST_COS_TAB_SIZE as f32 * phase;
let fract = index.fract();
let index = index.floor() as usize;
unsafe {
// XXX: note: mutable statics can be mutated by multiple
// threads: aliasing violations or data races
// will cause undefined behavior
let left = FAST_COS_TAB[index as usize];
let right = FAST_COS_TAB[index as usize + 1];
return left + (right - left) * fract;
}
}
/// A faster implementation of sine. It's not that much faster than
/// Rust's built in sine function. But YMMV.
///
/// Don't forget to call [init_cos_tab] before using this!
///
///```
/// use synfx_dsp::*;
/// init_cos_tab(); // Once on process initialization.
///
/// // ...
/// assert!((fast_sin(0.5 * std::f32::consts::PI) - 1.0).abs() < 0.001);
///```
pub fn fast_sin(x: f32) -> f32 {
fast_cos(x - (std::f32::consts::PI / 2.0))
}
pub fn square_135(phase: f32) -> f32 {
fast_sin(phase) + fast_sin(phase * 3.0) / 3.0 + fast_sin(phase * 5.0) / 5.0
}
pub fn square_35(phase: f32) -> f32 {
fast_sin(phase * 3.0) / 3.0 + fast_sin(phase * 5.0) / 5.0
}
// quickerTanh / quickerTanh64 credits to mopo synthesis library:
// Under GPLv3 or any later.
// Little IO <littleioaudio@gmail.com>
// Matt Tytel <matthewtytel@gmail.com>
pub fn quicker_tanh64(v: f64) -> f64 {
let square = v * v;
v / (1.0 + square / (3.0 + square / 5.0))
}
#[inline]
pub fn quicker_tanh(v: f32) -> f32 {
let square = v * v;
v / (1.0 + square / (3.0 + square / 5.0))
}
// quickTanh / quickTanh64 credits to mopo synthesis library:
// Under GPLv3 or any later.
// Little IO <littleioaudio@gmail.com>
// Matt Tytel <matthewtytel@gmail.com>
pub fn quick_tanh64(v: f64) -> f64 {
let abs_v = v.abs();
let square = v * v;
let num = v
* (2.45550750702956
+ 2.45550750702956 * abs_v
+ square * (0.893229853513558 + 0.821226666969744 * abs_v));
let den =
2.44506634652299 + (2.44506634652299 + square) * (v + 0.814642734961073 * v * abs_v).abs();
num / den
}
pub fn quick_tanh(v: f32) -> f32 {
let abs_v = v.abs();
let square = v * v;
let num = v
* (2.45550750702956
+ 2.45550750702956 * abs_v
+ square * (0.893229853513558 + 0.821226666969744 * abs_v));
let den =
2.44506634652299 + (2.44506634652299 + square) * (v + 0.814642734961073 * v * abs_v).abs();
num / den
}
// Taken from ValleyAudio
// Copyright Dale Johnson
// https://github.dev/ValleyAudio/ValleyRackFree/tree/v2.0
// Under GPLv3 license
pub fn tanh_approx_drive(v: f32, drive: f32) -> f32 {
let x = v * drive;
if x < -1.25 {
-1.0
} else if x < -0.75 {
1.0 - (x * (-2.5 - x) - 0.5625) - 1.0
} else if x > 1.25 {
1.0
} else if x > 0.75 {
x * (2.5 - x) - 0.5625
} else {
x
}
}