Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
saturate.hpp
Go to the documentation of this file.
1// sbl/dsp/saturate.hpp — Soft clipping and saturation
2//
3// Analytical saturation functions that produce musically useful harmonics
4// instead of the harsh artifacts from hard clipping. Zero flash cost
5// (no lookup tables), works on M7 FPU or soft-float.
6//
7// soft_limit(x): Smooth saturation with unity gain at origin. For small
8// inputs, output ≈ input. NOT bounded — approaches x/9 for large x.
9// Use soft_clip() for bounded output.
10//
11// soft_clip(x): Pre-clamps to [-3, 3] then applies soft_limit.
12// Output is bounded to exactly [-1, 1]. soft_clip(±3) = ±1.
13//
14// soft_clip_24(x): 24-bit audio output limiter. Unity gain for small
15// signals, smooth compression as level approaches ±SAMPLE_MAX_24.
16// Full-scale input produces ~78% output (gentle compression).
17// Useful for overdrive/warmth, or as a musical alternative to hard clamp.
18//
19// Inspired by Mutable Instruments stmlib (MIT).
20//
21// Usage:
22// float out = sbl::dsp::soft_clip(drive * input); // [-1, 1] output
23// int32_t out = sbl::dsp::soft_clip_24(sample); // 24-bit audio
24
25#pragma once
26
27#include <cstdint>
28
29#include <sbl/dsp/fixed.hpp>
30
31namespace sbl::dsp {
32
33/**
34 * @brief Smooth saturation with unity gain at origin
35 *
36 * Uses rational approximation: x * (27 + x²) / (27 + 9x²).
37 * Monotonic, odd-symmetric, derivative = 1 at x=0. NOT bounded —
38 * approaches x/9 for large x. Use soft_clip() for bounded output.
39 *
40 * Key values: soft_limit(1.0) ≈ 0.78, soft_limit(3.0) = 1.0
41 */
42inline float soft_limit(float x) {
43 float x2 = x * x;
44 return x * (27.0f + x2) / (27.0f + 9.0f * x2);
45}
46
47/**
48 * @brief Bounded soft clipper — output in [-1, 1]
49 *
50 * Pre-clamps to [-3, 3] then applies soft_limit. Since
51 * soft_limit(3) = 1.0 exactly, output is bounded to [-1, 1].
52 * Gentle compression: soft_clip(1.0) ≈ 0.78, soft_clip(2.0) ≈ 0.98.
53 */
54inline float soft_clip(float x) {
55 if (x < -3.0f) x = -3.0f;
56 if (x > 3.0f) x = 3.0f;
57 return soft_limit(x);
58}
59
60/**
61 * @brief Soft saturation for 24-bit audio samples
62 *
63 * Normalizes to [-1, 1], applies soft_limit, scales back. Unity gain
64 * for very small signals, gentle 3rd-harmonic compression as level
65 * increases. Full-scale produces ~78% output — this is intentional
66 * musical compression, not transparent limiting.
67 *
68 * For transparent hard limiting, use saturate_24() from fixed.hpp.
69 * For overdrive, pre-multiply by a gain factor before calling.
70 */
71inline int32_t soft_clip_24(int32_t x) {
72 constexpr float INV_MAX = 1.0f / static_cast<float>(SAMPLE_MAX_24);
73 float f = static_cast<float>(x) * INV_MAX; // normalize to ~[-1, 1]
74 // Clamp to [-3, 3] to handle signals beyond full scale
75 if (f < -3.0f) f = -3.0f;
76 if (f > 3.0f) f = 3.0f;
77 float sat = soft_limit(f);
78 return static_cast<int32_t>(sat * static_cast<float>(SAMPLE_MAX_24));
79}
80
81} // namespace sbl::dsp
DSP atoms for audio signal processing.
Definition allpass.hpp:22
float soft_limit(float x)
Smooth saturation with unity gain at origin.
Definition saturate.hpp:42
int32_t soft_clip_24(int32_t x)
Soft saturation for 24-bit audio samples.
Definition saturate.hpp:71
float soft_clip(float x)
Bounded soft clipper — output in [-1, 1].
Definition saturate.hpp:54
constexpr int32_t SAMPLE_MAX_24
Definition fixed.hpp:27