From 83aafa31f047a4937dec971a9e2d1ee8727e28a1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 22 Nov 2024 02:33:57 +0200 Subject: [PATCH] Better audio playback code --- include/audio/dsp_core.hpp | 3 ++- include/audio/miniaudio_device.hpp | 5 +++- src/core/audio/miniaudio_device.cpp | 36 +++++++++++++++++++++-------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/include/audio/dsp_core.hpp b/include/audio/dsp_core.hpp index 5addfd19..5a434b91 100644 --- a/include/audio/dsp_core.hpp +++ b/include/audio/dsp_core.hpp @@ -24,7 +24,8 @@ namespace Audio { static constexpr u64 lleSlice = 16384; class DSPCore { - using Samples = Common::RingBuffer; + // 0x2000 stereo (= 2 channel) samples + using Samples = Common::RingBuffer; protected: Memory& mem; diff --git a/include/audio/miniaudio_device.hpp b/include/audio/miniaudio_device.hpp index 3a658e58..f3b212e2 100644 --- a/include/audio/miniaudio_device.hpp +++ b/include/audio/miniaudio_device.hpp @@ -3,11 +3,12 @@ #include #include +#include "helpers.hpp" #include "miniaudio.h" #include "ring_buffer.hpp" class MiniAudioDevice { - using Samples = Common::RingBuffer; + using Samples = Common::RingBuffer; static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate static constexpr ma_uint32 channelCount = 2; // Audio output is stereo @@ -20,6 +21,8 @@ class MiniAudioDevice { bool initialized = false; bool running = false; + // Store the last stereo sample we output. We play this when underruning to avoid pops. + std::array lastStereoSample; std::vector audioDevices; public: diff --git a/src/core/audio/miniaudio_device.cpp b/src/core/audio/miniaudio_device.cpp index 265f2f78..cd537aef 100644 --- a/src/core/audio/miniaudio_device.cpp +++ b/src/core/audio/miniaudio_device.cpp @@ -1,5 +1,7 @@ #include "audio/miniaudio_device.hpp" +#include + #include "helpers.hpp" MiniAudioDevice::MiniAudioDevice() : initialized(false), running(false), samples(nullptr) {} @@ -87,20 +89,34 @@ void MiniAudioDevice::init(Samples& samples, bool safe) { deviceConfig.aaudio.usage = ma_aaudio_usage_game; deviceConfig.wasapi.noAutoConvertSRC = true; + lastStereoSample = {0, 0}; + deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) { auto self = reinterpret_cast(device->pUserData); - s16* output = reinterpret_cast(out); - const usize maxSamples = std::min(self->samples->Capacity(), usize(frameCount * channelCount)); - - // Wait until there's enough samples to pop - while (self->samples->size() < maxSamples) { - // If audio output is disabled from the emulator thread, make sure that this callback will return and not hang - if (!self->running) { - return; - } + if (!self->running) { + return; } - self->samples->pop(output, maxSamples); + s16* output = reinterpret_cast(out); + usize samplesWritten = 0; + samplesWritten += self->samples->pop(output, frameCount * channelCount); + + // Get the last sample for underrun handling + if (samplesWritten != 0) { + std::memcpy(&self->lastStereoSample[0], &output[(samplesWritten - 1) * 2], sizeof(lastStereoSample)); + } + + // If underruning, copy the last output sample + { + s16* pointer = &output[samplesWritten * 2]; + s16 l = self->lastStereoSample[0]; + s16 r = self->lastStereoSample[1]; + + for (usize i = samplesWritten; i < frameCount; i++) { + *pointer++ = l; + *pointer++ = r; + } + } }; if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {