Files
RustRaptor/docs/music-validation.md

78 lines
4.3 KiB
Markdown

# Mod mission music
Package music uses namespaced `MusicDef` entries and a mission's optional
`music_track`. It plays through the existing Rust MUS/GENMIDI FM synthesizer,
with the ordinary Music volume and fade controls. Sound effects keep their
separate digital bus. Original numeric mission tracks, menus and classic
cinematics keep their existing music paths.
Tracks use package-relative `.mus` files. The registry checks references,
duplicate IDs, namespaces and explicit overrides. Activation decodes the
winning owner's file and rejects missing, escaping, malformed or oversized
scores. The package's existing SHA-256 hash includes the score file, so pilot
mod compatibility covers music changes. Applying mods clears the bounded
decoded-score cache and returns an active mod track to main-menu music.
Limits apply to mod music without changing the legacy decoder: 128 KiB per
file, 16,384 events, at most 256 events at any service tick, positive duration,
and at most 126,000 score ticks (30 minutes at 70 Hz). Header ranges, supported
event types, primary channel count, velocities, controllers and the final end
event are checked by `MusScore::decode`. Cache allocation is bounded separately
from digital effects. Only MUS is supported; external MIDI and SoundFonts
remain deferred.
The host retains a namespaced desired-track ID and its loop flag. A mission
preview can select its music before SDL audio is initialized; attaching the
device applies that pending score. Re-selecting the same active ID/loop
preserves playback position. Selecting original menu or cinematic music clears
the mod identity. A failed score load leaves the pilot and menu state intact.
Synthetic checks cover registration and missing references, conflicting
legacy/custom choices, explicit overrides and owner resolution, hash changes,
cache invalidation, path and symlink containment, unsupported formats, file
size, event density, duration and channel limits. FM output checks exercise the
definition's actual loop flag and show that Music volume mutes the mod score
while digital effects remain audible. A one-play score's ending tail has less
than one twentieth the energy of the looping score in the tested fixture.
```sh
cargo test --locked --no-default-features \
--manifest-path RustRaptor/Cargo.toml --test music
```
Portable host checks verify pending identity, original-menu transitions and
rollback when music cannot load. The paused SDL test attaches a real device
object and compares consecutive FM PCM blocks against uninterrupted reference
playback, proving that selecting the same track does not restart it:
```sh
LD_LIBRARY_PATH="$HOME/.local/lib" cargo test --locked \
--manifest-path RustRaptor/Cargo.toml --lib mission_mod_music
LD_LIBRARY_PATH="$HOME/.local/lib" cargo test --locked \
--manifest-path RustRaptor/Cargo.toml --lib failed_mission_music_load
LD_LIBRARY_PATH="$HOME/.local/lib" SDL_AUDIODRIVER=dummy cargo test --locked \
--manifest-path RustRaptor/Cargo.toml --lib pending_mod_music_resumes -- --ignored
```
These checks validate the engine path using synthetic scores. Composition,
listening review and original-data in-game soundtrack captures are separate
release checks for each music package.
## Black Tide soundtrack
`mods/iron_coast_stage2_music` contains three original compositions: **Rivetline
Assault** (D minor, 144 BPM, 93.33 seconds), **Undertow Pursuit** (F-sharp minor,
168 BPM, 91.43 seconds), and **Black Tide Rising** (C minor, 126 BPM, 106.67 seconds).
Five FM channels and percussion use the bundled original GENMIDI instruments.
They introduce no original-score copies or replacements. Mission definitions
choose their namespaced IDs and the usual Music setting controls playback.
The package's real decoder and FM renderer checks cover every second, peak
levels at normal/full volume, exact mute, voice counts, looping boundaries and
one-play release tails. Recomposition and all six full/boundary PCM renders
reproduce byte-for-byte. `validate_music.py` resolves the exact active Cargo
library artifacts instead of guessing from modification time, so separate
desktop and headless builds cannot select incompatible Serde dependencies.
The package README documents the composition and reproduction commands.
These are automated score/PCM checks; physical speaker listening is separate.