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