MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Clock.hpp
Go to the documentation of this file.
1#pragma once
2
3namespace MayaFlux::Vruta {
4
5/**
6 * @class IClock
7 * @brief Abstract base interface for all clock types in the multimodal scheduling system
8 *
9 * IClock provides a unified interface that enables polymorphic handling of different
10 * timing domains while maintaining domain-specific implementations. This abstraction
11 * allows the TaskScheduler to work with any clock type (audio, visual, custom) through
12 * a common interface, enabling seamless multimodal coroutine scheduling.
13 *
14 * The interface supports:
15 * - Domain-agnostic time advancement through tick()
16 * - Position queries in domain-specific units
17 * - Time conversion to universal seconds
18 * - Rate queries for time calculations
19 * - Clock reset for testing and initialization
20 *
21 * Implementations must ensure thread-safety if used in multi-threaded contexts.
22 */
23class MAYAFLUX_API IClock {
24public:
25 virtual ~IClock() = default;
26
27 /**
28 * @brief Advance the clock by specified processing units
29 * @param units Number of domain-specific units to advance (samples, frames, etc.)
30 *
31 * This is the primary method for advancing time in any processing domain.
32 * The scheduler calls this after processing each block to maintain synchronization.
33 */
34 virtual void tick(uint64_t units = 1) = 0;
35
36 /**
37 * @brief Get current position in the domain's timeline
38 * @return Current position in domain-specific units (samples, frames, etc.)
39 *
40 * This position serves as the primary time reference for scheduling tasks
41 * in this domain. Routines use this value to determine execution timing.
42 */
43 [[nodiscard]] virtual uint64_t current_position() const = 0;
44
45 /**
46 * @brief Get current time converted to seconds
47 * @return Current time in seconds based on position and processing rate
48 *
49 * Provides a universal time representation for cross-domain coordination
50 * and user interface display purposes.
51 */
52 [[nodiscard]] virtual double current_time() const = 0;
53
54 /**
55 * @brief Get the processing rate for this timing domain
56 * @return Rate in units per second (sample rate, frame rate, etc.)
57 *
58 * Used for time conversions and calculating delays in domain-specific units.
59 */
60 [[nodiscard]] virtual uint32_t rate() const = 0;
61
62 /**
63 * @brief Reset clock to initial state (position 0)
64 *
65 * Resets the clock position to zero. Useful for testing, initialization,
66 * or when restarting processing sequences.
67 */
68 virtual void reset() = 0;
69};
70
71/**
72 * @class SampleClock
73 * @brief Sample-accurate timing system for audio processing domain
74 *
75 * SampleClock provides precise sample-based timing for audio processing tasks.
76 * Unlike wall-clock time which can be imprecise and subject to system load variations,
77 * SampleClock advances strictly based on audio samples processed, ensuring perfect
78 * synchronization with the audio stream.
79 *
80 * **Key Characteristics:**
81 * - **Sample-Accurate Precision**: Time measured in audio samples, not milliseconds
82 * - **Deterministic Timing**: Behavior is consistent regardless of system load
83 * - **Audio Stream Synchronization**: Advances in lockstep with audio processing
84 * - **Musical Timing**: Ideal for precisely timed musical events and audio effects
85 *
86 * This sample-based approach is essential for audio applications where precise timing
87 * is critical and operations must align perfectly with audio buffer boundaries.
88 * SampleClock serves as the authoritative timekeeper for the audio processing domain.
89 */
90class MAYAFLUX_API SampleClock : public IClock {
91public:
92 /**
93 * @brief Constructs a SampleClock with the specified sample rate
94 * @param sample_rate The number of samples per second (default: 48000)
95 *
96 * Creates a new SampleClock initialized at sample position 0.
97 * The sample rate determines the relationship between sample counts
98 * and real-time durations for all audio timing calculations.
99 */
100 SampleClock(uint64_t sample_rate = 48000);
101
102 /**
103 * @brief Advances the clock by the specified number of samples
104 * @param samples Number of samples to advance (default: 1)
105 *
106 * Called by the audio engine after processing each audio buffer.
107 * This maintains synchronization between the clock and the audio stream,
108 * ensuring sample-accurate timing for all scheduled audio tasks.
109 */
110 void tick(uint64_t samples = 1) override;
111
112 /**
113 * @brief Gets the current sample position
114 * @return The current sample count since clock initialization
115 *
116 * This sample count serves as the primary time reference for audio tasks.
117 * Audio routines use this value for precise scheduling and timing calculations.
118 */
119 [[nodiscard]] inline uint64_t current_sample() const { return current_position(); }
120
121 [[nodiscard]] uint64_t current_position() const override;
122
123 /**
124 * @brief Converts current sample position to seconds
125 * @return Current time in seconds
126 *
127 * Provides real-time representation useful for user interfaces,
128 * cross-domain synchronization, and integration with non-audio systems.
129 */
130 [[nodiscard]] double current_time() const override;
131
132 /**
133 * @brief Gets the audio sample rate
134 * @return Number of samples per second
135 *
136 * The sample rate defines the clock's time scale and is used for
137 * converting between samples and real-time durations.
138 */
139 [[nodiscard]] uint32_t sample_rate() const;
140
141 [[nodiscard]] uint32_t rate() const override;
142
143 void reset() override;
144
145private:
146 /**
147 * @brief Audio sample rate in samples per second
148 *
149 * Defines the clock's time scale, typically matching the audio engine's
150 * processing rate (e.g., 44100, 48000, or 96000 Hz).
151 */
153
154 /**
155 * @brief Current sample position counter
156 *
157 * Monotonically increasing counter representing processed samples.
158 * This is the authoritative time reference for the audio domain.
159 */
161};
162
163/**
164 * @class FrameClock
165 * @brief Frame-accurate timing system for visual processing domain
166 *
167 * Self-driven clock: Manages its own timing and advances autonomously.
168 * Unlike SampleClock which is driven by external audio callbacks,
169 * FrameClock actively manages frame timing and vsync coordination.
170 *
171 * **Threading Model:**
172 * - tick() is called from graphics thread ONLY
173 * - current_position() can be read from any thread (atomic)
174 * - Scheduler reads clock state but doesn't control advancement
175 *
176 * **Timing Characteristics:**
177 * - Advances based on wall-clock time, not external callbacks
178 * - Handles frame pacing and vsync timing
179 * - Measures actual FPS for diagnostics
180 * - Supports variable frame rates and adaptive timing
181 */
182class MAYAFLUX_API FrameClock : public IClock {
183public:
184 /**
185 * @brief Constructs a FrameClock with target frame rate
186 * @param target_fps Target frames per second (default: 60)
187 */
188 explicit FrameClock(uint32_t target_fps = 60);
189
190 /**
191 * @brief Advance clock by computing elapsed frames since last tick
192 *
193 * Self-driven advancement: Calculates how much time has passed
194 * since last tick and advances frame count accordingly.
195 *
196 * This is fundamentally different from SampleClock::tick(samples)
197 * which receives the sample count from external audio callback.
198 *
199 * Called from graphics thread loop. Thread-safe for single writer.
200 *
201 * @param forced_frames If non-zero, advances by this many frames
202 * instead of calculating from elapsed time.
203 * Used for testing or special cases.
204 */
205 void tick(uint64_t forced_frames = 0) override;
206
207 /**
208 * @brief Get current frame position (thread-safe read)
209 */
210 [[nodiscard]] uint64_t current_position() const override;
211
212 /**
213 * @brief Get current frame number (alias for current_position)
214 */
215 [[nodiscard]] inline uint64_t current_frame() const { return current_position(); }
216
217 /**
218 * @brief Convert current frame to seconds
219 */
220 [[nodiscard]] double current_time() const override;
221
222 /**
223 * @brief Get target frame rate
224 */
225 [[nodiscard]] uint32_t rate() const override;
226 [[nodiscard]] inline uint32_t frame_rate() const { return rate(); }
227
228 /**
229 * @brief Get actual measured FPS (exponentially smoothed)
230 */
231 [[nodiscard]] double get_measured_fps() const;
232
233 /**
234 * @brief Get time until next frame should occur
235 * @return Duration to sleep, or zero if already past next frame time
236 */
237 [[nodiscard]] std::chrono::nanoseconds time_until_next_frame() const;
238
239 /**
240 * @brief Wait (sleep) until next frame should occur
241 *
242 * Blocks calling thread until next frame time based on target FPS.
243 * Uses high-precision sleep for accurate frame pacing.
244 *
245 * Called from graphics thread loop after rendering to maintain
246 * consistent frame rate.
247 */
248 void wait_for_next_frame();
249
250 /**
251 * @brief Check if we're running behind target frame rate
252 * @return True if current time is past when next frame should occur
253 */
254 [[nodiscard]] bool is_frame_late() const;
255
256 /**
257 * @brief Get how many frames behind we are (if any)
258 * @return Number of frames we're running behind, or 0 if on time
259 */
260 [[nodiscard]] uint64_t get_frame_lag() const;
261
262 /**
263 * @brief Reset clock to initial state
264 */
265 void reset() override;
266
267 /**
268 * @brief Set new target frame rate (runtime adjustment)
269 * @param new_fps New target frames per second
270 */
271 void set_target_fps(uint32_t new_fps);
272
273private:
274 // Target timing
275 uint32_t m_target_fps;
276 std::chrono::nanoseconds m_frame_duration; // Cached: 1.0 / target_fps
277
278 // Current state (atomic for thread-safe reads)
279 std::atomic<uint64_t> m_current_frame;
280
281 // Timing tracking (written only from graphics thread)
282 std::chrono::steady_clock::time_point m_start_time;
283 std::chrono::steady_clock::time_point m_last_tick_time;
284 std::chrono::steady_clock::time_point m_next_frame_time;
285
286 // Measured FPS (smoothed, for diagnostics)
287 std::atomic<double> m_measured_fps;
288 static constexpr double FPS_SMOOTHING_ALPHA = 0.1; // Exponential smoothing factor
289
290 /**
291 * @brief Update measured FPS based on tick interval
292 * Called internally during tick()
293 */
294 void update_fps_measurement(std::chrono::steady_clock::time_point now);
295
296 /**
297 * @brief Calculate frames elapsed based on wall-clock time
298 * @param now Current time point
299 * @return Number of frames that should have elapsed
300 */
301 [[nodiscard]] uint64_t calculate_elapsed_frames(std::chrono::steady_clock::time_point now) const;
302
303 /**
304 * @brief Recalculate frame duration when target FPS changes
305 */
306 void recalculate_frame_duration();
307};
308
309/**
310 * @class CustomClock
311 * @brief Configurable timing system for custom processing domains
312 *
313 * CustomClock provides a flexible timing implementation for arbitrary processing
314 * domains that don't fit standard audio/visual patterns. It can be configured
315 * with any processing rate and unit name for specialized scheduling needs.
316 *
317 * **Use Cases:**
318 * - Custom processing pipelines with specific timing requirements
319 * - Integration with external systems that have unique timing patterns
320 * - Experimental processing domains during development
321 * - On-demand processing that doesn't follow regular timing patterns
322 */
323class MAYAFLUX_API CustomClock : public IClock {
324public:
325 /**
326 * @brief Constructs a CustomClock with configurable parameters
327 * @param processing_rate Rate in units per second (default: 1000)
328 * @param unit_name Name for the processing units (default: "units")
329 *
330 * Creates a flexible clock for custom processing domget_clockains.
331 * The unit name is stored for debugging and logging purposes.
332 */
333 CustomClock(uint64_t processing_rate = 1000, const std::string& unit_name = "units");
334
335 /**
336 * @brief Advances the clock by the specified number of custom units
337 * @param units Number of custom processing units to advance (default: 1)
338 */
339 void tick(uint64_t units = 1) override;
340
341 [[nodiscard]] uint64_t current_position() const override;
342
343 /**
344 * @brief Converts current position to seconds using custom rate
345 * @return Current time in seconds
346 */
347 [[nodiscard]] double current_time() const override;
348
349 [[nodiscard]] uint32_t rate() const override;
350
351 /**
352 * @brief Gets the name of the processing units
353 * @return String describing the unit type (e.g., "events", "packets", "cycles")
354 */
355 [[nodiscard]] const std::string& unit_name() const;
356
357 void reset() override;
358
359private:
360 /**
361 * @brief Custom processing rate in units per second
362 */
364
365 /**
366 * @brief Current position in custom units
367 */
369
370 /**
371 * @brief Name describing the custom units
372 */
373 std::string m_unit_name;
374};
375
376}
uint64_t m_current_position
Current position in custom units.
Definition Clock.hpp:368
std::string m_unit_name
Name describing the custom units.
Definition Clock.hpp:373
uint64_t m_processing_rate
Custom processing rate in units per second.
Definition Clock.hpp:363
Configurable timing system for custom processing domains.
Definition Clock.hpp:323
std::chrono::steady_clock::time_point m_last_tick_time
Definition Clock.hpp:283
uint32_t frame_rate() const
Definition Clock.hpp:226
std::atomic< double > m_measured_fps
Definition Clock.hpp:287
std::chrono::nanoseconds m_frame_duration
Definition Clock.hpp:276
std::chrono::steady_clock::time_point m_next_frame_time
Definition Clock.hpp:284
std::chrono::steady_clock::time_point m_start_time
Definition Clock.hpp:282
std::atomic< uint64_t > m_current_frame
Definition Clock.hpp:279
uint64_t current_frame() const
Get current frame number (alias for current_position)
Definition Clock.hpp:215
Frame-accurate timing system for visual processing domain.
Definition Clock.hpp:182
virtual uint32_t rate() const =0
Get the processing rate for this timing domain.
virtual void reset()=0
Reset clock to initial state (position 0)
virtual ~IClock()=default
virtual void tick(uint64_t units=1)=0
Advance the clock by specified processing units.
virtual uint64_t current_position() const =0
Get current position in the domain's timeline.
virtual double current_time() const =0
Get current time converted to seconds.
Abstract base interface for all clock types in the multimodal scheduling system.
Definition Clock.hpp:23
uint64_t current_sample() const
Gets the current sample position.
Definition Clock.hpp:119
uint64_t m_current_sample
Current sample position counter.
Definition Clock.hpp:160
uint32_t m_sample_rate
Audio sample rate in samples per second.
Definition Clock.hpp:152
Sample-accurate timing system for audio processing domain.
Definition Clock.hpp:90