/* This file is part of Ingen. Copyright 2017 David Robillard 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 . */ #ifndef INGEN_ENGINE_FRAMETIMER_HPP #define INGEN_ENGINE_FRAMETIMER_HPP #include #include #include 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