/*
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
namespace ingen::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((static_cast(period_size) /
static_cast(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 = (static_cast(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 = static_cast(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 = 0U;
double e2 = 0.0;
double t0 = 0.0;
double t1 = 0.0;
uint64_t n0 = 0U;
uint64_t n1 = 0U;
bool initialized = false;
};
} // namespace ingen::server
#endif // INGEN_ENGINE_FRAMETIMER_HPP