Files
RustRaptor/docs/music-validation.md
T

4.3 KiB

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.

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:

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.