mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
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:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user