mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2026-04-28 05:11:37 +00:00
Queue audio samples in a separate thread.
This commit is contained in:
parent
41d25ba891
commit
95487a5293
4 changed files with 58 additions and 54 deletions
|
|
@ -212,7 +212,6 @@ set(SWA_THIRDPARTY_INCLUDES
|
||||||
"${SWA_THIRDPARTY_ROOT}/imgui"
|
"${SWA_THIRDPARTY_ROOT}/imgui"
|
||||||
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
|
"${SWA_THIRDPARTY_ROOT}/libmspack/libmspack/mspack"
|
||||||
"${SWA_THIRDPARTY_ROOT}/magic_enum/include"
|
"${SWA_THIRDPARTY_ROOT}/magic_enum/include"
|
||||||
"${SWA_THIRDPARTY_ROOT}/miniaudio"
|
|
||||||
"${SWA_THIRDPARTY_ROOT}/stb"
|
"${SWA_THIRDPARTY_ROOT}/stb"
|
||||||
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c"
|
"${SWA_THIRDPARTY_ROOT}/tiny-AES-c"
|
||||||
"${SWA_THIRDPARTY_ROOT}/TinySHA1"
|
"${SWA_THIRDPARTY_ROOT}/TinySHA1"
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,8 @@
|
||||||
static PPCFunc* g_clientCallback{};
|
static PPCFunc* g_clientCallback{};
|
||||||
static uint32_t g_clientCallbackParam{}; // pointer in guest memory
|
static uint32_t g_clientCallbackParam{}; // pointer in guest memory
|
||||||
static SDL_AudioDeviceID g_audioDevice{};
|
static SDL_AudioDeviceID g_audioDevice{};
|
||||||
static std::unique_ptr<GuestThreadContext> g_audioCtx;
|
|
||||||
static uint32_t* g_audioOutput;
|
|
||||||
static bool g_downMixToStereo;
|
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()
|
void XAudioInitializeSystem()
|
||||||
{
|
{
|
||||||
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
|
SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback");
|
||||||
|
|
@ -32,7 +20,6 @@ void XAudioInitializeSystem()
|
||||||
desired.format = AUDIO_F32SYS;
|
desired.format = AUDIO_F32SYS;
|
||||||
desired.channels = XAUDIO_NUM_CHANNELS;
|
desired.channels = XAUDIO_NUM_CHANNELS;
|
||||||
desired.samples = XAUDIO_NUM_SAMPLES;
|
desired.samples = XAUDIO_NUM_SAMPLES;
|
||||||
desired.callback = AudioCallback;
|
|
||||||
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
|
g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
|
||||||
|
|
||||||
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS)
|
if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS)
|
||||||
|
|
@ -44,6 +31,44 @@ void XAudioInitializeSystem()
|
||||||
g_downMixToStereo = (obtained.channels == 2);
|
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)
|
void XAudioRegisterClient(PPCFunc* callback, uint32_t param)
|
||||||
{
|
{
|
||||||
auto* pClientParam = static_cast<uint32_t*>(g_userHeap.Alloc(sizeof(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;
|
g_clientCallback = callback;
|
||||||
|
|
||||||
SDL_PauseAudioDevice(g_audioDevice, 0);
|
SDL_PauseAudioDevice(g_audioDevice, 0);
|
||||||
|
g_audioThread = std::make_unique<std::thread>(AudioThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XAudioSubmitFrame(void* samples)
|
void XAudioSubmitFrame(void* samples)
|
||||||
|
|
@ -68,6 +94,8 @@ void XAudioSubmitFrame(void* samples)
|
||||||
|
|
||||||
auto floatSamples = reinterpret_cast<be<float>*>(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++)
|
for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||||
{
|
{
|
||||||
float ch0 = floatSamples[0 * 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 ch4 = floatSamples[4 * XAUDIO_NUM_SAMPLES + i];
|
||||||
float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
|
float ch5 = floatSamples[5 * XAUDIO_NUM_SAMPLES + i];
|
||||||
|
|
||||||
float left = ch0 + ch2 * 0.75f + ch4;
|
audioFrames[i * 2 + 0] = ch0 + ch2 * 0.75f + ch4;
|
||||||
float right = ch1 + ch2 * 0.75f + ch5;
|
audioFrames[i * 2 + 1] = 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto rawSamples = reinterpret_cast<be<uint32_t>*>(samples);
|
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 i = 0; i < XAUDIO_NUM_SAMPLES; i++)
|
||||||
{
|
{
|
||||||
for (size_t j = 0; j < XAUDIO_NUM_CHANNELS; j++)
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ GuestThreadContext::GuestThreadContext(uint32_t cpuNumber)
|
||||||
ppcContext.fn = (uint8_t*)g_codeCache.bucket;
|
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.r1.u64 = g_memory.MapVirtual(thread + PCR_SIZE + TLS_SIZE + TEB_SIZE + STACK_SIZE); // stack pointer
|
||||||
ppcContext.r13.u64 = g_memory.MapVirtual(thread);
|
ppcContext.r13.u64 = g_memory.MapVirtual(thread);
|
||||||
|
ppcContext.fpscr.loadFromHost();
|
||||||
|
|
||||||
assert(GetPPCContext() == nullptr);
|
assert(GetPPCContext() == nullptr);
|
||||||
SetPPCContext(ppcContext);
|
SetPPCContext(ppcContext);
|
||||||
|
|
@ -77,7 +78,7 @@ uint32_t GuestThread::Start(const GuestThreadParams& params)
|
||||||
GuestThreadContext ctx(cpuNumber);
|
GuestThreadContext ctx(cpuNumber);
|
||||||
ctx.ppcContext.r3.u64 = params.value;
|
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;
|
return ctx.ppcContext.r3.u32;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,16 +76,16 @@ static std::atomic<uint32_t> g_keSetEventGeneration;
|
||||||
|
|
||||||
struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
|
struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
|
||||||
{
|
{
|
||||||
std::atomic<uint32_t> count;
|
std::counting_semaphore<> semaphore;
|
||||||
uint32_t maximumCount;
|
uint32_t maximumCount;
|
||||||
|
|
||||||
Semaphore(XKSEMAPHORE* semaphore)
|
Semaphore(XKSEMAPHORE* semaphore)
|
||||||
: count(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
|
: semaphore(semaphore->Header.SignalState), maximumCount(semaphore->Limit)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Semaphore(uint32_t count, uint32_t maximumCount)
|
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)
|
if (timeout == 0)
|
||||||
{
|
{
|
||||||
uint32_t currentCount = count.load();
|
return semaphore.try_acquire() ? STATUS_SUCCESS : STATUS_TIMEOUT;
|
||||||
if (currentCount != 0)
|
|
||||||
{
|
|
||||||
if (count.compare_exchange_weak(currentCount, currentCount - 1))
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return STATUS_TIMEOUT;
|
|
||||||
}
|
}
|
||||||
else if (timeout == INFINITE)
|
else if (timeout == INFINITE)
|
||||||
{
|
{
|
||||||
uint32_t currentCount;
|
semaphore.acquire();
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
currentCount = count.load();
|
|
||||||
if (currentCount != 0)
|
|
||||||
{
|
|
||||||
if (count.compare_exchange_weak(currentCount, currentCount - 1))
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
count.wait(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -130,13 +109,7 @@ struct Semaphore final : KernelObject, HostObject<XKSEMAPHORE>
|
||||||
|
|
||||||
void Release(uint32_t releaseCount, uint32_t* previousCount)
|
void Release(uint32_t releaseCount, uint32_t* previousCount)
|
||||||
{
|
{
|
||||||
if (previousCount != nullptr)
|
semaphore.release(releaseCount);
|
||||||
*previousCount = count;
|
|
||||||
|
|
||||||
assert(count + releaseCount <= maximumCount);
|
|
||||||
|
|
||||||
count += releaseCount;
|
|
||||||
count.notify_all();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue