From a7d7ec0d936191d3d351473c3604fc22c8d60b80 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 16 Nov 2014 23:20:48 -0500 Subject: [PATCH] Frame rate limiter --- CMakeLists.txt | 2 + src/Timer.cpp | 181 ++++++++++++++++++++++++++++++ src/Timer.h | 196 +++++++++++++++++++++++++++++++++ src/visual/ScopeCanvas.cpp | 10 +- src/visual/ScopeCanvas.h | 3 + src/visual/SpectrumCanvas.cpp | 10 +- src/visual/SpectrumCanvas.h | 3 + src/visual/WaterfallCanvas.cpp | 10 +- src/visual/WaterfallCanvas.h | 3 + 9 files changed, 412 insertions(+), 6 deletions(-) create mode 100644 src/Timer.cpp create mode 100644 src/Timer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 06c8840..b65373c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ SET (cubicsdr_sources src/AudioThreadQueue.cpp src/AudioThreadTask.cpp src/Gradient.cpp + src/Timer.cpp src/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp src/visual/SpectrumCanvas.cpp @@ -121,6 +122,7 @@ SET (cubicsdr_headers src/AudioThreadQueue.h src/AudioThreadTask.h src/Gradient.h + src/Timer.h src/visual/ScopeCanvas.h src/visual/ScopeContext.h src/visual/SpectrumCanvas.h diff --git a/src/Timer.cpp b/src/Timer.cpp new file mode 100644 index 0000000..a555aeb --- /dev/null +++ b/src/Timer.cpp @@ -0,0 +1,181 @@ +/* + This file is part of CubicVR. + + CubicVR is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + CubicVR is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with CubicVR; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + Source (c) 2003 by Charles J. Cliffe unless otherwise specified + To contact me, e-mail or MSN to cj@cubicproductions.com or by ICQ#7188044 + http://www.cubicproductions.com +*/ + +#include "Timer.h" + +#ifdef _WIN32 + #include +#endif + +Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), paused_time(0), offset(0), paused_state(false) +{ +} + + +void Timer::start(void) +{ + update(); + num_updates = 0; + start_time = system_milliseconds; + last_update = start_time; + paused_state = false; + lock_state = false; + lock_rate = 0; + paused_time = 0; + offset = 0; +} + + +void Timer::stop(void) +{ + end_time = system_milliseconds; +} + + +void Timer::reset(void) +{ + start(); +} + + +void Timer::lockFramerate(float f_rate) +{ + lock_rate = 1.0/f_rate; + lock_state = true; +} + + +void Timer::unlock() +{ + unsigned long msec_tmp = system_milliseconds; + + lock_state = false; + + update(); + + last_update = system_milliseconds-lock_rate; + + offset += msec_tmp-system_milliseconds; + + lock_rate = 0; +} + +bool Timer::locked() +{ + return lock_state; +} + +void Timer::update(void) +{ + num_updates++; + last_update = system_milliseconds; + + + if (lock_state) + { + system_milliseconds += (unsigned long)(lock_rate*1000.0); + } + else + { +#ifdef _WIN32 + system_milliseconds = timeGetTime (); +#else + gettimeofday(&time_val,&time_zone); + + system_milliseconds = (unsigned long)time_val.tv_usec; + system_milliseconds /= 1000; + system_milliseconds += (unsigned long)(time_val.tv_sec*1000); +#endif + } + + + if (paused_state) paused_time += system_milliseconds-last_update; + + time_elapsed = system_milliseconds-start_time-paused_time+offset; +} + + +unsigned long Timer::getMilliseconds(void) +{ + return time_elapsed; +} + + + +double Timer::getSeconds(void) +{ + return ((double)getMilliseconds())/1000.0; +} + + +void Timer::setMilliseconds(unsigned long milliseconds_in) +{ + offset -= (system_milliseconds-start_time-paused_time+offset)-milliseconds_in; +} + + + +void Timer::setSeconds(double seconds_in) +{ + setMilliseconds((long)(seconds_in*1000.0)); +} + + +double Timer::lastUpdateSeconds(void) +{ + return ((double)lastUpdateMilliseconds())/1000.0; +} + + +unsigned long Timer::lastUpdateMilliseconds(void) +{ + return system_milliseconds-last_update; +} + +unsigned long Timer::totalMilliseconds() +{ + return system_milliseconds-start_time; +} + + +double Timer::totalSeconds(void) +{ + return totalMilliseconds()/1000.0; +} + + +unsigned long Timer::getNumUpdates(void) +{ + return num_updates; +} + + +void Timer::paused(bool pause_in) +{ + paused_state = pause_in; +} + +bool Timer::paused() +{ + return paused_state; +} diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..64500b0 --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,196 @@ +/* + This file is part of CubicVR. + + CubicVR is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + CubicVR is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with CubicVR; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + Source (c) 2003 by Charles J. Cliffe unless otherwise specified + To contact me, e-mail or MSN to cj@cubicproductions.com or by ICQ#7188044 + http://www.cubicproductions.com +*/ + +#ifndef TIMER_H +#define TIMER_H + +#ifndef _WIN32 +#include +#endif + +/// Timer Class, high resolution timer +/** + * Class provides high resolution timing and useful time control functions + */ + +class Timer +{ +private: + + unsigned long time_elapsed; + unsigned long system_milliseconds; + unsigned long start_time; + unsigned long end_time; + unsigned long last_update; + unsigned long num_updates; + unsigned long paused_time; + unsigned long offset; + +#ifndef _WIN32 + struct timeval time_val; + struct timezone time_zone; +#endif + + + bool paused_state; + bool lock_state; + float lock_rate; + +public: + + /// Constructor + Timer(); + + /// Start the timer + /** + * Resets the timer to 0 and begins timing + */ + void start(void); + + /// Stop the timer + /** + * Stops the timer and records the end time + */ + void stop(void); + + + /// Locks the timer to a specified framerate (for recording / benchmarking purposes typically) + /** + * Locks the timer to a specified framerate (for recording / benchmarking purposes typically) + */ + void lockFramerate(float f_rate); + + + /// Unlock any framerate lock that's been applied + /** + * Unlock any framerate lock that's been applied + */ + void unlock(); + + + /// Check locked state + /** + * Check locked state + */ + bool locked(); + + + /// Reset the timer counter + /** + * Resetting the timer will reset the current time to 0 + */ + void reset(void); + + + /// Timer update + /** + * Calling the update command will bring the timer value up to date, this is meant + * to be called at the begining of the frame to establish the time index which is being drawn. + */ + void update(void); + + + /// Get the total time elapsed since the timer start, not counting paused time + /** + * Returns the total time elapsed in since the timer start() to the last update() but + * does not count the time elapsed while the timer is paused(). + * \return Total time elapsed since the timer start() to the last update() excluding time paused() in milliseconds + */ + unsigned long getMilliseconds(void); + /// Alias of getMilliseconds() which returns time in seconds + /** + * \return Total time elapsed since the timer start() to the last update() excluding time paused() in seconds + */ + double getSeconds(void); + + + /// Get the total time elapsed since the timer start + /** + * Returns the total time elapsed in since the timer start() to the last update() + * this includes any time accumulated during updates while paused() + * \return Total time elapsed since the timer start() to the last update() including time paused() in milliseconds + */ + unsigned long totalMilliseconds(void); + /// Alias of totalMilliseconds() which returns time in seconds + /** + * \return Total time elapsed since the timer start() to the last update() including time paused() in seconds + */ + double totalSeconds(void); + + + /// Set the amount of time elapsed + /** + * Force the timer duration to a specific value, useful for rolling forward or back in a system + * based upon the timer. + * \param milliseconds_in Time to set timer to in milliseconds + */ + void setMilliseconds(unsigned long milliseconds_in); + /// alias of setMilliseconds() which accepts time in seconds + /** + * \param seconds_in Time to set timer to in seconds + */ + void setSeconds(double seconds_in); + + + /// Get the amount of times the update() command has been called + /** + * By using the number of times the update() command has been called you can easily determine + * an average frame rate. Also useful for merely determining how many frames have been drawn. + * \return Number of times update() has been called + */ + unsigned long getNumUpdates(void); + + + /// Get the timer duration during the last update + /** + * Useful for determining the amount of time which elapsed during the last update + * can be used to accurately control values with a per-second rate or determine the current frame rate. + * \return Duration of time between the last two calls to update() in milliseconds + */ + unsigned long lastUpdateMilliseconds(void); + /// Alias of lastUpdateMilliseconds() which returns time in seconds + /** + * \return Duration of time between the last two calls to update() in seconds + */ + double lastUpdateSeconds(void); + + + /// Set the timer pause state + /** + * Pause the timer, allowing for continued update() calls without an increment in timing but + * maintaining the update and total time count, useful for pausing a scene but allowing frame + * timing to resume. + * \param pause_in Value to set the current pause state to + */ + void paused(bool pause_in); + + /// Check if the timer is currently in a paused state + /** + * \return Current pause state, true if paused, false otherwise + */ + bool paused(); +}; + + +#endif + diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index f6ab56e..2c8618c 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -22,9 +22,10 @@ wxEND_EVENT_TABLE() ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); + timer.start(); } ScopeCanvas::~ScopeCanvas() { @@ -75,5 +76,10 @@ void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { } void ScopeCanvas::OnIdle(wxIdleEvent &event) { - Refresh(false); + timer.update(); + frameTimer += timer.lastUpdateSeconds(); + if (frameTimer > 1.0/30.0) { + Refresh(false); + frameTimer = 0; + } } diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index ab8b544..0e3145d 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -9,6 +9,7 @@ #include "ScopeContext.h" #include "fftw3.h" +#include "Timer.h" class ScopeCanvas: public wxGLCanvas { public: @@ -27,6 +28,8 @@ private: wxWindow *parent; ScopeContext *glContext; + Timer timer; + float frameTimer; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 17ca1fb..060950e 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -22,7 +22,7 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { int in_block_size = BUF_SIZE / 2; int out_block_size = FFT_SIZE; @@ -35,6 +35,7 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : fft_floor_ma = fft_floor_maa = 0.0; glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); + timer.start(); } SpectrumCanvas::~SpectrumCanvas() { @@ -152,5 +153,10 @@ void SpectrumCanvas::setData(std::vector *data) { } void SpectrumCanvas::OnIdle(wxIdleEvent &event) { - Refresh(false); + timer.update(); + frameTimer += timer.lastUpdateSeconds(); + if (frameTimer > 1.0/30.0) { + Refresh(false); + frameTimer = 0; + } } diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 29c748b..5b3cdb8 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -9,6 +9,7 @@ #include "SpectrumContext.h" #include "fftw3.h" +#include "Timer.h" class SpectrumCanvas: public wxGLCanvas { public: @@ -36,6 +37,8 @@ private: std::vector fft_result_maa; SpectrumContext *glContext; + Timer timer; + float frameTimer; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 6cb3bd4..8e04ced 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -22,7 +22,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { int in_block_size = BUF_SIZE / 2; int out_block_size = FFT_SIZE; @@ -35,6 +35,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : fft_floor_ma = fft_floor_maa = 0.0; glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); + timer.start(); } WaterfallCanvas::~WaterfallCanvas() { @@ -150,5 +151,10 @@ void WaterfallCanvas::setData(std::vector *data) { } void WaterfallCanvas::OnIdle(wxIdleEvent &event) { - Refresh(false); + timer.update(); + frameTimer += timer.lastUpdateSeconds(); + if (frameTimer > 1.0/30.0) { + Refresh(false); + frameTimer = 0; + } } diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 3d36593..207c288 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -9,6 +9,7 @@ #include "WaterfallContext.h" #include "fftw3.h" +#include "Timer.h" class WaterfallCanvas: public wxGLCanvas { public: @@ -36,6 +37,8 @@ private: std::vector fft_result_maa; WaterfallContext *glContext; + Timer timer; + float frameTimer; // event table wxDECLARE_EVENT_TABLE(); };