Skip to content

Commit

Permalink
Add API functions to set and retrieve frame times
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kblaschke committed Jun 9, 2024
1 parent bd2b1ba commit 2914d85
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 2 deletions.
32 changes: 32 additions & 0 deletions src/api/include/projectM-4/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,38 @@ PROJECTM_EXPORT void projectm_set_texture_search_paths(projectm_handle instance,
const char** texture_search_paths,
size_t count);

/**
* @brief Sets a user-specified frame time in fractional seconds.
*
* Setting this to any value equal to or larger than zero will make projectM use this time value for
* animations instead of the system clock. Any value less than zero will use the system time instead,
* which is the default behavior.
*
* This method can be used to render visualizations at non-realtime frame rates, e.g. encoding a video
* as fast as projectM can render frames.
*
* While switching back and forth between system and user time values is possible, it will cause
* visual artifacts in the rendering as the time value will make large jumps between frames. Thus,
* it is recommended to stay with one type of timing value.
*
* If using this feature, it is further recommended to set the time to 0.0 on the first frame.
*
* @param instance The projectM instance handle.
* @param seconds_since_first_frame Any value >= 0 to use user-specified timestamps, values < 0 will use the system clock.
*/
PROJECTM_EXPORT void projectm_set_frame_time(projectm_handle instance, double seconds_since_first_frame);

/**
* @brief Returns the fractional seconds time value used rendering the last frame.
* @note This will not return the value set with projectm_set_frame_time, but the actual time
* used to render the last frame. If a user-specified frame time was set, this value is
* returned. Otherwise, the frame time measured via the system clock will be returned.
* @param instance The projectM instance handle.
* @return Time elapsed since projectM was started, or the value of the user-specified time value used
* to render the last frame.
*/
PROJECTM_EXPORT double projectm_get_last_frame_time(projectm_handle instance);

/**
* @brief Sets the beat sensitivity.
*
Expand Down
10 changes: 10 additions & 0 deletions src/libprojectM/ProjectM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ auto ProjectM::PresetLocked() const -> bool
return m_presetLocked;
}

void ProjectM::SetFrameTime(double secondsSinceStart)
{
m_timeKeeper->SetFrameTime(secondsSinceStart);
}

double ProjectM::GetFrameTime()
{
return m_timeKeeper->GetFrameTime();
}

void ProjectM::SetBeatSensitivity(float sensitivity)
{
m_beatSensitivity = std::min(std::max(0.0f, sensitivity), 2.0f);
Expand Down
16 changes: 16 additions & 0 deletions src/libprojectM/ProjectM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@ class PROJECTM_EXPORT ProjectM

void RenderFrame(uint32_t targetFramebufferObject = 0);

/**
* @brief Sets a user-specified time for rendering the next frame
* Negative values will make projectM use the system clock instead.
* @param secondsSinceStart Fractional seconds since rendering the first frame.
*/
void SetFrameTime(double secondsSinceStart);

/**
* @brief Gets the time of the last frame rendered.
* @note This will not return the value set with SetFrameTime, but the actual time used to render the last frame.
* If a user-specified frame time was set, this value is returned. Otherwise, the frame time measured via the
* system clock will be returned.
* @return Seconds elapsed rendering the last frame since starting projectM.
*/
double GetFrameTime();

void SetBeatSensitivity(float sensitivity);

auto GetBeatSensitivity() const -> float;
Expand Down
13 changes: 13 additions & 0 deletions src/libprojectM/ProjectMCWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstring>
#include <sstream>
#include <projectM-4/render_opengl.h>
#include <projectM-4/parameters.h>


namespace libprojectM {
Expand Down Expand Up @@ -179,6 +180,18 @@ void projectm_opengl_render_frame_fbo(projectm_handle instance, uint32_t framebu
projectMInstance->RenderFrame(framebuffer_object_id);
}

void projectm_set_frame_time(projectm_handle instance, double seconds_since_first_frame)
{
auto projectMInstance = handle_to_instance(instance);
projectMInstance->SetFrameTime(seconds_since_first_frame);
}

double projectm_get_last_frame_time(projectm_handle instance)
{
auto projectMInstance = handle_to_instance(instance);
return projectMInstance->GetFrameTime();
}

void projectm_set_beat_sensitivity(projectm_handle instance, float sensitivity)
{
auto projectMInstance = handle_to_instance(instance);
Expand Down
19 changes: 17 additions & 2 deletions src/libprojectM/TimeKeeper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,26 @@ TimeKeeper::TimeKeeper(double presetDuration, double smoothDuration, double hard
UpdateTimers();
}

void TimeKeeper::SetFrameTime(double secondsSinceStart)
{
m_userSpecifiedTime = secondsSinceStart;
}

double TimeKeeper::GetFrameTime() const
{
return m_currentTime;
}

void TimeKeeper::UpdateTimers()
{
auto currentTime = std::chrono::high_resolution_clock::now();
double currentFrameTime{m_userSpecifiedTime};

if (m_userSpecifiedTime < 0.0)
{
auto currentTime = std::chrono::high_resolution_clock::now();
currentFrameTime = std::chrono::duration<double>(currentTime - m_startTime).count();
}

double currentFrameTime = std::chrono::duration<double>(currentTime - m_startTime).count();
m_secondsSinceLastFrame = currentFrameTime - m_currentTime;
m_currentTime = currentFrameTime;
m_presetFrameA++;
Expand Down
21 changes: 21 additions & 0 deletions src/libprojectM/TimeKeeper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ class TimeKeeper
public:
TimeKeeper(double presetDuration, double smoothDuration, double hardcutDuration, double easterEgg);

/**
* @brief Sets a custom time value to use instead of the system time.
* If less than zero, the system time will be used instead.
* @param secondsSinceStart Fractional seconds since rendering the first frame.
*/
void SetFrameTime(double secondsSinceStart);

/**
* @brief Gets the time of the last frame rendered.
* @note This will not return the value set with SetFrameTime, but the actual time used to render the last frame.
* If a user-specified frame time was set, this value is returned. Otherwise, the frame time measured via the
* system clock will be returned.
* @return Seconds elapsed rendering the last frame since starting projectM.
*/
double GetFrameTime() const;

/**
* @brief Updates internal timers with either the system clock or a user-specified time value.
*/
void UpdateTimers();

void StartPreset();
Expand Down Expand Up @@ -93,6 +112,8 @@ class TimeKeeper
std::random_device m_randomDevice{};
std::mt19937 m_randomGenerator{m_randomDevice()};

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.

double m_secondsSinceLastFrame{};

double m_easterEgg{};
Expand Down

0 comments on commit 2914d85

Please sign in to comment.