Queue audio samples in a separate thread.

This commit is contained in:
Skyth 2024-12-19 21:54:37 +03:00
parent 41d25ba891
commit 95487a5293
4 changed files with 58 additions and 54 deletions

View file

@ -212,7 +212,6 @@ set(SWA_THIRDPARTY_INCLUDES
"${SWA_THIRDPARTY_ROOT}/imgui"
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
"${SWA_THIRDPARTY_ROOT}/magic_enum/include"
"${SWA_THIRDPARTY_ROOT}/miniaudio"
"${SWA_THIRDPARTY_ROOT}/stb"
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c"
"${SWA_THIRDPARTY_ROOT}/TinySHA1"

View file

@ -7,20 +7,8 @@
static PPCFunc* g_clientCallback{};
static uint32_t g_clientCallbackParam{}; // pointer in guest memory
static SDL_AudioDeviceID g_audioDevice{};
static std::unique_ptr<GuestThreadContext> g_audioCtx;
static uint32_t* g_audioOutput;
static bool g_downMixToStereo;
static void AudioCallback(void*, uint8_t* frames, int len)
{
if (g_audioCtx == nullptr)
g_audioCtx = std::make_unique<GuestThreadContext>(0);
g_audioCtx->ppcContext.r3.u64 = g_clientCallbackParam;
g_audioOutput = reinterpret_cast<uint32_t*>(frames);
(*g_clientCallback)(g_audioCtx->ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
}
void XAudioInitializeSystem()
{
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
@ -32,7 +20,6 @@ void XAudioInitializeSystem()
desired.format = AUDIO_F32SYS;
desired.channels = XAUDIO_NUM_CHANNELS;
desired.samples = XAUDIO_NUM_SAMPLES;
desired.callback = AudioCallback;
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS)
@ -44,6 +31,44 @@ void XAudioInitializeSystem()
g_downMixToStereo = (obtained.channels == 2);
}
static std::unique_ptr<std::thread> g_audioThread;
static void AudioThread()
{
using namespace std::chrono_literals;
GuestThreadContext ctx(0);
size_t channels = g_downMixToStereo ? 2 : XAUDIO_NUM_CHANNELS;
constexpr double INTERVAL = double(XAUDIO_NUM_SAMPLES) / double(XAUDIO_SAMPLES_HZ);
auto start = std::chrono::steady_clock::now();
size_t iteration = 1;
while (true)
{
uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice);
constexpr size_t MAX_LATENCY = 10;
const size_t callbackAudioSize = channels * XAUDIO_NUM_SAMPLES * sizeof(float);
if ((queuedAudioSize / callbackAudioSize) <= MAX_LATENCY)
{
ctx.ppcContext.r3.u32 = g_clientCallbackParam;
g_clientCallback(ctx.ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
}
auto next = start + std::chrono::duration<double>(iteration * INTERVAL);
auto now = std::chrono::steady_clock::now();
if ((next - now) > 1s)
next = now;
std::this_thread::sleep_until(next);
iteration = std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count() / INTERVAL + 1;
}
}
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
{
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(param)));
@ -53,6 +78,7 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
g_clientCallback = callback;
SDL_PauseAudioDevice(g_audioDevice, 0);
g_audioThread = std::make_unique<std::thread>(AudioThread);
}
void XAudioSubmitFrame(void* samples)
@ -68,6 +94,8 @@ void XAudioSubmitFrame(void* samples)
auto floatSamples = reinterpret_cast<be<float>*>(samples);
std::array<float, 2 * XAUDIO_NUM_SAMPLES> audioFrames;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{
float ch0 = floatSamples[0 * XAUDIO_NUM_SAMPLES + i];
@ -77,21 +105,24 @@ void XAudioSubmitFrame(void* samples)
float ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i];
float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
float left = ch0 + ch2 * 0.75f + ch4;
float right = ch1 + ch2 * 0.75f + ch5;
g_audioOutput[i * 2 + 0] = *reinterpret_cast<uint32_t*>(&left);
g_audioOutput[i * 2 + 1] = *reinterpret_cast<uint32_t*>(&right);
audioFrames[i * 2 + 0] = ch0 + ch2 * 0.75f + ch4;
audioFrames[i * 2 + 1] = ch1 + ch2 * 0.75f + ch5;
}
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
}
else
{
auto rawSamples = reinterpret_cast<be<uint32_t>*>(samples);
std::array<uint32_t, XAUDIO_NUM_CHANNELS * XAUDIO_NUM_SAMPLES> audioFrames;
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
{
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
g_audioOutput[i * XAUDIO_NUM_CHANNELS + j] = rawSamples[j * XAUDIO_NUM_SAMPLES + i];
audioFrames[i * XAUDIO_NUM_CHANNELS + j] = rawSamples[j * XAUDIO_NUM_SAMPLES + i];
}
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
}
}

View file

@ -32,6 +32,7 @@ GuestThreadContext::GuestThreadContext(uint32_t cpuNumber)
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
ppcContext.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
ppcContext.r13.u64 = g_memory.MapVirtual(thread);
ppcContext.fpscr.loadFromHost();
assert(GetPPCContext() == nullptr);
SetPPCContext(ppcContext);
@ -77,7 +78,7 @@ uint32_t GuestThread::Start(const GuestThreadParams& params)
GuestThreadContext ctx(cpuNumber);
ctx.ppcContext.r3.u64 = params.value;
GuestCode::Run(g_codeCache.Find(params.function), &ctx.ppcContext, g_memory.Translate(0));
reinterpret_cast<PPCFunc*>(g_codeCache.Find(params.function))(ctx.ppcContext, reinterpret_cast<uint8_t*>(g_memory.base));
return ctx.ppcContext.r3.u32;
}

View file

@ -76,16 +76,16 @@ static std::atomic<uint32_t> g_keSetEventGeneration;
struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
{
std::atomic<uint32_t> count;
std::counting_semaphore<> semaphore;
uint32_t maximumCount;
Semaphore(XKSEMAPHORE* semaphore)
: count(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
: semaphore(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
{
}
Semaphore(uint32_t count, uint32_t maximumCount)
: count(count), maximumCount(maximumCount)
: semaphore(count), maximumCount(maximumCount)
{
}
@ -93,32 +93,11 @@ struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
{
if (timeout == 0)
{
uint32_t currentCount = count.load();
if (currentCount != 0)
{
if (count.compare_exchange_weak(currentCount, currentCount - 1))
return STATUS_SUCCESS;
}
return STATUS_TIMEOUT;
return semaphore.try_acquire() ? STATUS_SUCCESS : STATUS_TIMEOUT;
}
else if (timeout == INFINITE)
{
uint32_t currentCount;
while (true)
{
currentCount = count.load();
if (currentCount != 0)
{
if (count.compare_exchange_weak(currentCount, currentCount - 1))
return STATUS_SUCCESS;
}
else
{
count.wait(0);
}
}
semaphore.acquire();
return STATUS_SUCCESS;
}
else
@ -130,13 +109,7 @@ struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
void Release(uint32_t releaseCount, uint32_t* previousCount)
{
if (previousCount != nullptr)
*previousCount = count;
assert(count + releaseCount <= maximumCount);
count += releaseCount;
count.notify_all();
semaphore.release(releaseCount);
}
};