The trickiest reading on the disc lived in the Bevy viewer: resolving a
cutscene's voice to a continuous byte REGION of the sound stream, because the
movie voices are one XMA stream chunked into VOICE_*.slb entries whose
boundaries do not match the cues -- a cue routinely spans two chunks, so a .slb
need not hold the track its name claims.
That put the logic most likely to be re-derived incorrectly in the crate least
likely to be reused. The Godot port's exporter needs the same answers, and there
must be one implementation of them.
New `sylpheed_formats::media` owns every case where the bytes of one playable
thing are not one archive entry: segment-spanning reads, multi-sub-wave banks,
and the voice-region resolution. Callers supply bytes through a `DiscSource`
trait, so the viewer keeps its ISO/directory abstraction and a headless consumer
gets `DirectorySource` for free.
The seam is deliberate: this module returns XMA RIFFs, not PCM. Decoding means
shelling out to FFmpeg, which is native-only and a policy decision for the
consumer -- everything up to "here are the bytes that belong together" is disc
knowledge, everything after it is a codec choice.
The four moved functions were previously untested; `tests/media_disc.rs` now
pins them, including the negative the corpus paid for -- an unbound movie must
stay unvoiced rather than borrow a neighbour's clip, which was tried and played
the WRONG recording.
The algorithm is unchanged, moved verbatim (same window sizes, same fallbacks).
The new disc tests pass; the broader audio suite was not re-run in this pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BGM_001.slb is three sub-waves (10 KB, 4.47 MB, 4.67 MB) and the decoder
concatenates them into one 347 s track. That is a default nobody chose, not a
decision: two near-equal halves could be intro + loop, two variations, or two
halves of one piece, and a menu that loops its music needs to know which.
Found while wiring the Audio Library up to the shared banks. Recorded in
HANDOFF.md as a trap too, so the port does not build looping on top of the
concatenated track before the question is answered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>