libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
sdlaudioclock.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <promeki/namespace.h>
11#include <promeki/clock.h>
12#include <promeki/objectbase.h>
14#include <promeki/atomic.h>
15#include <promeki/string.h>
16
17#include <cstdint>
18#include <climits>
19
20PROMEKI_NAMESPACE_BEGIN
21
22class SDLAudioOutput;
23
102class SDLAudioClock : public Clock {
103 public:
117 struct Stats {
119 int64_t updateCount = 0;
121 int64_t checkpointResyncs = 0;
124 int64_t forwardSnaps = 0;
128 int64_t backDates = 0;
135 int64_t clampedRegressions = 0;
138 int64_t maxStepNs = 0;
142 int64_t maxBackDateNs = 0;
145 int64_t maxCallbackGapNs = 0;
146 };
147
168 explicit SDLAudioClock(SDLAudioOutput *output);
169
170 int64_t resolutionNs() const override;
171 ClockJitter jitter() const override;
172 double rateRatio() const override;
173
183 Stats stats() const { return _stats; }
184
193
194 protected:
195 Result<int64_t> raw() const override;
196 Error sleepUntilNs(int64_t targetNs) const override;
197 Error onPause(bool paused) override;
198
199 private:
200 // Core interpolation math shared between the normal
201 // raw() path and the pause-snapshot path in @ref
202 // onPause. Updates checkpoint state and logs stats.
203 int64_t computeRawNs() const;
204
205 void updateRateEstimate() const;
206 void reportMonitor() const;
207
208 // When @c _devicePaused is true, @ref raw returns
209 // @c _rawAtPause unchanged so wall time advancing
210 // through the paused interval does not drag the
211 // clock forward. Rate estimation and interpolation
212 // checkpoints are reset in @ref onPause on resume.
213 mutable Atomic<bool> _devicePaused{false};
214 mutable Atomic<int64_t> _rawAtPause{0};
215
216 ObjectBasePtr<SDLAudioOutput> _output;
217 double _bytesPerSec;
218 int64_t _resolutionNs;
219
220 // Rate-estimate state. Kept mutable so the update
221 // can run inside nowNs(). Caller is expected to use
222 // the clock from a single thread (the pacer or
223 // FrameSync worker).
224 mutable bool _rateBaselineValid = false;
225 mutable bool _rateEstimateStable = false;
226 mutable int64_t _rateBaselineWallNs = 0;
227 mutable int64_t _rateBaselineConsumed = 0;
228 mutable int64_t _lastRateUpdateWallNs = 0;
229 mutable double _filteredRateRatio = 1.0;
230
231 // Slowly-ramped, externally-visible ratio. Starts at
232 // 1.0 and drifts toward the internal LPF over a long
233 // time constant so that consumers (e.g. an audio
234 // resampler) can't detect the moment measurement
235 // stabilises as an audible pitch step.
236 mutable double _publishedRateRatio = 1.0;
237
238 // Smooth-nowNs interpolation state. Each advance
239 // of @c consumed (an SDL audio callback) is treated
240 // as a phase checkpoint; between checkpoints the
241 // clock extrapolates forward at the fast-tracking
242 // filtered rate. When a checkpoint lands behind
243 // the extrapolated value the wall anchor is
244 // back-dated rather than snapped, keeping the
245 // reported time monotonic while the rate filter
246 // converges on the true consumption rate.
247 mutable int64_t _checkpointConsumed = -1;
248 mutable int64_t _checkpointConsumedNs = 0;
249 mutable int64_t _checkpointWallNs = 0;
250
251 // Rate snapshot at the moment the checkpoint was
252 // anchored. Interpolation between checkpoints
253 // uses this value rather than the live rate
254 // filter output so that rate-filter updates
255 // mid-interval cannot pull the extrapolated
256 // position backward and stall the clock on the
257 // monotonicity clamp. The live filter still
258 // drives @ref rateRatio and still takes effect at
259 // the next checkpoint update.
260 mutable double _checkpointRate = 1.0;
261
262 // Last value returned from @ref nowNs. Used as a
263 // final clamp to defend the monotonicity contract
264 // against sub-nanosecond floating-point rounding
265 // in the back-date arithmetic.
266 mutable int64_t _lastReportedNs = INT64_MIN;
267
268 // Latches true the first time SDL reports a non-
269 // zero @c consumed. Gates the silent-startup
270 // path: before audio has started, @ref nowNs holds
271 // at zero rather than extrapolating wall time
272 // forward (which would lie to A/V sync and force
273 // an enormous back-date when the first real chunk
274 // finally lands). Once latched, transient drops
275 // of @c consumed back to zero are treated as noise
276 // and routed through the normal back-date path so
277 // monotonicity is preserved.
278 mutable bool _audioStarted = false;
279
280 // Monitoring state. @c _stats accumulates runtime
281 // counters; @c _lastCallbackWallNs is used to
282 // compute callback-gap maxima; @c _monitor fires
283 // once per second from inside @ref nowNs and
284 // publishes an error/debug summary through the
285 // logger; @c _monitorSnapshot holds the previous
286 // tick's counters so the monitor can report deltas.
287 // @c _prevRateStable latches the previous rate-
288 // filter stable flag so the maxes can be reset once
289 // at the warmup→stable transition — startup
290 // transients dominate those maxes and would mask
291 // later, real misbehavior if left in place.
292 mutable Stats _stats;
293 mutable int64_t _lastCallbackWallNs = 0;
294 mutable PeriodicCallback _monitor;
295 mutable Stats _monitorSnapshot;
296 mutable bool _prevRateStable = false;
297};
298
299PROMEKI_NAMESPACE_END
Clock driven by an SDL audio device's consumption rate.
Definition sdlaudioclock.h:102
Stats stats() const
Returns a snapshot of the cumulative runtime counters.
Definition sdlaudioclock.h:183
void resetStats()
Resets all counters in stats to zero.
SDLAudioClock(SDLAudioOutput *output)
Constructs an SDL audio clock bound to output.
Manages an SDL3 audio output device for playback.
Definition sdlaudiooutput.h:64
Runtime counters for the clock's smoothing machinery.
Definition sdlaudioclock.h:117