[APU] Fix mission-audio silence: ALSA underrun keepalive + guest_audio_flags

Two Linux mission-audio fixes for Project Sylpheed (audio played in
intro/menu but died when a mission finished loading, never returning):

- alsa_audio_driver: when the guest stalls (mission "Preparing for Sortie"
  load) and the ring buffer empties, feed silence to keep the PCM alive
  instead of sleeping. Previously the small buffer drained, XRUN'd, and
  playback never recovered (matches the known "audio muted permanently").
- xconfig: make the guest speaker config a cvar (guest_audio_flags,
  default Digital Stereo) instead of hardcoded Dolby Digital surround.

(The practical fix on this box also needed log_mask=13 to stop kernel log
spam starving the audio pipeline during missions — config, not code.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-12 16:49:25 +02:00
parent 919e526fd8
commit f10484834c
2 changed files with 37 additions and 3 deletions

View File

@@ -336,8 +336,30 @@ void ALSAAudioDriver::WorkerThread() {
size_t current_write = write_index_.load(std::memory_order_acquire);
if (current_read == current_write) {
// No data available, sleep and try again
std::this_thread::sleep_for(std::chrono::milliseconds(5));
// No guest audio available. During long guest stalls (e.g. a mission
// "Preparing for Sortie" load) the game stops submitting frames; if we
// merely sleep, ALSA drains its small buffer, XRUNs, and playback never
// recovers (the game's audio is silent for the rest of the session).
// Keep the PCM alive by topping it up with silence whenever the queued
// audio runs low, so real audio resumes seamlessly once the stall ends.
snd_pcm_sframes_t avail = snd_pcm_avail_update(pcm_handle_);
if (avail < 0) {
if (!RecoverFromUnderrun(avail)) {
running_ = false;
}
continue;
}
snd_pcm_uframes_t queued =
(buffer_size_ > (snd_pcm_uframes_t)avail) ? buffer_size_ - avail : 0;
if (queued < period_size_ * 2) {
snd_pcm_sframes_t w =
snd_pcm_writei(pcm_handle_, silence.get(), period_size_);
if (w < 0) {
RecoverFromUnderrun(w);
}
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
continue;
}

View File

@@ -9,6 +9,7 @@
#include "xenia/kernel/xconfig.h"
#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
#include "xenia/base/filesystem.h"
@@ -19,6 +20,17 @@
#include <ranges>
// Speaker configuration reported to the guest (XCONFIG_USER_AUDIO_FLAGS).
// Default = 0 (Digital Stereo): the Linux/ALSA APU driver's 5.1 (6-channel)
// path yields silence in some titles (e.g. Project Sylpheed missions), while
// stereo output works, so stereo is the safe default. Set to 0x00010001 for
// Dolby Digital + Pro Logic surround.
DEFINE_uint32(guest_audio_flags, 0,
"Speaker config reported to the guest (XCONFIG_USER_AUDIO_FLAGS): "
"0 = Digital Stereo (safe on Linux/ALSA), 0x00010001 = Dolby "
"Digital + Pro Logic surround.",
"APU");
namespace xe {
namespace kernel {
@@ -94,7 +106,7 @@ void XConfig::SetDefaults() {
xconfig_data_.user.language = static_cast<uint32_t>(XLanguage::kEnglish);
xconfig_data_.user.country =
static_cast<uint8_t>(XOnlineCountry::kUnitedStates);
xconfig_data_.user.audio_flags = DolbyDigital | DolbyProLogic;
xconfig_data_.user.audio_flags = cvars::guest_audio_flags;
xconfig_data_.user.av_pack_hdmi_sz = XHDTVResolution.at(1).to_host();
xconfig_data_.user.av_pack_component_sz = XHDTVResolution.at(1).to_host();
xconfig_data_.user.av_pack_vga_sz = XVGAResolution.at(3).to_host();