MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Clock.cpp
Go to the documentation of this file.
1#include "Clock.hpp"
2
4
5#include "ChronUtils.hpp"
6
7namespace MayaFlux::Vruta {
8
9uint32_t s_registered_sample_rate { 48000 };
11
12SampleClock::SampleClock(uint64_t sample_rate)
13 : m_sample_rate(sample_rate)
14 , m_current_sample(0)
15{
16}
17
18void SampleClock::tick(uint64_t samples)
19{
20 m_current_sample += samples;
21}
22
24{
25 return m_current_sample;
26}
27
32
34{
35 return m_sample_rate;
36}
37
38uint32_t SampleClock::rate() const
39{
40 return m_sample_rate;
41}
42
44{
46}
47
48FrameClock::FrameClock(uint32_t target_fps)
49 : m_target_fps(target_fps)
50 , m_current_frame(0)
51 , m_start_time(std::chrono::steady_clock::now())
52 , m_last_tick_time(std::chrono::steady_clock::now())
53 , m_next_frame_time(std::chrono::steady_clock::now())
54 , m_measured_fps(static_cast<double>(target_fps))
55{
57}
58
59void FrameClock::tick(uint64_t forced_frames)
60{
61 auto now = std::chrono::steady_clock::now();
62
63 uint64_t frames_to_advance = forced_frames > 0 ? forced_frames : calculate_elapsed_frames(now);
64
65 if (frames_to_advance > 0) {
66 m_current_frame.fetch_add(frames_to_advance, std::memory_order_release);
68 m_last_tick_time = now;
70 }
71}
72
74{
75 return m_current_frame.load(std::memory_order_acquire);
76}
77
82
83uint32_t FrameClock::rate() const
84{
85 return m_target_fps;
86}
87
89{
90 return m_measured_fps.load(std::memory_order_acquire);
91}
92
93std::chrono::nanoseconds FrameClock::time_until_next_frame() const
94{
95 auto now = std::chrono::steady_clock::now();
96 auto until_next = m_next_frame_time - now;
97
98 if (until_next.count() < 0) {
99 return std::chrono::nanoseconds(0);
100 }
101
102 return std::chrono::duration_cast<std::chrono::nanoseconds>(until_next);
103}
104
106{
107 auto now = std::chrono::steady_clock::now();
108 return now > m_next_frame_time;
109}
110
112{
113 auto now = std::chrono::steady_clock::now();
114
115 if (now <= m_next_frame_time) {
116 return 0;
117 }
118
119 auto elapsed = now - m_next_frame_time;
120 auto frames_behind = elapsed / m_frame_duration;
121 return static_cast<uint64_t>(frames_behind);
122}
123
125{
126 m_current_frame.store(0, std::memory_order_release);
127 m_start_time = std::chrono::steady_clock::now();
130 m_measured_fps.store(static_cast<double>(m_target_fps), std::memory_order_release);
131}
132
133void FrameClock::set_target_fps(uint32_t new_fps)
134{
135 if (new_fps == 0 || new_fps > 1000) {
136 error<std::runtime_error>(Journal::Component::Vruta,
138 std::source_location::current(),
139 "FrameClock", "set_target_fps", "Invalid FPS value: {}",
140 new_fps);
141 }
142
143 if (new_fps == m_target_fps)
144 return;
145
146 m_target_fps = new_fps;
148
149 auto frames_elapsed = static_cast<double>(m_current_frame.load());
150 auto expected_time = m_start_time + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<double>(frames_elapsed / m_target_fps));
151
152 m_next_frame_time = expected_time + m_frame_duration;
153}
154
155void FrameClock::update_fps_measurement(std::chrono::steady_clock::time_point now)
156{
157 auto dt = std::chrono::duration<double>(now - m_last_tick_time).count();
158
159 if (dt > 0.0 && dt <= 1.0) {
160 double instantaneous_fps = 1.0 / dt;
161
162 double current_measured = m_measured_fps.load(std::memory_order_acquire);
163 double new_measured = FPS_SMOOTHING_ALPHA * instantaneous_fps + (1.0 - FPS_SMOOTHING_ALPHA) * current_measured;
164
165 m_measured_fps.store(new_measured, std::memory_order_release);
166 }
167}
168
169uint64_t FrameClock::calculate_elapsed_frames(std::chrono::steady_clock::time_point now) const
170{
171 auto time_since_last_tick = now - m_last_tick_time;
172 return static_cast<uint64_t>(time_since_last_tick / m_frame_duration);
173}
174
176{
177 double ns_per_frame = 1'000'000'000.0 / m_target_fps;
178 m_frame_duration = std::chrono::nanoseconds(static_cast<uint64_t>(ns_per_frame));
179}
180
182{
183 auto sleep_duration = time_until_next_frame();
184 if (sleep_duration > std::chrono::microseconds(100)) {
185 std::this_thread::sleep_until(m_next_frame_time);
186
187 } else if (sleep_duration > std::chrono::nanoseconds(0)) {
188 auto wake_time = std::chrono::steady_clock::now() + sleep_duration;
189
190 while (std::chrono::steady_clock::now() < wake_time) {
191 std::this_thread::yield();
192 }
193 }
194}
195
196// ========================================
197// CustomClock Implementation
198// ========================================
199
200CustomClock::CustomClock(uint64_t processing_rate, const std::string& unit_name)
201 : m_processing_rate(processing_rate)
202 , m_current_position(0)
203 , m_unit_name(unit_name)
204{
205}
206
207void CustomClock::tick(uint64_t units)
208{
209 m_current_position += units;
210}
211
213{
214 return m_current_position;
215}
216
221
222uint32_t CustomClock::rate() const
223{
224 return m_processing_rate;
225}
226
227const std::string& CustomClock::unit_name() const
228{
229 return m_unit_name;
230}
231
233{
235}
236}
const std::string & unit_name() const
Gets the name of the processing units.
Definition Clock.cpp:227
double current_time() const override
Converts current position to seconds using custom rate.
Definition Clock.cpp:217
uint64_t m_current_position
Current position in custom units.
Definition Clock.hpp:368
void reset() override
Reset clock to initial state (position 0)
Definition Clock.cpp:232
void tick(uint64_t units=1) override
Advances the clock by the specified number of custom units.
Definition Clock.cpp:207
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
uint32_t rate() const override
Get the processing rate for this timing domain.
Definition Clock.cpp:222
uint64_t current_position() const override
Get current position in the domain's timeline.
Definition Clock.cpp:212
CustomClock(uint64_t processing_rate=1000, const std::string &unit_name="units")
Constructs a CustomClock with configurable parameters.
Definition Clock.cpp:200
std::chrono::steady_clock::time_point m_last_tick_time
Definition Clock.hpp:283
void recalculate_frame_duration()
Recalculate frame duration when target FPS changes.
Definition Clock.cpp:175
bool is_frame_late() const
Check if we're running behind target frame rate.
Definition Clock.cpp:105
static constexpr double FPS_SMOOTHING_ALPHA
Definition Clock.hpp:288
void reset() override
Reset clock to initial state.
Definition Clock.cpp:124
std::atomic< double > m_measured_fps
Definition Clock.hpp:287
uint64_t current_position() const override
Get current frame position (thread-safe read)
Definition Clock.cpp:73
uint32_t rate() const override
Get target frame rate.
Definition Clock.cpp:83
double get_measured_fps() const
Get actual measured FPS (exponentially smoothed)
Definition Clock.cpp:88
std::chrono::nanoseconds m_frame_duration
Definition Clock.hpp:276
std::chrono::steady_clock::time_point m_next_frame_time
Definition Clock.hpp:284
double current_time() const override
Convert current frame to seconds.
Definition Clock.cpp:78
FrameClock(uint32_t target_fps=60)
Constructs a FrameClock with target frame rate.
Definition Clock.cpp:48
uint64_t calculate_elapsed_frames(std::chrono::steady_clock::time_point now) const
Calculate frames elapsed based on wall-clock time.
Definition Clock.cpp:169
std::chrono::steady_clock::time_point m_start_time
Definition Clock.hpp:282
uint64_t get_frame_lag() const
Get how many frames behind we are (if any)
Definition Clock.cpp:111
std::atomic< uint64_t > m_current_frame
Definition Clock.hpp:279
void set_target_fps(uint32_t new_fps)
Set new target frame rate (runtime adjustment)
Definition Clock.cpp:133
void wait_for_next_frame()
Wait (sleep) until next frame should occur.
Definition Clock.cpp:181
std::chrono::nanoseconds time_until_next_frame() const
Get time until next frame should occur.
Definition Clock.cpp:93
void update_fps_measurement(std::chrono::steady_clock::time_point now)
Update measured FPS based on tick interval Called internally during tick()
Definition Clock.cpp:155
void tick(uint64_t forced_frames=0) override
Advance clock by computing elapsed frames since last tick.
Definition Clock.cpp:59
void tick(uint64_t samples=1) override
Advances the clock by the specified number of samples.
Definition Clock.cpp:18
uint32_t sample_rate() const
Gets the audio sample rate.
Definition Clock.cpp:33
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
uint64_t current_position() const override
Get current position in the domain's timeline.
Definition Clock.cpp:23
void reset() override
Reset clock to initial state (position 0)
Definition Clock.cpp:43
double current_time() const override
Converts current sample position to seconds.
Definition Clock.cpp:28
uint32_t rate() const override
Get the processing rate for this timing domain.
Definition Clock.cpp:38
SampleClock(uint64_t sample_rate=48000)
Constructs a SampleClock with the specified sample rate.
Definition Clock.cpp:12
@ ClockSync
Clock synchronization (SampleClock, FrameClock coordination)
@ Vruta
Coroutines, schedulers, clocks, task management.
MAYAFLUX_API uint32_t s_registered_frame_rate
Definition Clock.cpp:10
MAYAFLUX_API uint32_t s_registered_sample_rate
Definition Clock.cpp:9
double units_to_seconds(uint64_t units, uint32_t rate)
Convert processing units to seconds for any rate.