Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
lut.hpp
Go to the documentation of this file.
1// sbl/dsp/lut.hpp — Lookup table interpolation functions
2//
3// These functions read LUTE-generated constexpr tables. The table pointer
4// must point to an array of (Size + guard_points) entries.
5//
6// Phase convention: uint32_t phase where the full 32-bit range maps to one
7// cycle of the table. Upper bits select the table index, lower bits provide
8// sub-sample fractional precision.
9//
10// Inspired by MI stmlib/utils/dsp.h (MIT). Generalized for arbitrary
11// power-of-2 table sizes via constexpr template parameters.
12
13#pragma once
14
15#include <cstdint>
16
17#include <sbl/dsp/fixed.hpp>
18
19namespace sbl::dsp::lut {
20
21// ─── Compile-time helpers ──────────────────────────────────────────────
22
23namespace detail {
24
25constexpr uint8_t log2_of(uint16_t n) {
26 uint8_t r = 0;
27 while (n > 1) { n >>= 1; ++r; }
28 return r;
29}
30
31} // namespace detail
32
33// ─── uint16_t linear interpolation (control domain) ────────────────────
34//
35// Used by warp engines (ExpCurveWarp, etc.) for envelope curve shaping.
36// Operates in the uint16_t control domain [0, 65535], NOT audio.
37
38template<uint16_t Size>
39inline uint16_t lookup_linear(const uint16_t* table, uint32_t phase) {
40 constexpr uint8_t bits = detail::log2_of(Size);
41 static_assert(bits >= 1 && bits <= 16, "Size must be power of 2 in [2, 65536]");
42
43 uint32_t idx = phase >> (32 - bits);
44 uint32_t a = table[idx];
45 uint32_t b = table[idx + 1];
46 uint16_t frac = (phase >> (16 - bits)) & FRAC16_MASK;
47 return static_cast<uint16_t>(a + (((b - a) * frac) >> 16));
48}
49
50// ─── uint16_t mix ──────────────────────────────────────────────────────
51
52inline uint16_t mix(uint16_t a, uint16_t b, uint16_t balance) {
53 return static_cast<uint16_t>(
54 (static_cast<uint32_t>(a) * (U16_MAX - balance) +
55 static_cast<uint32_t>(b) * balance) >> 16);
56}
57
58// ═══════════════════════════════════════════════════════════════════════
59// Float lookup functions
60//
61// For float tables in [-1.0, 1.0]. Same phase convention (uint32_t full
62// range = one cycle), dramatically simpler math — no Q16 fractions, no
63// int64 intermediates.
64// ═══════════════════════════════════════════════════════════════════════
65
66// ─── Float linear interpolation ──────────────────────────────────────
67
68/// @note All functions in sbl::dsp::lut are ISR-safe — bounded computation, no I/O.
69
70template<uint16_t Size>
71inline float lookup_linear(const float* table, uint32_t phase) {
72 constexpr uint8_t bits = detail::log2_of(Size);
73 static_assert(bits >= 1 && bits <= 16, "Size must be power of 2 in [2, 65536]");
74
75 uint32_t idx = phase >> (32 - bits);
76 float frac = static_cast<float>(phase & ((1u << (32 - bits)) - 1))
77 * (1.0f / static_cast<float>(1u << (32 - bits)));
78 return table[idx] + (table[idx + 1] - table[idx]) * frac;
79}
80
81// ─── Float cubic Hermite interpolation ───────────────────────────────
82
83template<uint16_t Size>
84inline float lookup_cubic(const float* table, uint32_t phase) {
85 constexpr uint8_t bits = detail::log2_of(Size);
86 static_assert(bits >= 2 && bits <= 16, "Size must be power of 2 in [4, 65536]");
87
88 uint32_t idx = phase >> (32 - bits);
89 float f = static_cast<float>(phase & ((1u << (32 - bits)) - 1))
90 * (1.0f / static_cast<float>(1u << (32 - bits)));
91
92 float xm1 = table[idx > 0 ? idx - 1 : Size - 1];
93 float x0 = table[idx];
94 float x1 = table[idx + 1];
95 float x2 = table[idx + 2];
96
97 // Catmull-Rom coefficients
98 float c = (x1 - xm1) * 0.5f;
99 float v = x0 - x1;
100 float w = c + v;
101 float a = w + v + (x2 - x0) * 0.5f;
102 float b_neg = w + a;
103
104 // Horner's method: ((a*f - b_neg)*f + c)*f + x0
105 return ((a * f - b_neg) * f + c) * f + x0;
106}
107
108// ─── Float crossfade between two tables ──────────────────────────────
109//
110// balance: 0.0 = 100% table_a, 1.0 = 100% table_b.
111
112template<uint16_t Size>
113inline float crossfade(const float* table_a, const float* table_b,
114 uint32_t phase, float balance) {
115 float a = lookup_linear<Size>(table_a, phase);
116 float b = lookup_linear<Size>(table_b, phase);
117 return a + (b - a) * balance;
118}
119
120// ─── Float mix ───────────────────────────────────────────────────────
121
122inline float mix(float a, float b, float balance) {
123 return a + (b - a) * balance;
124}
125
126} // namespace sbl::dsp::lut
constexpr uint8_t log2_of(uint16_t n)
Definition lut.hpp:25
Lookup table functions.
Definition lut.hpp:19
uint16_t lookup_linear(const uint16_t *table, uint32_t phase)
Definition lut.hpp:39
float lookup_cubic(const float *table, uint32_t phase)
Definition lut.hpp:84
uint16_t mix(uint16_t a, uint16_t b, uint16_t balance)
Definition lut.hpp:52
float crossfade(const float *table_a, const float *table_b, uint32_t phase, float balance)
Definition lut.hpp:113
constexpr uint16_t FRAC16_MASK
Definition fixed.hpp:24
constexpr uint16_t U16_MAX
Definition fixed.hpp:18