DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
DspMath.h
Go to the documentation of this file.
1// DSPark -- Professional Audio DSP Framework
2// Copyright (c) 2026 Cristian Moresi -- MIT License
3
4#pragma once
5
16#include <algorithm>
17#include <cmath>
18#include <concepts>
19#include <numbers>
20
21namespace dspark {
22
23// ============================================================================
24// Concepts
25// ============================================================================
26
28template <typename T>
29concept FloatType = std::floating_point<T>;
30
31// ============================================================================
32// Constants
33// ============================================================================
34
36template <FloatType T> inline constexpr T pi = std::numbers::pi_v<T>;
37
39template <FloatType T> inline constexpr T twoPi = T(2) * std::numbers::pi_v<T>;
40
42template <FloatType T> inline constexpr T invTwoPi = T(1) / twoPi<T>;
43
45template <FloatType T> inline constexpr T halfPi = std::numbers::pi_v<T> / T(2);
46
48template <FloatType T> inline constexpr T sqrt2 = std::numbers::sqrt2_v<T>;
49
51template <FloatType T> inline constexpr T invSqrt2 = T(1) / std::numbers::sqrt2_v<T>;
52
53// ============================================================================
54// Decibel / Gain Conversions
55// ============================================================================
56
64template <FloatType T>
65[[nodiscard]] inline T decibelsToGain(T dB, T minusInfinityDb = T(-100)) noexcept
66{
67 return dB <= minusInfinityDb ? T(0) : std::pow(T(10), dB / T(20));
68}
69
77template <FloatType T>
78[[nodiscard]] inline T gainToDecibels(T gain, T minusInfinityDb = T(-100)) noexcept
79{
80 return gain > T(0) ? std::max(minusInfinityDb, T(20) * std::log10(gain))
81 : minusInfinityDb;
82}
83
84// ============================================================================
85// Interpolation and Mapping
86// ============================================================================
87
100template <FloatType T>
101[[nodiscard]] inline T mapRange(T value, T inMin, T inMax, T outMin, T outMax) noexcept
102{
103 if (inMin == inMax) return outMin; // Safety: prevent NaN/Inf in audio pipeline
104 return outMin + (outMax - outMin) * ((value - inMin) / (inMax - inMin));
105}
106
107// ============================================================================
108// Fast Approximations
109// ============================================================================
110
125template <FloatType T>
126[[nodiscard]] inline T fastTanh(T x) noexcept
127{
128 // std::clamp translates to SIMD min/max intrinsics (no branching).
129 // Maintains continuity avoiding step-aliasing at the bounds.
130 x = std::clamp(x, T(-3), T(3));
131
132 const auto x2 = x * x;
133 const auto x4 = x2 * x2;
134 // Pade [5,4] approximant: max error < 0.05% for |x| <= 3
135 return x * (T(945) + T(105) * x2 + x4) / (T(945) + T(420) * x2 + T(15) * x4);
136}
137
147template <FloatType T>
148[[nodiscard]] inline T fastPow10(T x) noexcept
149{
150 // log2(10) folded into one constant: rounding it once is more accurate
151 // than multiplying log2(e) * ln(10) at runtime, and it saves a multiply
152 // (compilers may not reassociate x * a * b without fast-math).
153 constexpr T kLog2Of10 =
154 static_cast<T>(std::numbers::ln10_v<long double> / std::numbers::ln2_v<long double>);
155 return std::exp2(x * kLog2Of10);
156}
157
166template <FloatType T>
167[[nodiscard]] inline T fastExp(T x) noexcept
168{
169 return std::exp2(x * std::numbers::log2e_v<T>);
170}
171
186template <FloatType T>
187[[nodiscard]] inline T fastTan(T x) noexcept
188{
189 constexpr T limit = T(1.520); // ~pi/2 - 0.05
190 if (std::abs(x) > limit) return std::tan(x);
191 const T x2 = x * x;
192 const T x4 = x2 * x2;
193 // Pade [5,4]: tan(x) ~= x * (945 - 105*x^2 + x^4) / (945 - 420*x^2 + 15*x^4)
194 return x * (T(945) - T(105) * x2 + x4) / (T(945) - T(420) * x2 + T(15) * x4);
195}
196
212template <FloatType T>
213[[nodiscard]] inline T fastSin(T x) noexcept
214{
215 // Range-reduce to [-pi, pi] with a two-term Cody-Waite split of 2*pi so the
216 // subtraction stays accurate in float even for arguments several periods out.
217 const T k = std::floor(x * invTwoPi<T> + T(0.5));
218 x -= k * T(6.28125); // high part (exactly representable)
219 x -= k * T(0.0019353071795864769253); // low part of 2*pi
220
221 // Fold into [-pi/2, pi/2] where the polynomial converges fast:
222 // sin(x) = sin(pi - x) for x > pi/2 (and the odd mirror for x < -pi/2).
223 if (x > halfPi<T>) x = pi<T> - x;
224 else if (x < -halfPi<T>) x = -pi<T> - x;
225
226 const T x2 = x * x;
227 // Minimax coefficients for sin on [-pi/2, pi/2]. Stored to ~7 digits, so
228 // the realised max abs error is ~3.2e-6 (coefficient-limited, identical in
229 // float and double; measured M-002).
230 return x * (T(0.9999999995)
231 + x2 * (T(-0.1666666580)
232 + x2 * (T(0.0083333075)
233 + x2 * (T(-0.0001984090)
234 + x2 * T(0.0000027526)))));
235}
236
243template <FloatType T>
244[[nodiscard]] inline T fastCos(T x) noexcept
245{
246 return fastSin(x + halfPi<T>);
247}
248
260template <FloatType T>
261[[nodiscard]] inline T fastLog(T x) noexcept
262{
263 int e = 0;
264 T m = std::frexp(x, &e); // x = m * 2^e, m in [0.5, 1)
265 // Normalise mantissa into [sqrt(0.5), sqrt(2)) so the series is centred.
266 if (m < T(0.70710678118654752440)) { m *= T(2); --e; }
267
268 // Classic atanh form (Cephes): ln(m) = 2*atanh(s), s = (m-1)/(m+1).
269 // |s| <= 0.1716 so the odd series converges below 1e-7 with 4 terms.
270 const T s = (m - T(1)) / (m + T(1));
271 const T s2 = s * s;
272 const T lnm = T(2) * s * (T(1)
273 + s2 * (T(1.0 / 3.0)
274 + s2 * (T(1.0 / 5.0)
275 + s2 * T(1.0 / 7.0))));
276
277 return lnm + static_cast<T>(e) * std::numbers::ln2_v<T>; // + e * ln2
278}
279
280// ============================================================================
281// Utility
282// ============================================================================
283
293template <FloatType T>
294[[nodiscard]] inline T wrapPhase(T phase) noexcept
295{
296 // floor() correctly handles negative values, preventing branching
297 T wrapped = phase - twoPi<T> * std::floor(phase * invTwoPi<T>);
298 // Rounding in the k * twoPi product can land wrapped just outside
299 // [0, 2*pi) on either side: large phases where the product overshoots
300 // the input leave a slightly negative result, and tiny negative phases
301 // can round the sum up to exactly twoPi. Enforce the documented
302 // half-open range. Order matters: the += branch can itself round up to
303 // exactly twoPi, which the second branch then folds to 0.
304 if (wrapped < T(0)) wrapped += twoPi<T>;
305 if (wrapped >= twoPi<T>) wrapped -= twoPi<T>;
306 return wrapped;
307}
308
309} // namespace dspark
Constrains a type to IEEE floating-point (float or double).
Definition DspMath.h:29
Main namespace for the DSPark framework.
constexpr T invSqrt2
1 / square root of 2 (0.70710...). Butterworth Q factor.
Definition DspMath.h:51
T fastTan(T x) noexcept
Fast approximation of tan(x) using a Pade [5,4] rational approximant.
Definition DspMath.h:187
T fastPow10(T x) noexcept
Fast approximation of 10^x using exp2.
Definition DspMath.h:148
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
Definition DspMath.h:65
T mapRange(T value, T inMin, T inMax, T outMin, T outMax) noexcept
Maps a value from one range to another (linear interpolation).
Definition DspMath.h:101
constexpr T sqrt2
Square root of 2 (1.41421...).
Definition DspMath.h:48
constexpr T pi
Pi (3.14159...) for the given floating-point type.
Definition DspMath.h:36
T fastSin(T x) noexcept
Fast sine approximation (degree-9 odd minimax polynomial).
Definition DspMath.h:213
T fastCos(T x) noexcept
Fast cosine approximation. See fastSin() for accuracy notes (float error is ~7e-6 here: half an ulp m...
Definition DspMath.h:244
T fastLog(T x) noexcept
Fast natural logarithm approximation.
Definition DspMath.h:261
T gainToDecibels(T gain, T minusInfinityDb=T(-100)) noexcept
Converts a linear gain value to decibels.
Definition DspMath.h:78
T fastTanh(T x) noexcept
Fast tanh approximation using Pade rational function.
Definition DspMath.h:126
constexpr T invTwoPi
1 / (2 * Pi) (0.15915...). Useful for fast phase divisions.
Definition DspMath.h:42
T fastExp(T x) noexcept
Fast approximation of e^x via std::exp2 (~2x faster than std::exp on MSVC).
Definition DspMath.h:167
constexpr T halfPi
Pi / 2 (1.57079...). Quarter period; sin/cos phase offset.
Definition DspMath.h:45
constexpr T twoPi
2 * Pi (6.28318...).
Definition DspMath.h:39
T wrapPhase(T phase) noexcept
Normalises a phase value to the range [0, 2*pi).
Definition DspMath.h:294