Skip to content

Commit 2914d85

Browse files
committed
Add API functions to set and retrieve frame times
Setting a custom frame time will result in stable animation speeds if projectM does not render at real-time speeds, e.g. while encoding a video from an audio file as fast as possible.
1 parent bd2b1ba commit 2914d85

File tree

6 files changed

+109
-2
lines changed

6 files changed

+109
-2
lines changed

src/api/include/projectM-4/parameters.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,38 @@ PROJECTM_EXPORT void projectm_set_texture_search_paths(projectm_handle instance,
4545
const char** texture_search_paths,
4646
size_t count);
4747

48+
/**
49+
* @brief Sets a user-specified frame time in fractional seconds.
50+
*
51+
* Setting this to any value equal to or larger than zero will make projectM use this time value for
52+
* animations instead of the system clock. Any value less than zero will use the system time instead,
53+
* which is the default behavior.
54+
*
55+
* This method can be used to render visualizations at non-realtime frame rates, e.g. encoding a video
56+
* as fast as projectM can render frames.
57+
*
58+
* While switching back and forth between system and user time values is possible, it will cause
59+
* visual artifacts in the rendering as the time value will make large jumps between frames. Thus,
60+
* it is recommended to stay with one type of timing value.
61+
*
62+
* If using this feature, it is further recommended to set the time to 0.0 on the first frame.
63+
*
64+
* @param instance The projectM instance handle.
65+
* @param seconds_since_first_frame Any value >= 0 to use user-specified timestamps, values < 0 will use the system clock.
66+
*/
67+
PROJECTM_EXPORT void projectm_set_frame_time(projectm_handle instance, double seconds_since_first_frame);
68+
69+
/**
70+
* @brief Returns the fractional seconds time value used rendering the last frame.
71+
* @note This will not return the value set with projectm_set_frame_time, but the actual time
72+
* used to render the last frame. If a user-specified frame time was set, this value is
73+
* returned. Otherwise, the frame time measured via the system clock will be returned.
74+
* @param instance The projectM instance handle.
75+
* @return Time elapsed since projectM was started, or the value of the user-specified time value used
76+
* to render the last frame.
77+
*/
78+
PROJECTM_EXPORT double projectm_get_last_frame_time(projectm_handle instance);
79+
4880
/**
4981
* @brief Sets the beat sensitivity.
5082
*

src/libprojectM/ProjectM.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,16 @@ auto ProjectM::PresetLocked() const -> bool
281281
return m_presetLocked;
282282
}
283283

284+
void ProjectM::SetFrameTime(double secondsSinceStart)
285+
{
286+
m_timeKeeper->SetFrameTime(secondsSinceStart);
287+
}
288+
289+
double ProjectM::GetFrameTime()
290+
{
291+
return m_timeKeeper->GetFrameTime();
292+
}
293+
284294
void ProjectM::SetBeatSensitivity(float sensitivity)
285295
{
286296
m_beatSensitivity = std::min(std::max(0.0f, sensitivity), 2.0f);

src/libprojectM/ProjectM.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,22 @@ class PROJECTM_EXPORT ProjectM
103103

104104
void RenderFrame(uint32_t targetFramebufferObject = 0);
105105

106+
/**
107+
* @brief Sets a user-specified time for rendering the next frame
108+
* Negative values will make projectM use the system clock instead.
109+
* @param secondsSinceStart Fractional seconds since rendering the first frame.
110+
*/
111+
void SetFrameTime(double secondsSinceStart);
112+
113+
/**
114+
* @brief Gets the time of the last frame rendered.
115+
* @note This will not return the value set with SetFrameTime, but the actual time used to render the last frame.
116+
* If a user-specified frame time was set, this value is returned. Otherwise, the frame time measured via the
117+
* system clock will be returned.
118+
* @return Seconds elapsed rendering the last frame since starting projectM.
119+
*/
120+
double GetFrameTime();
121+
106122
void SetBeatSensitivity(float sensitivity);
107123

