feat(audio): shuffle the soundtrack across gauntlet/contract runs + trim dead air - #42
Merged
Merged
Conversation
…g one track Replay modes picked one random track and looped it forever (numberOfLoops = -1), so a whole run was the same song on repeat. Now a run is a shuffle: play a random pool track once, then hand off to a fresh random pick 1s before it ends, for as long as the run lasts. The next pick always excludes the current track, so nothing plays back to back. - shuffleRunActive/shuffleCurrentTrack track the run; playMissionMusic is idempotent so crossing a room boundary keeps the run going rather than restarting on a new song. - Menu loops, boss tracks, boss chains and stop() all take over the shuffle. - Ducking (mini-games) defers via the existing deferredResumeKind, with a new .shuffleRun case. - A missing file retries up to 3 other picks rather than stranding the run in silence. Verified in-simulator over a 90-minute gauntlet run: 35 handoffs across 25 distinct tracks, zero back-to-back repeats. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Most tracks ended with 2-13s of sub -45dB tail. Harmless when a track looped, but with the run shuffle each tail became an audible gap between songs. Trimmed each track to its last non-silent moment with a stream copy (ffmpeg -c copy), so this is a container-level cut with no re-encode and no quality loss. 148s of dead air removed across 29 files; the longest cuts were intro_m5_music (-12.8s), m2_a (-12.7s) and drop_intro (-8.3s). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Two related audio fixes for the replay modes.
1. Runs shuffle instead of looping one track
Gauntlet/Contract picked one random track and set
numberOfLoops = -1— so an entire run was that same song on repeat.A run is now a shuffle: play a random pool track once, then hand off to a fresh random pick 1s before it ends, for as long as the run lasts. The next pick always excludes the current track, so nothing plays back to back.
playMissionMusicis idempotent — crossing a room boundary keeps the run going rather than restarting on a new song.stop()all take over the shuffle.deferredResumeKind, via a new.shuffleRuncase.2. Trimmed trailing dead air (29 tracks)
Most tracks ended with 2–13s of sub -45dB tail. Harmless while a track looped, but under the shuffle each tail became an audible gap between songs.
Trimmed to the last non-silent moment with a stream copy (
ffmpeg -c copy) — a container-level cut, no re-encode, no quality loss. 148s of dead air removed; longest cutsintro_m5_music(-12.8s),m2_a(-12.7s),drop_intro(-8.3s).Validation
Ran a gauntlet floor in the iOS Simulator for ~90 minutes:
intro_m1(60) + intro_m5_5(114) + m6_b(204) + m5_5_coldtrace(180) + intro_m4_5(118)= 676s, observed 11:38→11:50)m6_breports 204s, not 210s)Assets only + one Swift file; no files added/removed, so no XcodeGen regeneration needed.
🤖 Generated with Claude Code