Sound Byte Libs 0.5.1-121-g3358a44
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#ifndef SBL_DSP_MATH_LUT_HPP_
14#define SBL_DSP_MATH_LUT_HPP_
15
16#include <cstdint>
17
18namespace sbl::dsp::lut {
19
20// ─── Compile-time helpers ──────────────────────────────────────────────
21
22namespace detail {
23
24constexpr uint8_t log2_of(uint16_t n) {
25 uint8_t r = 0;
26 while (n > 1) { n >>= 1; ++r; }
27 return r;
28}
29
30} // namespace detail
31
32// ═══════════════════════════════════════════════════════════════════════
33// Float lookup functions
34//
35// For float tables in [-1.0, 1.0]. Same phase convention (uint32_t full
36// range = one cycle), dramatically simpler math — no Q16 fractions, no
37// int64 intermediates.
38// ═══════════════════════════════════════════════════════════════════════
39
40// ─── Float linear interpolation ──────────────────────────────────────
41
42/// @note All functions in sbl::dsp::lut are ISR-safe — bounded computation, no I/O.
43
44template<uint16_t Size>
45inline float lookup_linear(const float* table, uint32_t phase) {
46 constexpr uint8_t bits = detail::log2_of(Size);
47 static_assert(bits >= 1 && bits <= 16, "Size must be power of 2 in [2, 65536]");
48
49 uint32_t idx = phase >> (32 - bits);
50 float frac = static_cast<float>(phase & ((1u << (32 - bits)) - 1))
51 * (1.0f / static_cast<float>(1u << (32 - bits)));
52 return table[idx] + (table[idx + 1] - table[idx]) * frac;
53}
54
55// ─── Float cubic Hermite interpolation ───────────────────────────────
56
57template<uint16_t Size>
58inline float lookup_cubic(const float* table, uint32_t phase) {
59 constexpr uint8_t bits = detail::log2_of(Size);
60 static_assert(bits >= 2 && bits <= 16, "Size must be power of 2 in [4, 65536]");
61
62 uint32_t idx = phase >> (32 - bits);
63 float f = static_cast<float>(phase & ((1u << (32 - bits)) - 1))
64 * (1.0f / static_cast<float>(1u << (32 - bits)));
65
66 float xm1 = table[idx > 0 ? idx - 1 : Size - 1];
67 float x0 = table[idx];
68 float x1 = table[idx + 1];
69 float x2 = table[idx + 2];
70
71 // Catmull-Rom coefficients
72 float c = (x1 - xm1) * 0.5f;
73 float v = x0 - x1;
74 float w = c + v;
75 float a = w + v + (x2 - x0) * 0.5f;
76 float b_neg = w + a;
77
78 // Horner's method: ((a*f - b_neg)*f + c)*f + x0
79 return ((a * f - b_neg) * f + c) * f + x0;
80}
81
82// ─── Float crossfade between two tables ──────────────────────────────
83//
84// balance: 0.0 = 100% table_a, 1.0 = 100% table_b.
85
86template<uint16_t Size>
87inline float crossfade(const float* table_a, const float* table_b,
88 uint32_t phase, float balance) {
89 float a = lookup_linear<Size>(table_a, phase);
90 float b = lookup_linear<Size>(table_b, phase);
91 return a + (b - a) * balance;
92}
93
94} // namespace sbl::dsp::lut
95
96#endif // SBL_DSP_MATH_LUT_HPP_
constexpr uint8_t log2_of(uint16_t n)
Definition lut.hpp:24
Lookup-table access and the generated tables.
Definition lut.hpp:18
float lookup_cubic(const float *table, uint32_t phase)
Definition lut.hpp:58
float lookup_linear(const float *table, uint32_t phase)
Definition lut.hpp:45
float crossfade(const float *table_a, const float *table_b, uint32_t phase, float balance)
Definition lut.hpp:87