108124
auto GetBeatSensitivity() const -> float;

src/libprojectM/ProjectMCWrapper.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <cstring>
88
#include <sstream>
99
#include <projectM-4/render_opengl.h>
10+
#include <projectM-4/parameters.h>
1011

1112

1213
namespace libprojectM {
@@ -179,6 +180,18 @@ void projectm_opengl_render_frame_fbo(projectm_handle instance, uint32_t framebu
179180
projectMInstance->RenderFrame(framebuffer_object_id);
180181
}
181182

183+
void projectm_set_frame_time(projectm_handle instance, double seconds_since_first_frame)
184+
{
185+
auto projectMInstance = handle_to_instance(instance);
186+
projectMInstance->SetFrameTime(seconds_since_first_frame);
187+
}
188+
189+
double projectm_get_last_frame_time(projectm_handle instance)
190+
{
191+
auto projectMInstance = handle_to_instance(instance);
192+
return projectMInstance->GetFrameTime();
193+
}
194+
182195
void projectm_set_beat_sensitivity(projectm_handle instance, float sensitivity)
183196
{
184197
auto projectMInstance = handle_to_instance(instance);

src/libprojectM/TimeKeeper.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,26 @@ TimeKeeper::TimeKeeper(double presetDuration, double smoothDuration, double hard
1414
UpdateTimers();
1515
}
1616

17+
void TimeKeeper::SetFrameTime(double secondsSinceStart)
18+
{
19+
m_userSpecifiedTime = secondsSinceStart;
20+
}
21+
22+
double TimeKeeper::GetFrameTime() const
23+
{
24+
return m_currentTime;
25+
}
26+
1727
void TimeKeeper::UpdateTimers()
1828
{
19-
auto currentTime = std::chrono::high_resolution_clock::now();
29+
double currentFrameTime{m_userSpecifiedTime};
30+
31+
if (m_userSpecifiedTime < 0.0)
32+
{
33+
auto currentTime = std::chrono::high_resolution_clock::now();
34+
currentFrameTime = std::chrono::duration<double>(currentTime - m_startTime).count();
35+
}
2036

21-
double currentFrameTime = std::chrono::duration<double>(currentTime - m_startTime).count();
2237
m_secondsSinceLastFrame = currentFrameTime - m_currentTime;
2338
m_currentTime = currentFrameTime;
2439
m_presetFrameA++;

src/libprojectM/TimeKeeper.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ class TimeKeeper
1111
public:
1212
TimeKeeper(double presetDuration, double smoothDuration, double hardcutDuration, double easterEgg);
1313

14+
/**
15+
* @brief Sets a custom time value to use instead of the system time.
16+
* If less than zero, the system time will be used instead.
17+
* @param secondsSinceStart Fractional seconds since rendering the first frame.
18+
*/
19+
void SetFrameTime(double secondsSinceStart);
20+
21+
/**
22+
* @brief Gets the time of the last frame rendered.
23+
* @note This will not return the value set with SetFrameTime, but the actual time used to render the last frame.
24+
* If a user-specified frame time was set, this value is returned. Otherwise, the frame time measured via the
25+
* system clock will be returned.
26+
* @return Seconds elapsed rendering the last frame since starting projectM.
27+
*/
28+
double GetFrameTime() const;
29+
30+
/**
31+
* @brief Updates internal timers with either the system clock or a user-specified time value.
32+
*/
1433
void UpdateTimers();
1534

1635
void StartPreset();
@@ -93,6 +112,8 @@ class TimeKeeper
93112
std::random_device m_randomDevice{};
94113
std::mt19937 m_randomGenerator{m_randomDevice()};
95114

115+
double m_userSpecifiedTime{-1.0}; //!< User-specifed run time. If set to a value >= 0.0, this time is used instead of the system clock.
116+
96117
double m_secondsSinceLastFrame{};
97118

98119
double m_easterEgg{};

0 commit comments

Comments
 (0)