Merge pull request #1486 from popcoder565/fix/audio-precise-pacing

fix(audio): pace blocking audio calls with a high-resolution timer
This commit is contained in:
Adrià 2026-10-10 03:03:00 +02:00 committed by GitHub
commit e0d4516b43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 16 additions and 10 deletions

View File

@ -7,9 +7,9 @@
#include <set>
#include <stdexcept>
#include <string>
#include <thread>
#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;
}

View File

@ -2,13 +2,13 @@
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/PreciseWait.hpp"
#include "SDL.h"
#include <algorithm>
#include <array>
#include <chrono>
#include <cstring>
#include <mutex>
#include <thread>
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<std::uint8_t, 4096> 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();

View File

@ -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<std::mutex>& lock, std::uint64_t us) {
struct timespec req{};
req.tv_sec = static_cast<time_t>(us / 1000000ULL);
req.tv_nsec = static_cast<long>((us % 1000000ULL) * 1000ULL);
lock.unlock();
nanosleep(&req, nullptr);
PreciseSleepUs(us);
lock.lock();
}

View File

@ -12,6 +12,7 @@
#include <vector>
#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();
}

View File

@ -37,4 +37,11 @@ inline void PreciseSleepUs(unsigned long long micros) {
std::this_thread::sleep_for(std::chrono::microseconds(micros));
}
template <class TClock, class TDuration>
inline void PreciseSleepUntil(const std::chrono::time_point<TClock, TDuration>& deadline) {
for (auto now = TClock::now(); now < deadline; now = TClock::now()) {
PreciseSleepUs(static_cast<unsigned long long>(std::chrono::ceil<std::chrono::microseconds>(deadline - now).count()));
}
}
#endif