From de2527ade580119a3f58e92714728bf7fd252e25 Mon Sep 17 00:00:00 2001 From: Dipshet <264011288+Dipshet@users.noreply.github.com> Date: Wed, 5 Aug 2026 20:31:37 +0200 Subject: [PATCH] Revert "Fix doubled cutscene dialogue: fold only the fronts while a cutscene plays" This reverts commit edcb6314f2b1b06e70dbb214674eb7c2325f8ab1. The fold was a mitigation: the doubling was never in the assets but came from the decoder dropping straddled-header frames, letting the premix streams drift and comb. With the decode fixed at the root, the full summing fold is clean and cutscenes regain centre and rear content. --- src/ac6_backend_fixes/ac6_cutscene_resync.cpp | 7 -- .../include/native/audio/conversion.h | 111 +----------------- .../src/native/audio/audio_system.cpp | 41 ------- 3 files changed, 4 insertions(+), 155 deletions(-) diff --git a/src/ac6_backend_fixes/ac6_cutscene_resync.cpp b/src/ac6_backend_fixes/ac6_cutscene_resync.cpp index ed773a68..f05607a9 100644 --- a/src/ac6_backend_fixes/ac6_cutscene_resync.cpp +++ b/src/ac6_backend_fixes/ac6_cutscene_resync.cpp @@ -31,7 +31,6 @@ #include #include -#include #include #include #include @@ -122,12 +121,6 @@ void ExecWithResync(PPCContext& ctx, uint8_t* base, } g_session.last_exec_ms = now_ms; - // Stamp the cinematic-audio gate for the stereo fold-down (conversion.h): - // the demo wrappers tick only while an in-engine cutscene plays, so their - // freshness is the "cutscene audio active" signal. Unconditional - the - // stamp is independent of whether the resync behavior itself is enabled. - rex::audio::NotifyCinematicAudioTick(now_ms); - uint64_t audio_samples = 0; const bool clock_ok = ReadAudioClockSamples(ctx, &audio_samples); if (clock_ok && !g_session.clock_valid) { diff --git a/thirdparty/rexglue-sdk/include/native/audio/conversion.h b/thirdparty/rexglue-sdk/include/native/audio/conversion.h index 7a91375a..46c828cc 100644 --- a/thirdparty/rexglue-sdk/include/native/audio/conversion.h +++ b/thirdparty/rexglue-sdk/include/native/audio/conversion.h @@ -3,40 +3,13 @@ #pragma once -#include -#include -#include #include #include #include -#include #include #include -REXCVAR_DECLARE(bool, audio_cutscene_downmix); -REXCVAR_DECLARE(double, audio_downmix_center_gain); -REXCVAR_DECLARE(double, audio_downmix_surround_gain); -REXCVAR_DECLARE(double, audio_downmix_lfe_gain); -REXCVAR_DECLARE(double, audio_downmix_cutscene_center_gain); -REXCVAR_DECLARE(double, audio_downmix_cutscene_surround_gain); -REXCVAR_DECLARE(double, audio_downmix_cutscene_lfe_gain); -REXCVAR_DECLARE(double, audio_downmix_cutscene_trim); -REXCVAR_DECLARE(double, audio_downmix_cutscene_ramp_ms); - -namespace rex::audio { - -// Wall-clock ms of the last in-engine cutscene sequencer tick, stamped by the -// demo-tick hook (ac6_cutscene_resync). Lets the stereo fold-down apply -// cutscene-specific gains without reaching into game code. INT64_MIN = never. -inline std::atomic g_last_cinematic_audio_tick_ms{INT64_MIN}; - -inline void NotifyCinematicAudioTick(int64_t now_ms) { - g_last_cinematic_audio_tick_ms.store(now_ms, std::memory_order_relaxed); -} - -} // namespace rex::audio - namespace rex::audio::conversion { inline constexpr float kStereoDownmixCenterGain = 0.70710678f; @@ -47,81 +20,6 @@ inline constexpr float kStereoDownmixNormalize = 1.0f / (1.0f + kStereoDownmixCenterGain + kStereoDownmixSurroundGain + kStereoDownmixLfeGain); -// Live fold-down gains for the path AC6 actually plays through (the AMD64 -// planar fold below; the other variants keep the compile-time constants). -// The base gains default to those constants; the cutscene set applies while -// the demo sequencer is ticking, because AC6's cutscene mixer submits its premix -// spread across ALL six speaker slots as decorrelated near-copies of one mix -// (measured: equal RMS on every channel, inter-channel correlation 0.69-0.94 -// at exactly lag 0, identical structure across scenes). Real speakers -// separate the copies acoustically; an electrical 6-to-2 sum combs them - -// heard as doubled dialogue. The fronts alone carry the complete mix, so the -// cutscene defaults fold only the fronts. Gameplay audio (discrete channels) -// is bit-identical to the old constants. -struct StereoDownmixGains { - float center; - float surround; - float lfe; - float normalize; -}; - -inline StereoDownmixGains GetStereoDownmixGains() { - // The cutscene gains engage while demo-wrapper ticks are fresh; f slews - // over ramp_ms so fold changes never step. Known cosmetic (accepted): the - // wrapper keeps ticking through the gallery's menu->scene transitions, so - // the gallery's front-weighted transition SFX plays through the - // fronts-only fold hot; campaign flows are unaffected. - const int64_t now_ms = std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - const int64_t last_tick = - g_last_cinematic_audio_tick_ms.load(std::memory_order_relaxed); - const bool engaged = REXCVAR_GET(audio_cutscene_downmix) && - last_tick != INT64_MIN && (now_ms - last_tick) <= 250; - static float f_state = 0.0f; // 0 = base fold, 1 = full cutscene gains - static int64_t f_last_ms = INT64_MIN; - const double ramp = std::max(1.0, REXCVAR_GET(audio_downmix_cutscene_ramp_ms)); - float step = 1.0f; - if (f_last_ms != INT64_MIN && now_ms >= f_last_ms) { - step = float(std::min(1.0, double(now_ms - f_last_ms) / ramp)); - } - f_last_ms = now_ms; - const float target = engaged ? 1.0f : 0.0f; - if (target > f_state) { - f_state = std::min(target, f_state + step); - } else { - f_state = std::max(target, f_state - step); - } - const float f = f_state; - auto clamp_gain = [](double v) { - return std::min(2.0f, std::max(0.0f, float(v))); - }; - auto blend = [&](double cutscene_value, double base_value) { - const float base = clamp_gain(base_value); - const float cut = - cutscene_value >= 0.0 ? clamp_gain(cutscene_value) : base; - return base + (cut - base) * f; - }; - StereoDownmixGains gains; - gains.center = blend(REXCVAR_GET(audio_downmix_cutscene_center_gain), - REXCVAR_GET(audio_downmix_center_gain)); - gains.surround = blend(REXCVAR_GET(audio_downmix_cutscene_surround_gain), - REXCVAR_GET(audio_downmix_surround_gain)); - gains.lfe = blend(REXCVAR_GET(audio_downmix_cutscene_lfe_gain), - REXCVAR_GET(audio_downmix_lfe_gain)); - // Normalization follows the live gains so loudness stays consistent at any - // setting; at the stock base gains this equals the old fixed constant, so - // gameplay output is bit-identical. The near-copy cutscene mixes are - // self-correcting under it (fold of N unity-ish copies divided by the gain - // sum lands at the same level whichever channels fold); the measured - // residual vs the old fold is +0.6 dB, cancelled by the default trim. - gains.normalize = 1.0f / (1.0f + gains.center + gains.surround + gains.lfe); - const float trim = std::min( - 2.0f, std::max(0.0f, float(REXCVAR_GET(audio_downmix_cutscene_trim)))); - gains.normalize *= 1.0f + (trim - 1.0f) * f; - return gains; -} - inline float SanitizeGuestAudioSample(float sample) { if (!std::isfinite(sample)) { return 0.0f; @@ -172,11 +70,10 @@ inline void sequential_6_BE_to_interleaved_2_LE(float* output, const float* inpu const __m128i byte_swap_shuffle = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); - const StereoDownmixGains live_gains = GetStereoDownmixGains(); - const __m128 center_gain = _mm_set1_ps(live_gains.center); - const __m128 surround_gain = _mm_set1_ps(live_gains.surround); - const __m128 lfe_gain = _mm_set1_ps(live_gains.lfe); - const __m128 normalize = _mm_set1_ps(live_gains.normalize); + const __m128 center_gain = _mm_set1_ps(kStereoDownmixCenterGain); + const __m128 surround_gain = _mm_set1_ps(kStereoDownmixSurroundGain); + const __m128 lfe_gain = _mm_set1_ps(kStereoDownmixLfeGain); + const __m128 normalize = _mm_set1_ps(kStereoDownmixNormalize); const __m128 peak_headroom = _mm_set1_ps(kStereoDownmixPeakHeadroom); const __m128 sign_mask = _mm_set1_ps(-0.0f); diff --git a/thirdparty/rexglue-sdk/src/native/audio/audio_system.cpp b/thirdparty/rexglue-sdk/src/native/audio/audio_system.cpp index d2b7435a..3288bd1c 100644 --- a/thirdparty/rexglue-sdk/src/native/audio/audio_system.cpp +++ b/thirdparty/rexglue-sdk/src/native/audio/audio_system.cpp @@ -19,47 +19,6 @@ 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_cutscene_downmix, true, "Audio", - "Master switch for the cutscene-specific stereo fold-down " - "(fronts-only fold while the demo sequencer is active - " - "removes the doubled/combed dialogue caused by summing the " - "cutscene mixer's six decorrelated near-copies to stereo). " - "false = the original summing fold everywhere; the " - "audio_downmix_cutscene_* cvars then have no effect. " - "Gameplay audio is identical either way."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_center_gain, 0.70710678, "Audio", - "Front-center gain in the 6ch-to-stereo fold-down (was a " - "compile-time constant; default unchanged). Normalization " - "follows the live gains. Read continuously."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_surround_gain, 0.5, "Audio", - "Rear-channel gain in the 6ch-to-stereo fold-down. See " - "audio_downmix_center_gain."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_lfe_gain, 0.0, "Audio", - "LFE gain in the 6ch-to-stereo fold-down. See " - "audio_downmix_center_gain."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_cutscene_center_gain, 0.0, "Audio", - "Front-center fold-down gain used ONLY while an in-engine " - "cutscene is playing (demo sequencer active); negative = " - "inherit audio_downmix_center_gain. AC6's cutscene mixer " - "spreads the premix across ALL six speaker slots as " - "decorrelated near-copies of one mix; the fronts alone " - "carry the complete mix, so the cutscene fold takes only " - "them - center and surround default to 0 here."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_cutscene_surround_gain, 0.0, "Audio", - "Rear-channel fold-down gain during in-engine cutscenes; " - "negative = inherit. See audio_downmix_cutscene_center_gain."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_cutscene_lfe_gain, -1.0, "Audio", - "LFE fold-down gain during in-engine cutscenes; negative = " - "inherit. See audio_downmix_cutscene_center_gain."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_cutscene_trim, 0.933, "Audio", - "Extra output gain on the cutscene fold (ramped in with " - "the cutscene gains). Default 0.933 = the measured RMS " - "ratio of the fronts-only fold vs the stock summing fold " - "(-0.6 dB), so cutscene loudness matches the original " - "baseline; 1.0 = no trim."); -REXCVAR_DEFINE_DOUBLE(audio_downmix_cutscene_ramp_ms, 250.0, "Audio", - "Slew time between the base fold and the cutscene gains " - "when the demo-sequencer signal engages or releases."); 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 "