Sound Byte Libs 29c5ff3
C++ firmware library for audio applications on 32-bit ARM Cortex-M processors
Loading...
Searching...
No Matches
convert.hpp
Go to the documentation of this file.
1// sbl/dsp/convert.hpp — DMA boundary conversion utilities
2//
3// These are the ONLY int↔float conversion functions in a float pipeline.
4// Widgets process entirely in float [-1.0, 1.0]. Conversion happens at the
5// DMA boundary: interleaved int32 (24-bit audio) ↔ separate float L/R buffers.
6//
7// See FDP-024 for design rationale.
8
9#pragma once
10
11#include <cstdint>
12
13#include <sbl/dsp/types.hpp>
14
15// NaN detection macro — increments counter when profiling is enabled,
16// compiles to nothing otherwise. Used in interleave_from_float().
17#ifdef SBL_PROFILING_ENABLED
18#define SBL_NAN_DETECTED() (++sbl::dsp::nan_detect_count())
19#else
20#define SBL_NAN_DETECTED() ((void)0)
21#endif
22
23namespace sbl::dsp {
24
25// 24-bit audio range: ±8,388,607
26inline constexpr float SAMPLE_SCALE_F = 8388607.0f;
27inline constexpr float SAMPLE_SCALE_INV_F = 1.0f / 8388607.0f;
28
29// ─── Single-sample conversion ────────────────────────────────────────
30
31inline float to_float(Sample s) {
32 return static_cast<float>(s) * SAMPLE_SCALE_INV_F;
33}
34
35inline Sample to_sample(float f) {
36 return static_cast<Sample>(f * SAMPLE_SCALE_F);
37}
38
39// ─── Block conversion (mono) ─────────────────────────────────────────
40
41inline void to_float(const Sample* in, float* out, uint16_t frames) {
42 for (uint16_t i = 0; i < frames; ++i) {
43 out[i] = static_cast<float>(in[i]) * SAMPLE_SCALE_INV_F;
44 }
45}
46
47inline void to_sample(const float* in, Sample* out, uint16_t frames) {
48 for (uint16_t i = 0; i < frames; ++i) {
49 out[i] = static_cast<Sample>(in[i] * SAMPLE_SCALE_F);
50 }
51}
52
53// ─── Stereo interleave/deinterleave with conversion ──────────────────
54//
55// DMA buffers are interleaved: [L0, R0, L1, R1, ...] as int32_t.
56// Audio processing uses separate float L/R buffers in [-1.0, 1.0].
57
58inline void deinterleave_to_float(const int32_t* interleaved,
59 float* left, float* right,
60 uint16_t frames) {
61 for (uint16_t i = 0; i < frames; ++i) {
62 left[i] = static_cast<float>(interleaved[i * 2]) * SAMPLE_SCALE_INV_F;
63 right[i] = static_cast<float>(interleaved[i * 2 + 1]) * SAMPLE_SCALE_INV_F;
64 }
65}
66
67// NaN detection counter — gated behind SBL_PROFILING_ENABLED.
68// NaN in the audio path indicates a bug upstream (uninitialized buffer,
69// division by zero, corrupted filter state). The counter helps identify
70// the problem without adding runtime cost in release builds.
71#ifdef SBL_PROFILING_ENABLED
72inline uint32_t& nan_detect_count() {
73 static uint32_t count = 0;
74 return count;
75}
76#endif
77
78// Hard clamps to [-1.0, 1.0] before conversion — the ONE place clamping
79// happens. Signals are free to exceed unity anywhere in the float pipeline;
80// only the final DMA output enforces range.
81//
82// NaN guard: IEEE 754 NaN fails all comparisons, so the clamp below would
83// pass NaN through to static_cast<int32_t>(NaN * SCALE), which is undefined
84// behavior. We check NaN first and replace with silence (0.0f). This is the
85// audio equivalent of a fuse — you lose signal, but you protect hearing.
86inline void interleave_from_float(const float* left, const float* right,
87 int32_t* interleaved,
88 uint16_t frames) {
89 for (uint16_t i = 0; i < frames; ++i) {
90 float l = left[i];
91 float r = right[i];
92 // NaN check: NaN != NaN is true per IEEE 754
93 if (l != l) { l = 0.0f; SBL_NAN_DETECTED(); }
94 if (r != r) { r = 0.0f; SBL_NAN_DETECTED(); }
95 if (l > 1.0f) l = 1.0f;
96 if (l < -1.0f) l = -1.0f;
97 if (r > 1.0f) r = 1.0f;
98 if (r < -1.0f) r = -1.0f;
99 interleaved[i * 2] = static_cast<int32_t>(l * SAMPLE_SCALE_F);
100 interleaved[i * 2 + 1] = static_cast<int32_t>(r * SAMPLE_SCALE_F);
101 }
102}
103
104} // namespace sbl::dsp
#define SBL_NAN_DETECTED()
Definition convert.hpp:20
DSP atoms for audio signal processing.
Definition allpass.hpp:22
Sample to_sample(float f)
Definition convert.hpp:35
constexpr float SAMPLE_SCALE_INV_F
Definition convert.hpp:27
constexpr float SAMPLE_SCALE_F
Definition convert.hpp:26
float to_float(Sample s)
Definition convert.hpp:31
int32_t Sample
Definition types.hpp:11
void deinterleave_to_float(const int32_t *interleaved, float *left, float *right, uint16_t frames)
Definition convert.hpp:58
void interleave_from_float(const float *left, const float *right, int32_t *interleaved, uint16_t frames)
Definition convert.hpp:86