Files
zombie-fps/docs/MUSIC.md
T

71 lines
3.3 KiB
Markdown

# Soundtrack — industrial metal loop
Endless survival loop played from the first frame, at `-11 dB`, independent of gameplay state.
## Source
Generated locally (2026-10-10) with **YuE2-3B Q8_0** through the audio.cpp CUDA build for the
Tesla V100 (`~/dev/audio.cpp`, `cmake -DCMAKE_CUDA_ARCHITECTURES=70`), seed 31337, 16 steps,
`cot=melody` + an external ABC score written for this track (K:Em Phrygian, `Q:1/4=168`,
96 bars; the model rendered it at a perceived 112.5 BPM). Style prompt: instrumental
industrial metal, Nine Inch Nails meets the Quake 2 soundtrack, machine drums, palm-muted
downtuned guitars, atonal synth stabs, no vocals.
`--lyrics "[instrumental]"` does **not** suppress singing (verified: male shouts 0:52-1:17 and
1:37-2:35). The instrumental was therefore obtained by **HTDemucs** vocal removal
(`--task sep --family htdemucs`, input resampled to 44.1 kHz, RTF 0.049) and remixed as
`drums + bass + other`.
Working files: `~/yue2-out/zombie-industrial/`.
## Loop construction
The track has a structural repeat (bars 24-30 ≈ 46-52, spectral similarity 0.95), so the loop
splices at matching material instead of cutting arbitrarily:
- region 51.37 s → 108.97 s of the instrumental master = 27 bars (bar = 2.1333 s, 112.5 BPM);
- rotation by one tail segment plus a **0.60 s** equal-power crossfade;
- result: **57.60 s**, seam dip ≤ 1.6 dB in 20 ms windows, sample step 0.04 (inaudible);
- perceptual check on a 3 s + 3 s excerpt around the seam: no click, no level hole, natural
continuation.
A longer variant (132 s) was rejected: its crossfade cancels (a -48 dB hole) because the two
overlapped sections are out of phase.
## Asset and import
`assets/audio/music/zombie_industrial_loop.ogg` — Ogg Vorbis q6, 48 kHz stereo, 1.0 MB,
-14 LUFS / -1.0 dBTP. Import: `loop=true`, `bpm=112.5`, `beat_count=108`, `bar_beats=4`.
The Web staging step (`tools/prepare_web.py`) copies the asset but ignores `*.import`, and the
staging import regenerates it with `loop=false`. `main.gd` therefore sets `stream.loop = true`
at runtime: keep that line, it is what makes the browser build loop.
## Runtime wiring
`scripts/main.gd`: `_setup_music()` creates the `AudioStreamPlayer` named `Music` (group
`music`), `_start_music()` starts it once, `_restart_music()` restarts it if the loop flag is
ever absent. On Web the start is deferred to the click that captures the mouse, because
browsers only allow audio after a user gesture. Music keeps playing while the player is dead;
`R` reloads the scene and the track restarts with it.
## Verification
```
godot --headless --path . --script res://tools/test_music.gd
godot --display-driver headless --audio-driver PulseAudio --path . --script res://tools/test_music.gd
godot --headless --path . -- --smoke
```
Both runs PASS: node present, `loop=true`, length 57.60 s, volume -11 dB, playback advancing
(confirmed on the real PulseAudio device, not only the Dummy driver), music still playing after
player death. Grenade, wave, death and damage-feedback tests also pass unchanged.
## License
YuE2-3B weights are **CC BY-NC 4.0** — a fact about the model, not about this track.
The project owner states that they hold the full licence and title to the generated track and has
authorised its use, including public deployment, on the same basis as the Valve assets already
shipped in this repository.