diff options
Diffstat (limited to 'src/server/FrameTimer.hpp')
-rw-r--r-- | src/server/FrameTimer.hpp | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/server/FrameTimer.hpp b/src/server/FrameTimer.hpp new file mode 100644 index 00000000..57acbaa5 --- /dev/null +++ b/src/server/FrameTimer.hpp @@ -0,0 +1,110 @@ +/* + This file is part of Ingen. + Copyright 2017 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INGEN_ENGINE_FRAMETIMER_HPP +#define INGEN_ENGINE_FRAMETIMER_HPP + +#include <chrono> +#include <cmath> +#include <cstdint> + +namespace ingen { +namespace server { + +/** Delay-locked loop for monotonic sample time. + * + * See "Using a DLL to filter time" by Fons Adriaensen + * http://kokkinizita.linuxaudio.org/papers/usingdll.pdf + */ +class FrameTimer +{ +public: + static constexpr double PI = 3.14159265358979323846; + static constexpr double bandwidth = 1.0 / 8.0; // Hz + static constexpr double us_per_s = 1000000.0; + + FrameTimer(uint32_t period_size, uint32_t sample_rate) + : tper(((double)period_size / (double)sample_rate) * us_per_s) + , omega(2 * PI * bandwidth / us_per_s * tper) + , b(sqrt(2) * omega) + , c(omega * omega) + , nper(period_size) + { + } + + /** Update the timer for current real time `usec` and frame `frame`. */ + void update(uint64_t usec, uint64_t frame) { + if (!initialized || frame != n1) { + init(usec, frame); + return; + } + + // Calculate loop error + const double e = ((double)usec - t1); + + // Update loop + t0 = t1; + t1 += b * e + e2; + e2 += c * e; + + // Update frame counts + n0 = n1; + n1 += nper; + } + + /** Return an estimate of the frame time for current real time `usec`. */ + uint64_t frame_time(uint64_t usec) const { + if (!initialized) { + return 0; + } + + const double delta = (double)usec - t0; + const double period = t1 - t0; + return n0 + std::round(delta / period * nper); + } + +private: + void init(uint64_t now, uint64_t frame) { + // Init loop + e2 = tper; + t0 = now; + t1 = t0 + e2; + + // Init sample counts + n0 = frame; + n1 = n0 + nper; + + initialized = true; + } + + const double tper; + const double omega; + const double b; + const double c; + + uint64_t nper; + double e2; + double t0; + double t1; + uint64_t n0; + uint64_t n1; + bool initialized; +}; + +} // namespace server +} // namespace ingen + +#endif // INGEN_ENGINE_FRAMETIMER_HPP |