gpt4 book ai didi

c++ - 不一致的 chrono::high_resolution_clock 延迟

转载 作者:行者123 更新时间:2023-11-30 01:34:26 26 4
gpt4 key购买 nike

我正在尝试实现类似 MIDI 的时钟采样播放器。

有一个计时器,它增加脉冲计数器,每 480 个脉冲是一个四分之一,所以脉冲周期为 1041667 纳秒,每分钟 120 次。计时器不是基于 sleep 的,而是在单独的线程中运行,但似乎延迟时间不一致:测试文件中播放的样本之间的周期波动 +- 20 毫秒(在某些情况下周期正常且稳定,我找不到不依赖于此效果)。

排除了音频后端的影响:我已经尝试过 OpenAL 和 SDL_mixer。

void Timer_class::sleep_ns(uint64_t ns){
auto start = std::chrono::high_resolution_clock::now();
bool sleep = true;

while(sleep)
{
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start);
if (elapsed.count() >= ns) {
TestTime = elapsed.count();
sleep = false;
//break;
}
}
}

void Timer_class::Runner(void){
// this running as thread
while(1){
sleep_ns(BPMns);
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
}
}
};

void Player_class::PlayFile(int FileNumber){
#ifdef AUDIO_SDL_MIXER
if(Mix_PlayChannel(-1, WaveData[FileNumber], 0)==-1) {
printf("Mix_PlayChannel: %s\n",Mix_GetError());
}
#endif // AUDIO_SDL_MIXER
}

我在方法方面做错了什么吗?有没有更好的方法来实现这种计时器?对于音频,高于 4-5 毫秒的偏差太大了。

最佳答案

我看到一个大错误和一个小错误。最大的错误是您的代码假定 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,您将得到大约 1041667ns。 iteration_time{1} 旨在成为您希望 Timer_class::Runner 中循环的每次迭代所花费的精确时间量。

要纠正较大的错误,您需要直到 time_point sleep ,而不是 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};
}
}

关于c++ - 不一致的 chrono::high_resolution_clock 延迟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56407265/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com