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
audio_budget.hpp
Go to the documentation of this file.
1/**
2 * @file audio_budget.hpp
3 * @brief Audio callback tick budget tracker
4 *
5 * Measures per-block tick consumption in audio callbacks using the
6 * platform timing backend. Tracks average (EWMA), peak, min, overrun count,
7 * and a per-block load histogram (11 buckets, 10% each).
8 *
9 * Gated behind SBL_PROFILING_ENABLED. When the define is absent, all
10 * methods compile to no-ops and the class has zero data members -- no
11 * binary impact whatsoever.
12 *
13 * Usage:
14 * static sbl::diagnostics::AudioBudget budget;
15 * budget.init(48000, 48);
16 *
17 * void audio_callback(int32_t* tx, const int32_t* rx, uint16_t frames) {
18 * budget.on_block_start();
19 * // ... DSP ...
20 * budget.on_block_end();
21 * }
22 *
23 * // In control loop:
24 * budget.avg_load() // [0.0, 1.0+]
25 * budget.peak_load() // worst-case since last reset
26 * budget.overruns() // count of callbacks that exceeded budget
27 */
28#ifndef SBL_COMMON_DIAGNOSTICS_AUDIO_BUDGET_HPP_
29#define SBL_COMMON_DIAGNOSTICS_AUDIO_BUDGET_HPP_
30
31#include <cstdint>
32
33#include <cstddef>
34
35#ifdef SBL_PROFILING_ENABLED
37#endif
38
40
42 static constexpr size_t HIST_BUCKETS = 11;
43
44public:
45 /// Configure budget from audio parameters.
46 /// budget_ticks = (tick_rate() / sample_rate) * block_size
47 void init(uint32_t sample_rate, uint16_t block_size) {
48#ifdef SBL_PROFILING_ENABLED
49 budget_ = (tick_rate() / sample_rate) * block_size;
50 reset();
51#else
52 (void)sample_rate; (void)block_size;
53#endif
54 }
55
56 /// Mark the start of audio processing (call first in callback)
58#ifdef SBL_PROFILING_ENABLED
59 start_ = ticks();
60#endif
61 }
62
63 /// Mark the end of audio processing (call last in callback)
64 void on_block_end() {
65#ifdef SBL_PROFILING_ENABLED
66 uint32_t elapsed = ticks() - start_;
67 ++count_;
68
69 if (elapsed > max_) max_ = elapsed;
70 if (elapsed < min_) min_ = elapsed;
71 if (elapsed > budget_) ++overruns_;
72
73 // EWMA smoothing, alpha = 1/128 (~130ms settling at 1ms blocks)
74 avg_ += (static_cast<float>(elapsed) - avg_) * (1.0f / 128.0f);
75
76 // Histogram: bucket index = load_percent / 10, clamped to [0, HIST_BUCKETS-1]
77 uint32_t pct = (elapsed * 100) / budget_;
78 uint32_t bucket = pct / 10;
79 if (bucket >= HIST_BUCKETS) bucket = HIST_BUCKETS - 1;
80 hist_[bucket]++;
81#endif
82 }
83
84 // --- Queries (safe to call from control context) ---
85
86 /// Average load as fraction [0.0, 1.0+]
87 float avg_load() const {
88#ifdef SBL_PROFILING_ENABLED
89 return avg_ / static_cast<float>(budget_);
90#else
91 return 0.0f;
92#endif
93 }
94
95 /// Peak (worst-case) load since last reset
96 float peak_load() const {
97#ifdef SBL_PROFILING_ENABLED
98 return static_cast<float>(max_) / static_cast<float>(budget_);
99#else
100 return 0.0f;
101#endif
102 }
103
104 /// Minimum load since last reset
105 float min_load() const {
106#ifdef SBL_PROFILING_ENABLED
107 return static_cast<float>(min_) / static_cast<float>(budget_);
108#else
109 return 0.0f;
110#endif
111 }
112
113 /// Raw tick counts
114 uint32_t avg_ticks() const {
115#ifdef SBL_PROFILING_ENABLED
116 return static_cast<uint32_t>(avg_);
117#else
118 return 0;
119#endif
120 }
121
122 uint32_t peak_ticks() const {
123#ifdef SBL_PROFILING_ENABLED
124 return max_;
125#else
126 return 0;
127#endif
128 }
129
130 uint32_t min_ticks() const {
131#ifdef SBL_PROFILING_ENABLED
132 return min_;
133#else
134 return 0;
135#endif
136 }
137
138 uint32_t budget_ticks() const {
139#ifdef SBL_PROFILING_ENABLED
140 return budget_;
141#else
142 return 0;
143#endif
144 }
145
146 /// Number of callbacks that exceeded the tick budget
147 uint32_t overruns() const {
148#ifdef SBL_PROFILING_ENABLED
149 return overruns_;
150#else
151 return 0;
152#endif
153 }
154
155 /// Total callbacks measured
156 uint32_t count() const {
157#ifdef SBL_PROFILING_ENABLED
158 return count_;
159#else
160 return 0;
161#endif
162 }
163
164 // --- Histogram (10% buckets: 0-10%, 10-20%, ..., 90-100%, >100%) ---
165
166 /// Number of histogram buckets
167 static constexpr size_t hist_size() { return HIST_BUCKETS; }
168
169 /// Read histogram bucket count (0-indexed, last bucket is >100%)
170 uint32_t hist_bucket(size_t i) const {
171#ifdef SBL_PROFILING_ENABLED
172 return (i < HIST_BUCKETS) ? hist_[i] : 0;
173#else
174 (void)i; return 0;
175#endif
176 }
177
178 /// Reset statistics (budget preserved)
179 void reset() {
180#ifdef SBL_PROFILING_ENABLED
181 avg_ = 0.0f;
182 max_ = 0;
183 min_ = budget_;
184 overruns_ = 0;
185 count_ = 0;
186 for (auto& b : hist_) b = 0;
187#endif
188 }
189
190private:
191#ifdef SBL_PROFILING_ENABLED
192 uint32_t start_ = 0;
193 uint32_t budget_ = 0;
194 float avg_ = 0.0f;
195 uint32_t max_ = 0;
196 uint32_t min_ = 0;
197 uint32_t overruns_ = 0;
198 uint32_t count_ = 0;
199
200 uint32_t hist_[HIST_BUCKETS] = {};
201#endif
202};
203
204} // namespace sbl::diagnostics
205
206#endif // SBL_COMMON_DIAGNOSTICS_AUDIO_BUDGET_HPP_
void init(uint32_t sample_rate, uint16_t block_size)
uint32_t overruns() const
Number of callbacks that exceeded the tick budget.
void reset()
Reset statistics (budget preserved)
void on_block_start()
Mark the start of audio processing (call first in callback)
void on_block_end()
Mark the end of audio processing (call last in callback)
static constexpr size_t hist_size()
Number of histogram buckets.
uint32_t count() const
Total callbacks measured.
float avg_load() const
Average load as fraction [0.0, 1.0+].
uint32_t avg_ticks() const
Raw tick counts.
float min_load() const
Minimum load since last reset.
float peak_load() const
Peak (worst-case) load since last reset.
uint32_t hist_bucket(size_t i) const
Read histogram bucket count (0-indexed, last bucket is >100%)
Diagnostics and profiling.
uint32_t ticks()
Read current tick count (free-running, wraps at 2^32).
Definition ticks.hpp:29
constexpr uint32_t tick_rate()
Tick rate in Hz (ticks per second).
Definition ticks.hpp:32
Tick counter interface for profiling.