From b2cde4a0f2af792b2213121ff6c3a184bd972ab1 Mon Sep 17 00:00:00 2001 From: popcoder565 <339285150+popcoder565@users.noreply.github.com> Date: Thu, 8 Oct 2026 04:58:34 -0400 Subject: [PATCH] fix(audio): pace blocking audio calls with a high-resolution timer On Windows, nanosleep and this_thread::sleep_for/sleep_until go through winpthreads, which ends a wait on a 15.6 ms GetTickCount64 step. Blocking sceAudioOutOutput and sceAudioOut2ContextPush calls returned in bursts about every 16 ms, a blocking sceAudio3dPortPush ran at about 68 % of real time, and sceAudioInInput without a device woke anywhere from 1 ms to 31 ms apart. They now sleep with PreciseSleepUs or the new PreciseSleepUntil. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceAudio3d/Export.cpp | 4 ++-- core/libs/prx/libSceAudioIn/Export.cpp | 6 +++--- core/libs/prx/libSceAudioOut/src/AudioOut.cpp | 6 ++---- core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp | 3 ++- core/libs/prx/libc/include/PreciseWait.hpp | 7 +++++++ 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceAudio3d/Export.cpp b/core/libs/prx/libSceAudio3d/Export.cpp index 78049dcf1..b366da306 100644 --- a/core/libs/prx/libSceAudio3d/Export.cpp +++ b/core/libs/prx/libSceAudio3d/Export.cpp @@ -7,9 +7,9 @@ #include #include #include -#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libc/include/PreciseWait.hpp" namespace { @@ -168,7 +168,7 @@ int APS5_VABI sceAudio3dPortPush(uint32_t port_id, uint32_t blocking) { if (blocking == AUDIO3D_BLOCKING_ASYNC || level < g_port.queue_depth) return 0; wait_until = g_port.playing[level - g_port.queue_depth]; } - while (Clock::now() < wait_until) std::this_thread::sleep_until(wait_until); + PreciseSleepUntil(wait_until); return 0; } diff --git a/core/libs/prx/libSceAudioIn/Export.cpp b/core/libs/prx/libSceAudioIn/Export.cpp index 1899b5ab7..4ba03302f 100644 --- a/core/libs/prx/libSceAudioIn/Export.cpp +++ b/core/libs/prx/libSceAudioIn/Export.cpp @@ -2,13 +2,13 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libc/include/PreciseWait.hpp" #include "SDL.h" #include #include #include #include #include -#include namespace { @@ -76,7 +76,7 @@ Port* find(int handle) { } std::size_t capture(SDL_AudioDeviceID device, std::uint8_t* dest, std::size_t bytes, Clock::time_point deadline) { - while (SDL_GetQueuedAudioSize(device) < bytes && Clock::now() < deadline) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + while (SDL_GetQueuedAudioSize(device) < bytes && Clock::now() < deadline) PreciseSleepUs(1000); const std::size_t queued = SDL_GetQueuedAudioSize(device); std::array discard; for (std::size_t excess = queued > bytes * MAX_QUEUED_BLOCKS ? queued - bytes * MAX_QUEUED_BLOCKS : 0; excess > 0;) { @@ -122,7 +122,7 @@ int APS5_VABI sceAudioInInput(int handle, void* dest) { if (device != 0) { captured = capture(device, out, bytes, deadline); } else { - while (Clock::now() < wake) std::this_thread::sleep_until(wake); + PreciseSleepUntil(wake); } std::memset(out + captured, 0, bytes - captured); lock.lock(); diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp index cf035c56f..5dcdd2821 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut.cpp @@ -13,6 +13,7 @@ #include "SDL.h" #include "SceTypes.hpp" +#include "prx/libc/include/PreciseWait.hpp" #include "prx/libkernel/Time/include/Time.hpp" static constexpr int PORT_TYPE_MAIN = 0; @@ -192,11 +193,8 @@ static const void* prepareBuffer(const Port& port, const void* data, std::vector } static void sleepUs(std::unique_lock& lock, std::uint64_t us) { - struct timespec req{}; - req.tv_sec = static_cast(us / 1000000ULL); - req.tv_nsec = static_cast((us % 1000000ULL) * 1000ULL); lock.unlock(); - nanosleep(&req, nullptr); + PreciseSleepUs(us); lock.lock(); } diff --git a/core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp b/core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp index 9dc8f44d6..383ad018f 100644 --- a/core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp +++ b/core/libs/prx/libSceAudioOut/src/AudioOut2Context.cpp @@ -12,6 +12,7 @@ #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libc/include/PreciseWait.hpp" #include "AudioOut2Internal.hpp" // An AudioOut2 context is the hardware output queue: every push appends a grain (num_grains samples) @@ -324,7 +325,7 @@ int APS5_VABI sceAudioOut2ContextPush(AudioOut2ContextHandle ctx, uint32_t block } if (now - waitStart > FULL_WAIT_TIMEOUT) break; lock.unlock(); - std::this_thread::sleep_for(FULL_WAIT_STEP); + PreciseSleepUs(std::chrono::microseconds(FULL_WAIT_STEP).count()); lock.lock(); now = Clock::now(); } diff --git a/core/libs/prx/libc/include/PreciseWait.hpp b/core/libs/prx/libc/include/PreciseWait.hpp index 06196d05f..e9fc32421 100644 --- a/core/libs/prx/libc/include/PreciseWait.hpp +++ b/core/libs/prx/libc/include/PreciseWait.hpp @@ -37,4 +37,11 @@ inline void PreciseSleepUs(unsigned long long micros) { std::this_thread::sleep_for(std::chrono::microseconds(micros)); } +template +inline void PreciseSleepUntil(const std::chrono::time_point& deadline) { + for (auto now = TClock::now(); now < deadline; now = TClock::now()) { + PreciseSleepUs(static_cast(std::chrono::ceil(deadline - now).count())); + } +} + #endif