Fix Samus charge whoosh persisting after pause (hotfix)#130
Merged
Conversation
The IDO/N64 audio thread blocks on osRecvMesg(&sSYAudioSPTaskMesgQueue) each frame; while the pause menu owns the SP, that message stops landing, the FGM bytecode interpreter stops ticking, and looping FGM voices wind down naturally. The port's audio thread runs on the VI tick alone, no SP gate, so the bytecode keeps cycling and the voice keeps playing. Per-fighter ftParamStopLoopSFX doesn't work on the port because siz34->unk_0x28 (the cached EE0C ptr) goes stale across the bytecode's natural case-2 → 0 → free cycle. Diagnosed via per-frame trace that logged on_0x3C=0 for every audio frame after pause — the EE0C we marked was already freed and the audible voice was on a different EE0C. Fix lives in the decomp submodule (port-patches branch): - portAudioPurgeFGMs in n_env.c walks unk_alsound_0x3C directly and calls n_alSynStopVoice + n_alSynFreeVoice on every voice (synthesizer-level kill, bypasses the envelope-mixer fade-to-zero path), then NULLs every siz34->unk_0x28. - ftParamStopAllFightersLoopSFX clears each fighter's p_loop_sfx cache and delegates the actual kill to portAudioPurgeFGMs. - Hooked into ifCommonBattlePauseInitInterface before the existing func_80026594_27194 call. Pause beep is allocated AFTER the purge on a fresh slot; BGM lives on the CSPlayer pipeline, untouched. This is a hotfix at pause-init only — proper root cause is "port audio thread doesn't deschedule like SP-gated N64 thread"; real fixes (gate n_alAudioFrame on game state, or lock siz34->unk_0x28) have wider blast radius and are deferred. Verified pre-refactor (vendored src/) on RelWithDebInfo Windows build: whoosh cuts on pause, pause beep plays, resume continues without the loop re-arming; user reported "Fixed". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4d1626a to
cf8fa5a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
portAudioPurgeFGMsinn_env.c, hooked fromftParamStopAllFightersLoopSFXin the existing pause path. Walksunk_alsound_0x3Cdirectly and callsn_alSynStopVoice+n_alSynFreeVoiceon every voice, then NULLs every siz34'sunk_0x28.Root cause vs hotfix
On IDO/N64 the audio thread is gated each frame by
osRecvMesg(&sSYAudioSPTaskMesgQueue); while the pause menu owns the SP, the message stops landing, the FGM bytecode tick stops advancing, and looping voices wind down naturally because the wavetable loop region stops getting re-armed bycase 1ticks. The port's audio thread runs on the VI tick alone — no SP, no handshake — so the bytecode keeps cycling and the voice keeps playing.The polite per-fighter
ftParamStopLoopSFX(fp)route doesn't work on the port:siz34->unk_0x28(the EE0C ptr cached when the bytecode allocated the current note's voice) goes stale across the bytecode's natural case-2 → 0 → free cycle. Per-frame trace loggedon_0x3C=0for every audio frame after pause — the EE0C the polite path wroteunk2A=2to was already freed and the audible voice was on a different EE0C entirely.Real root-cause fixes (out of scope here):
n_alAudioFrameongSCManagerBattleState->game_status != Pause. Emulates SP starvation. Side effect: short SFX in their decay tail at pause cut off.siz34->unk_0x28reads/writes between game thread and audio thread. Lets the polite path work as designed. Invasive across the audio engine.Either would replace
portAudioPurgeFGMsand theftParamStopAllFightersLoopSFXhook. Until then the queue-wide purge is the working hotfix.Submodule pointer bump
Decomp pushed to
JRickey/ssb-decomp-re#port-patchesat5cf15364c. The four-file diff underdecomp/src/rides along via this submodule update.Test plan
src/, RelWithDebInfo Windows): whoosh cuts on pause, pause beep plays, resume continues without re-arming. User confirmed "Fixed".decomp/submodule, RelWithDebInfo Windows): not verified by me — the refactored origin/main has pre-existing MSVC build breaks unrelated to this fix:decomp/include/stdarg.h:34— re-definesva_list __builtin_va_liston MSVC where that intrinsic doesn't exist (C2061). Tried to fix with#ifndef _MSC_VERguard but reverted to keep this PR scoped.decomp/src/mn/mnmaps/mnmaps.c:1073(and ~28 other sites) —#ifdef PORTdirectives inside function-like macro arguments. Even with/Zc:preprocessor, MSVC's conforming preprocessor rejects these as undefined behavior (C2059); this is a pre-existing decomp-source-level issue.The fix code itself is mechanically identical to the version that tested working pre-refactor — it's just the mechanical path migration (
src/→decomp/src/).🤖 Generated with Claude Code