Pace AX with a clock that moves, on Windows too

NNshi's Windows core produced about a fifth of the audio it should, which
is silence on WASAPI and a 90% underrun on XAudio and ASIO. The ratio is
the tell: AXOut_update gates on 3 ms and queues one 3 ms frame per pass,
and 15.6 ms divided by 3 ms is 5.2.

15.6 ms is the Windows system timer period, and that is the resolution of
std::chrono::high_resolution_clock on a MinGW build - libstdc++ makes it
an alias of system_clock, which reads the system time rather than QPC.
now_cached() hands MinGW exactly that, because its fast path is written in
MSVC STL internals and everything else falls through to the standard
clock. Upstream never meets this: its Windows build is MSVC, where
now_cached is QPC by hand, and on Linux and macOS the standard clock is
fine.

So the pacing uses tick_cached() instead, which is QPC on every compiler
and monotonic besides - which is what a delta wanted in the first place.

The same fallback is in VPADController for its input tick. Left alone:
nothing has been reported there and a coarse clock costs less in input
sampling than it does in an audio gate, so that is a change to make with
evidence rather than by analogy.
This commit is contained in:
WizzardSK committed 2026-09-19 17:52:46 +02:00
1 parent 71418964e4
commit 29b06ff0fa
1 file changed
+12 -3
+12 -3
View File
@@ -557,10 +557,19 @@ namespace snd_core
// s_last_check is always set to the timestamp at the time of firing
// it's used to enforce the minimum wait delay (we want to avoid calling AX update in quick succession because other threads may need to do work first)
static auto s_ax_interval_timer = now_cached() - kWaitDuration;
static auto s_last_check = now_cached();
// tick_cached, not now_cached: these are deltas, so what they want is a
// monotonic clock - and on a MinGW build now_cached falls back to
// std::chrono::high_resolution_clock, which is libstdc++'s system_clock
// and on Windows ticks with the system timer, not with QPC. A clock that
// only moves every ~15.6 ms never opens this 3 ms gate on time: AX then
// queues one 3 ms frame per tick instead of five, which is audio at a
// fifth of real time - silence on WASAPI, underruns everywhere else.
// Upstream never meets this because its Windows build is MSVC, where
// now_cached is QPC by hand. tick_cached is QPC on every compiler.
static auto s_ax_interval_timer = tick_cached() - kWaitDuration;
static auto s_last_check = tick_cached();
const auto now = now_cached();
const auto now = tick_cached();
const auto diff = (now - s_ax_interval_timer);
if (diff < wait_duration)