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