Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
fast_math.hpp
Go to the documentation of this file.
1// sbl/dsp/fast_math.hpp — Fast analytical approximations (Audio Stack — Atoms)
2//
3// Lightweight math functions for hot audio paths where libm functions
4// like sinf() are too expensive. All functions are pure arithmetic —
5// no lookup tables, no flash cost.
6//
7// fast_sinf(x): 5th-order Taylor series for sin(x), valid for x in
8// [0, π/2]. Max error < 0.00016 at x = π/2. ~5 cycles on M7 FPU
9// vs ~500 cycles for newlib sinf().
10//
11// fast_exp2f(x): Fast 2^x for exponential modulation (1V/oct-style).
12// Integer part via IEEE 754 exponent shift, fractional part via
13// 3rd-order minimax polynomial. ~8 cycles on M7 FPU. Accurate to
14// ~20 bits for |x| <= 4 (±4 octaves).
15//
16// Usage:
17// float w = pi * fc / (2.0f * fs);
18// float f = 2.0f * sbl::dsp::fast_sinf(w); // SVF coefficient
19//
20// // Exponential modulation (LFO → filter cutoff):
21// float mod_cutoff = cutoff * sbl::dsp::fast_exp2f(lfo * depth_oct);
22
23#pragma once
24
25namespace sbl::dsp {
26
27/// Fast sine approximation for x in [0, π/2]
28///
29/// Uses 5th-order Taylor series: x - x³/6 + x⁵/120
30///
31/// Accuracy:
32/// x = 0.0 → error = 0
33/// x = 0.589 → error < 0.00016 (SVF max at 18 kHz / 48 kHz)
34/// x = π/2 → error ≈ 0.00045 (theoretical max in domain)
35///
36/// @param x Input angle in radians, must be in [0, π/2]
37/// @return Approximation of sin(x)
38inline float fast_sinf(float x) {
39 float x2 = x * x;
40 return x * (1.0f - x2 * (1.0f / 6.0f - x2 * (1.0f / 120.0f)));
41}
42
43/// Fast tan(π·f) for normalized frequency f ∈ [0, 0.497]
44///
45/// [5,4] Padé approximant of tan(x) evaluated at x = π·f:
46///
47/// tan(x) ≈ x · (945 - 105x² + x⁴) / (945 - 420x² + 15x⁴)
48///
49/// Unlike a polynomial, the Padé rational function correctly models the
50/// pole at f = 0.5 (Nyquist). The denominator goes to zero at x = π/2,
51/// matching the true singularity of tan.
52///
53/// Accuracy vs true tan(π·f):
54/// f = 0.10 (4.8 kHz) → error < 0.001%
55/// f = 0.35 (16.8 kHz) → error < 0.3%
56/// f = 0.45 (21.6 kHz) → error < 2.4%
57/// f = 0.497 (23.9 kHz) → error < 0.1%
58///
59/// The previous 5th-order polynomial (from MI stmlib) diverged badly
60/// above f ≈ 0.35, giving 45% error at f = 0.45 — causing audible
61/// artifacts (secondary resonant peaks) in the ZDF SVF.
62///
63/// Cost: ~8 FMA + 1 VDIV ≈ 20–25 cycles on M7 FPU (vs ~5 for the old
64/// polynomial, vs ~50–100 for newlib tanf).
65///
66/// @param f Normalized frequency (freq_hz / sample_rate), must be < 0.497
67/// @return Approximation of tan(π·f)
68inline float fast_tan_pif(float f) {
69 constexpr float pi = 3.14159265f;
70 constexpr float pi2 = pi * pi;
71 constexpr float pi4 = pi2 * pi2;
72 float f2 = f * f;
73 float f4 = f2 * f2;
74 float num = 945.0f - 105.0f * pi2 * f2 + pi4 * f4;
75 float den = 945.0f - 420.0f * pi2 * f2 + 15.0f * pi4 * f4;
76 return pi * f * num / den;
77}
78
79
80/// Fast 2^x approximation for exponential modulation
81///
82/// Decomposes x into integer and fractional parts. The integer part is
83/// applied by shifting the IEEE 754 exponent field (exact). The fractional
84/// part uses a 3rd-order minimax polynomial (accurate to ~20 bits).
85///
86/// This is the "exponential converter" primitive — the analog equivalent
87/// of the circuit that makes 1V/oct work in a VCF or VCO.
88///
89/// Accuracy (vs std::exp2f):
90/// |x| <= 1 → max relative error < 0.02%
91/// |x| <= 4 → max relative error < 0.05%
92/// |x| > 16 → clamped (returns 0 for x < -16)
93///
94/// @param x Exponent (e.g., ±2.0 for ±2 octave modulation)
95/// @return Approximation of 2^x
96inline float fast_exp2f(float x) {
97 // Clamp to prevent overflow/underflow
98 if (x < -16.0f) return 0.0f;
99 if (x > 16.0f) x = 16.0f;
100
101 // Decompose into integer and fractional parts
102 // Use truncation toward negative infinity
103 int i = static_cast<int>(x);
104 float f = x - static_cast<float>(i);
105 if (f < 0.0f) { f += 1.0f; --i; }
106
107 // 4th-order polynomial for 2^f, f in [0, 1)
108 // Remez-style minimax coefficients for improved accuracy over Taylor
109 float p = 1.0f + f * (0.6931472f + f * (0.2402265f
110 + f * (0.0558011f + f * 0.00898f)));
111
112 // Apply integer exponent via IEEE 754 bit manipulation
113 union { float fv; int32_t iv; } v;
114 v.fv = p;
115 v.iv += i << 23;
116 return v.fv;
117}
118
119} // namespace sbl::dsp
DSP atoms for audio signal processing.
Definition allpass.hpp:22
float fast_tan_pif(float f)
Definition fast_math.hpp:68
float fast_sinf(float x)
Definition fast_math.hpp:38
float fast_exp2f(float x)
Definition fast_math.hpp:96