Я вижу большую ошибку и маленькую ошибку.Большая ошибка в том, что ваш код предполагает, что основная обработка в Runner
постоянно занимает нулевое время:
if (Run) Transport.IncPlaybackMarker(); // marker increment
if (Transport.GetPlaybackMarker() == Transport.GetPlaybackEnd()){ // check if timer have reached end, which is 480 pulses
Transport.SetPlaybackMarker(Transport.GetPlaybackStart());
Player.PlayFile(1); // period of this event fluctuates severely
}
То есть вы «спите» в течение времени, которое требуется для итерации цикла,и затем вы выполняете обработку поверх этого.
Небольшая ошибка предполагает, что вы можете представить идеальное время итерации цикла с помощью целого числа наносекунд.Эта ошибка настолько мала, что это не имеет значения.Однако я развлекаюсь, показывая людям, как они могут избавиться и от этой ошибки.: -)
Сначала давайте исправим небольшую ошибку на точно , представляющую идеализированное время итерации цикла:
using quarterPeriod = std::ratio<1, 2>;
using iterationPeriod = std::ratio_divide<quarterPeriod, std::ratio<480>>;
using iteration_time = std::chrono::duration<std::int64_t, iterationPeriod>;
Я ничего не знаю о музыке, но я предполагаюприведенный выше код верен, потому что если вы конвертируете iteration_time{1}
в nanoseconds
, вы получите примерно 1041667 нс.iteration_time{1}
предназначен для того, чтобы быть точным количеством времени, которое вы хотите, чтобы каждая итерация вашего цикла в Timer_class::Runner
занимала.
Чтобы исправить большую ошибку, вам нужно спать до atime_point
, в отличие от сна для a duration
.Вот общая утилита, которая поможет вам сделать это:
template <class Clock, class Duration>
void
delay_until(std::chrono::time_point<Clock, Duration> tp)
{
while (Clock::now() < tp)
;
}
Теперь, если вы наберете Timer_class::Runner
, чтобы использовать delay_until
вместо sleep_ns
, я думаю, вы поправитесьрезультаты:
void
Timer_class::Runner()
{
auto next_start = std::chrono::steady_clock::now() + iteration_time{1};
while (true)
{
if (Run) Transport.IncPlaybackMarker(); // marker increment
if (Transport.GetPlaybackMarker() == Transport.GetPlaybackEnd()){ // check if timer have reached end, which is 480 pulses
Transport.SetPlaybackMarker(Transport.GetPlaybackStart());
Player.PlayFile(1);
}
delay_until(next_start);
next_start += iteration_time{1};
}
}