Files
Xenia-Canary/src/xenia/kernel/xboxkrnl/xboxkrnl_audio.cc
MechaCat02 7b6902e08f
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m34s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
[WIP] Audio/threading fixes + crash investigation; NEW ORACLE: crash is ours not the game
Snapshot for handoff. Contains the mission-audio + threading fixes and the
crash-investigation instrumentation (all diagnostic cvars default-OFF).

Fixes (behavioral):
- threading_posix.cc: reap-once guard on PosixCondition<Thread>::post_execution
  (double pthread_join at mission teardown -> fault loop -> audio death + freeze).
- xma_decoder.cc: work_event_->Set() in Pause() so the idle XMA worker observes
  paused_ and signals pause_fence_ (Pause() deadlock -> permanent audio death).
- audio_system / xma_context_master / xboxkrnl_audio / apu_flags / alsa: mission
  audio keepalive + guest_audio_flags + watchdogs.

Instrumentation (additive, default-off): xboxkrnl_debug cache-throw diag +
guest-catch dispatcher, xex_module PE/PDATA/EH scans, kernel_state mem_watch
(NOTE: mem_watch DEFAULTS TRUE -- an always-on host poll thread; prime crash suspect).

NEW ORACLE (see HANDOFF-crash-oracle-2026-07-16.md): stock 6e5b8324f built with
our toolchain + zero custom code = NO crash, NO sound-stop, plays the Ready Room.
=> the Ready-Room out_of_range crash is introduced by THESE changes, not the game
and not the (LTO-broken) build chain. Bisection plan + suspect ranking in the note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:43:50 +02:00

131 lines
4.6 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/audio_system.h"
#include "xenia/base/logging.h"
#include "xenia/emulator.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/xbox.h"
DECLARE_uint32(audio_flag);
namespace xe {
namespace kernel {
namespace xboxkrnl {
dword_result_t XAudioGetSpeakerConfig_entry(lpdword_t config_ptr) {
kernel_state()->xconfig()->ReadSetting(
XCONFIG_USER_CATEGORY,
XCONFIG_USER_CATEGORY_ENTRIES::XCONFIG_USER_AUDIO_FLAGS, config_ptr);
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT1(XAudioGetSpeakerConfig, kAudio, kImplemented);
dword_result_t XAudioGetVoiceCategoryVolumeChangeMask_entry(
lpunknown_t driver_ptr, lpdword_t out_ptr) {
assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000);
xe::threading::MaybeYield();
// Checking these bits to see if any voice volume changed.
// I think.
*out_ptr = 0;
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT2(XAudioGetVoiceCategoryVolumeChangeMask, kAudio, kStub,
kHighFrequency);
dword_result_t XAudioGetVoiceCategoryVolume_entry(dword_t unk,
lpfloat_t out_ptr) {
// Expects a floating point single. Volume %?
*out_ptr = 1.0f;
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT2(XAudioGetVoiceCategoryVolume, kAudio, kStub,
kHighFrequency);
dword_result_t XAudioEnableDucker_entry(dword_t unk) { return X_ERROR_SUCCESS; }
DECLARE_XBOXKRNL_EXPORT1(XAudioEnableDucker, kAudio, kStub);
dword_result_t XAudioRegisterRenderDriverClient_entry(
lpdword_t callback_ptr, lpdword_t driver_ptr,
const ppc_context_t& ppc_context) {
if (cvars::audio_watchdog) {
XELOGW("AUDIO-WD guest called XAudioRegisterRenderDriverClient lr={:08X}",
static_cast<uint32_t>(ppc_context->lr));
}
if (!callback_ptr) {
return X_E_INVALIDARG;
}
uint32_t callback = callback_ptr[0];
if (!callback) {
return X_E_INVALIDARG;
}
uint32_t callback_arg = callback_ptr[1];
auto audio_system = kernel_state()->emulator()->audio_system();
size_t index;
auto result = audio_system->RegisterClient(callback, callback_arg, &index);
if (XFAILED(result)) {
return result;
}
assert_true(!(index & ~0x0000FFFF));
*driver_ptr = 0x41550000 | (static_cast<uint32_t>(index) & 0x0000FFFF);
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT1(XAudioRegisterRenderDriverClient, kAudio,
kImplemented);
dword_result_t XAudioUnregisterRenderDriverClient_entry(
lpunknown_t driver_ptr, const ppc_context_t& ppc_context) {
assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000);
// Who tore audio down? The guest's return address names the game code path
// (resolve with zq.py fn <lr>) -- the game only does this on its own error /
// teardown logic, which is what we are hunting. Diagnostics only.
if (cvars::audio_watchdog) {
XELOGW("AUDIO-WD guest called XAudioUnregisterRenderDriverClient lr={:08X}",
static_cast<uint32_t>(ppc_context->lr));
}
auto audio_system = kernel_state()->emulator()->audio_system();
audio_system->UnregisterClient(driver_ptr.guest_address() & 0x0000FFFF);
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT1(XAudioUnregisterRenderDriverClient, kAudio,
kImplemented);
dword_result_t XAudioSubmitRenderDriverFrame_entry(lpunknown_t driver_ptr,
lpunknown_t samples_ptr) {
assert_true((driver_ptr.guest_address() & 0xFFFF0000) == 0x41550000);
auto audio_system = kernel_state()->emulator()->audio_system();
auto samples =
kernel_state()->memory()->TranslateVirtual<float*>(samples_ptr);
audio_system->SubmitFrame(driver_ptr.guest_address() & 0x0000FFFF, samples);
return X_ERROR_SUCCESS;
}
DECLARE_XBOXKRNL_EXPORT2(XAudioSubmitRenderDriverFrame, kAudio, kImplemented,
kHighFrequency);
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
DECLARE_XBOXKRNL_EMPTY_REGISTER_EXPORTS(Audio);