Skip to content

SPU: free rung-out sustain voices + stop same-sample loop stacking - #6

Open
vjravenlight wants to merge 1 commit into
SlickAmogus:masterfrom
vjravenlight:fix/spu-adsr-loops
Open

SPU: free rung-out sustain voices + stop same-sample loop stacking#6
vjravenlight wants to merge 1 commit into
SlickAmogus:masterfrom
vjravenlight:fix/spu-adsr-loops

Conversation

@vjravenlight

Copy link
Copy Markdown

Targets the looping-SFX bug family from silent-hill-decomp#13: the wheelchair loop that survives load screens, the Nowhere grandfather-clock ring that swells into an endless crescendo (#27 there), and the Good+ ending SFX pile-ups.

Root cause

With ADSR off, a looping voice plays at static gain until an explicit key-off that in these cases never comes — on PSX the sample's own decreasing sustain/release envelope silenced it. And every re-strike of the same sample (each clock chime) key-ons a new voice while the old one still reads busy, so instances stack toward all 24 voices — that stack is the audible crescendo.

Fixes

  1. Decreasing sustain reaching level 0 now transitions to ENV_OFF. Previously the voice stayed in ENV_SUSTAIN at level 0 forever: silent, but reading keyed-on, so libsd's free-voice scan couldn't reclaim it and each re-strike pinned another channel.
  2. Key-on of a looping voice silences any other non-enveloped voice looping the same sample address. This is the PSX-behavior fallback for when envelopes aren't engaged (feature off, or the sample programs no ADSR): the new strike replaces the stale ring instead of stacking on it. Enveloped instances are skipped — they're already ringing out and may overlap naturally, as on hardware.

Both changes are no-ops for one-shot SFX (voice->looping gating).

Relationship to the ADSR gate

The envelope path currently ships default-off (g_SpuAdsrEnabled = 0) because its original render-thread tick placement deadlocked — but that was already fixed by moving the tick to the audio-timing thread (PsyX_main.cpp intrThread), and the game already force-enables it for the endings and Cybil maps. The companion silent-hill-decomp PR adds an adsr_enabled config key (default on) so the whole game benefits; with it off, fix 2 here still kills the stacking/crescendo on its own.

Builds clean, boots and plays (SH1 port, MSYS2/MinGW64).

🤖 Generated with Claude Code

Two fixes for the endless-loop SFX family (wheelchair over load screens,
the Nowhere grandfather-clock crescendo, ending SFX pile-ups):

- A decreasing ADSR sustain that reaches level 0 now transitions to
  ENV_OFF. Left in SUSTAIN at 0, the voice read keyed-on forever, so
  libsd's free-voice scan saw a silent-but-busy channel and every
  re-strike of the same sample pinned a fresh voice until all 24 drained.
- Key-on of a looping voice silences any OTHER non-enveloped voice still
  looping the same sample address. PSX hardware freed a re-struck loop
  via its ADSR release; without an engaged envelope the previous
  instance rang at full static gain on another voice and instances
  piled into an endless crescendo. Enveloped instances still overlap
  and ring out naturally.

Both are no-ops for one-shot SFX. Pairs with the game-side change that
turns ADSR on by default via config (the render-thread deadlock that
originally shipped it off was fixed by the audio-thread tick placement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants