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
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#ifndef SBL_DSP_TYPES_CONVERT_HPP_
10#define SBL_DSP_TYPES_CONVERT_HPP_
11
12#include <cstdint>
13
15
16// NaN detection macro — increments counter when profiling is enabled,
17// compiles to nothing otherwise. Used in interleave_from_float().
18#ifdef SBL_PROFILING_ENABLED
19#define SBL_NAN_DETECTED() (++sbl::dsp::types::nan_detect_count())
20#else
21#define SBL_NAN_DETECTED() ((void)0)
22#endif
23
24namespace sbl::dsp::types {
25
27
28// 24-bit audio range: ±8,388,607
29inline constexpr float SAMPLE_SCALE_F = 8388607.0f;
30inline constexpr float SAMPLE_SCALE_INV_F = 1.0f / 8388607.0f;
31
32// ─── Single-sample conversion ────────────────────────────────────────
33
34inline float to_float(Sample s) {
35 return static_cast<float>(s) * SAMPLE_SCALE_INV_F;
36}
37
38inline Sample to_sample(float f) {
39 return static_cast<Sample>(f * SAMPLE_SCALE_F);
40}
41
42// ─── Block conversion (mono) ─────────────────────────────────────────
43
44inline void to_float(const Sample* in, float* out, uint16_t frames) {
45 for (uint16_t i = 0; i < frames; ++i) {
46 out[i] = static_cast<float>(in[i]) * SAMPLE_SCALE_INV_F;
47 }
48}
49
50inline void to_sample(const float* in, Sample* out, uint16_t frames) {
51 for (uint16_t i = 0; i < frames; ++i) {
52 out[i] = static_cast<Sample>(in[i] * SAMPLE_SCALE_F);
53 }
54}
55
56// ─── Stereo interleave/deinterleave with conversion ──────────────────
57//
58// DMA buffers are interleaved: [L0, R0, L1, R1, ...] as int32_t.
59// Audio processing uses separate float L/R buffers in [-1.0, 1.0].
60
61inline void deinterleave_to_float(const int32_t* interleaved,
62 float* left, float* right,
63 uint16_t frames) {
64 for (uint16_t i = 0; i < frames; ++i) {
65 // Sign-extend from 24-bit to 32-bit. The SAI stores 24-bit data
66 // right-aligned in 32-bit DMA words without sign extension, so a
67 // negative 24-bit sample (e.g. 0x00E27772) appears as a large
68 // positive int32_t. Shift left 8 to put bit 23 into bit 31 (sign
69 // position), then arithmetic shift right 8 to sign-extend.
70 int32_t l = (interleaved[i * 2] << 8) >> 8;
71 int32_t r = (interleaved[i * 2 + 1] << 8) >> 8;
72 left[i] = static_cast<float>(l) * SAMPLE_SCALE_INV_F;
73 right[i] = static_cast<float>(r) * SAMPLE_SCALE_INV_F;
74 }
75}
76
77// NaN detection counter — gated behind SBL_PROFILING_ENABLED.
78// NaN in the audio path indicates a bug upstream (uninitialized buffer,
79// division by zero, corrupted filter state). The counter helps identify
80// the problem without adding runtime cost in release builds.
81#ifdef SBL_PROFILING_ENABLED
82inline uint32_t& nan_detect_count() {
83 static uint32_t count = 0;
84 return count;
85}
86#endif
87
88// Hard clamps to [-1.0, 1.0] before conversion — the ONE place clamping
89// happens. Signals are free to exceed unity anywhere in the float pipeline;
90// only the final DMA output enforces range.
91//
92// NaN guard: IEEE 754 NaN fails all comparisons, so the clamp below would
93// pass NaN through to static_cast<int32_t>(NaN * SCALE), which is undefined
94// behavior. We check NaN first and replace with silence (0.0f). This is the
95// audio equivalent of a fuse — you lose signal, but you protect hearing.
96inline void interleave_from_float(const float* left, const float* right,
97 int32_t* interleaved,
98 uint16_t frames) {
99 for (uint16_t i = 0; i < frames; ++i) {
100 float l = left[i];
101 float r = right[i];
102 // NaN check: NaN != NaN is true per IEEE 754
103 if (l != l) { l = 0.0f; SBL_NAN_DETECTED(); }
104 if (r != r) { r = 0.0f; SBL_NAN_DETECTED(); }
105 if (l > 1.0f) l = 1.0f;
106 if (l < -1.0f) l = -1.0f;
107 if (r > 1.0f) r = 1.0f;
108 if (r < -1.0f) r = -1.0f;
109 interleaved[i * 2] = static_cast<int32_t>(l * SAMPLE_SCALE_F);
110 interleaved[i * 2 + 1] = static_cast<int32_t>(r * SAMPLE_SCALE_F);
111 }
112}
113
114} // namespace sbl::dsp::types
115
116#endif // SBL_DSP_TYPES_CONVERT_HPP_
#define SBL_NAN_DETECTED()
Definition convert.hpp:21
Audio common types and configuration.
Sample rate, sample formats, blocks and frames.
Definition block.hpp:20
Sample to_sample(float f)
Definition convert.hpp:38
float to_float(Sample s)
Definition convert.hpp:34
constexpr float SAMPLE_SCALE_INV_F
Definition convert.hpp:30
void interleave_from_float(const float *left, const float *right, int32_t *interleaved, uint16_t frames)
Definition convert.hpp:96
void deinterleave_to_float(const int32_t *interleaved, float *left, float *right, uint16_t frames)
Definition convert.hpp:61
constexpr float SAMPLE_SCALE_F
Definition convert.hpp:29
int32_t Sample
Definition types.hpp:49