mirror of
https://github.com/sal063/AC6_recomp
synced 2026-08-21 23:00:53 -04:00
Guard XMA loop handling against degenerate loop metadata
Streamed voices carrying degenerate loop metadata (loop_count 0xFF with loop_start == loop_end == 0) re-fired the loop machinery on every input buffer swap, truncating or skipping subframes of the stream. Require a real loop window (loop_start < loop_end, read_offset ≥ loop_end) before engaging, as in Xenia PR #1808. 47 confirmed fires in AC6; default on.
This commit is contained in:
@@ -19,6 +19,16 @@ REXCVAR_DEFINE_BOOL(audio_trace_render_driver_verbose, false, "Audio",
|
||||
"Trace render-driver activity");
|
||||
REXCVAR_DEFINE_BOOL(audio_deep_trace, false, "Audio",
|
||||
"Enable verbose runtime audio tracing");
|
||||
REXCVAR_DEFINE_BOOL(audio_xma_loop_guard, true, "Audio",
|
||||
"Require a real loop window (loop_start < loop_end, "
|
||||
"read_offset >= loop_end) before engaging XMA loop "
|
||||
"handling, as in current Xenia master (PR #1808, debugged "
|
||||
"on Ace Combat 6). Off: legacy behavior, where streamed "
|
||||
"voices carrying degenerate loop metadata (loop_count="
|
||||
"0xff, loop_start == loop_end == 0) re-fire the loop "
|
||||
"machinery on every input buffer swap, truncating/"
|
||||
"skipping subframes of the stream (confirmed firing on "
|
||||
"AC6 cutscene streams via the deep-trace counter).");
|
||||
REXCVAR_DEFINE_BOOL(audio_xma_preserve_timeline, true, "Audio",
|
||||
"When the XMA decoder backend rejects a damaged frame, emit that "
|
||||
"frame's worth of silence instead of dropping it. The input read "
|
||||
|
||||
+27
-2
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* ReXGlue native audio runtime
|
||||
* Part of the AC6 Recompilation project
|
||||
*/
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <native/stream.h>
|
||||
|
||||
REXCVAR_DECLARE(bool, audio_deep_trace);
|
||||
REXCVAR_DECLARE(bool, audio_xma_loop_guard);
|
||||
REXCVAR_DECLARE(bool, audio_xma_preserve_timeline);
|
||||
|
||||
namespace rex::audio {
|
||||
@@ -342,7 +343,31 @@ void XmaContext::UpdateLoopStatus(XMA_CONTEXT_DATA* data) {
|
||||
const uint32_t loop_start = std::max(kBitsPerPacketHeader, data->loop_start);
|
||||
const uint32_t loop_end = std::max(kBitsPerPacketHeader, data->loop_end);
|
||||
|
||||
if (data->input_buffer_read_offset != loop_end) {
|
||||
if (REXCVAR_GET(audio_xma_loop_guard)) {
|
||||
// Xenia master's TrySetupNextLoop guard (PR #1808, debugged on Ace
|
||||
// Combat 6): streamed voices can carry degenerate loop metadata
|
||||
// (loop_count=0xff, loop_start == loop_end == 0). The clamps above turn
|
||||
// that into start == end == kBitsPerPacketHeader, and SwapInputBuffer
|
||||
// resets the read offset to exactly kBitsPerPacketHeader, so without a
|
||||
// real-window check every buffer swap re-fires the loop machinery -
|
||||
// the frame output limit truncates that frame and the loop-start skip
|
||||
// drops subframes. Require loop_start < loop_end (raw values) and use
|
||||
// master's read_offset >= loop_end trigger.
|
||||
if (data->loop_start >= data->loop_end ||
|
||||
data->input_buffer_read_offset < loop_end) {
|
||||
if (data->loop_start >= data->loop_end &&
|
||||
data->input_buffer_read_offset == loop_end && IsDeepTraceEnabled()) {
|
||||
REXAPU_DEBUG(
|
||||
"XmaContext {}: loop guard suppressed degenerate loop fire "
|
||||
"(loop_start={} loop_end={} loop_count={} read_offset={})",
|
||||
id(), static_cast<uint32_t>(data->loop_start),
|
||||
static_cast<uint32_t>(data->loop_end),
|
||||
static_cast<uint32_t>(data->loop_count),
|
||||
static_cast<uint32_t>(data->input_buffer_read_offset));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (data->input_buffer_read_offset != loop_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user