fix(audio): Fix buffer underrun handling to prevent buzzing

- Repeat last valid frame during underruns instead of fading to silence
- Maintain audio continuity when buffers are temporarily unavailable
- Prevent harsh audio artifacts during loading screens (0 FPS scenarios)

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-12-05 16:37:08 +10:00
parent 453c4b4c4f
commit a3831e37ae

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@@ -229,10 +231,19 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
// If the playing buffer has been consumed or has no frames, we need a new one // If the playing buffer has been consumed or has no frames, we need a new one
if (playing_buffer.consumed || playing_buffer.frames == 0) { if (playing_buffer.consumed || playing_buffer.frames == 0) {
if (!queue.try_dequeue(playing_buffer)) { if (!queue.try_dequeue(playing_buffer)) {
// If no buffer was available we've underrun, fill the remaining buffer with // If no buffer was available we've underrun, repeat the last frame to maintain
// the last written frame and continue. // audio continuity. Use the last valid frame we played to avoid harsh transitions.
for (size_t i = frames_written; i < num_frames; i++) { if (frames_written > 0) {
std::memcpy(&output_buffer[i * frame_size], &last_frame[0], frame_size_bytes); // We have a valid last_frame, repeat it smoothly
for (size_t i = frames_written; i < num_frames; i++) {
std::memcpy(&output_buffer[i * frame_size], &last_frame[0], frame_size_bytes);
}
} else {
// No frames written yet, fill with silence
static constexpr std::array<s16, 6> silence{};
for (size_t i = frames_written; i < num_frames; i++) {
std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes);
}
} }
frames_written = num_frames; frames_written = num_frames;
continue; continue;