From de2fe4a110992fdbd9b951bf8030fdc33629641d Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Sat, 29 Aug 2026 14:56:17 +0000 Subject: [PATCH] re: the voice-region third chunk is a different structure from the BGM one The port hit a 2+1 chunk signature on a resolved movie-voice region and asked whether the bank-header explanation that closed HANDOFF Q10 also covers it, rather than assuming it. It does not, and the discriminator is mechanical. Disc-wide over the 95 English movie-voice regions the manifest binds: 78 open with a bank header -- bank_header_len fires, 10240 B = 5 packets exactly, every time. That is the BGM case. 17 open with a leading headerless stream -- bank_header_len is None, and all 17 have length congruent to 1392 mod 2048, the disc s own derived data offset. No other residue occurs. 0 begin at a RIFF. Counting chunks does not discriminate: 8 bank-header regions also yield three chunks. slb.rs already predicted this in its own doc comment -- the header signature has "zero false positives on the 7993 mid-bank windows, where the leading region IS real" -- and a voice region is a mid-bank window by construction. Also tested the obvious defence of dropping the leading chunk, that it is the predecessor cue s audio: 0 of 17 leading spans lie inside any other resolved region, 0.0 percent on every one. The test finds overlaps where they exist (16 overlapping pairs among the regions, 60 exactly-adjacent boundaries, 73 of 78 bank-header regions starting where another ends), so the zero is not the instrument. Left open, with reach: the census covers movie-voice regions only, and the same stream carries the in-mission VOICE_D_* cues, which are not enumerated -- the leading bytes plausibly belong to one of those. Could not be settled by listening: no XMA1 decoder in this container, and sylpheed-cli audio info reports these chunks as 16 channels / 4310 Hz / 2-bit, which is visibly wrong. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd --- .../examples/voice_region_chunks.rs | 97 +++++++++++++++ docs/port/HANDOFF.md | 40 +++++++ docs/re/data/voice-region-chunk-census.txt | 103 ++++++++++++++++ .../structures/voice-region-leading-chunk.md | 110 ++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 crates/sylpheed-formats/examples/voice_region_chunks.rs create mode 100644 docs/re/data/voice-region-chunk-census.txt create mode 100644 docs/re/structures/voice-region-leading-chunk.md diff --git a/crates/sylpheed-formats/examples/voice_region_chunks.rs b/crates/sylpheed-formats/examples/voice_region_chunks.rs new file mode 100644 index 00000000..0d907922 --- /dev/null +++ b/crates/sylpheed-formats/examples/voice_region_chunks.rs @@ -0,0 +1,97 @@ +//! What ARE the chunks a movie-voice region decodes to? +//! +//! The port reports a resolved voice region decoding to **three** chunks — two +//! of equal duration each spanning the whole movie, and a leading one that +//! "matches nothing" — and notes that this is the same 2+1 signature the BGM +//! banks showed before `bank_header_len` attributed the extra to a bank header. +//! Two different asset kinds with one signature is worth checking, because if +//! the same explanation applies then `bank_header_len` is incomplete, and if it +//! does not then the leading chunk is something we are discarding. +//! +//! `slb.rs`'s own doc comment already predicts the answer and disagrees with +//! "drop it": the header signature fires on 28 entries, all music banks, with +//! "zero false positives on the 7 993 mid-bank windows, WHERE THE LEADING +//! REGION IS REAL". A voice region is a mid-bank window by construction — +//! `resolve_movie_voice_region` starts it at the PREDECESSOR cue's trailer. +//! +//! cargo run -p sylpheed-formats --example voice_region_chunks -- +use sylpheed_formats::media::{self, DirectorySource, DiscSource}; +use sylpheed_formats::slb::{self, VoiceLang}; + +fn main() { + let disc = std::env::args() + .nth(1) + .unwrap_or_else(|| std::env::var("SYLPHEED_DISC").expect("disc dir")); + let src = DirectorySource::new(std::path::PathBuf::from(&disc)); + + // Disc-wide, not the four movies that motivated the question: every movie + // the manifest binds a voice to. + let movies: Vec = { + use sylpheed_formats::movie_manifest; + let tpak = src.open_pak("dat/tables.pak").expect("tables.pak"); + let manifest = tpak + .entries() + .iter() + .find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) + .expect("movie manifest"); + movie_manifest::parse(&manifest) + .into_iter() + .map(|m| m.movie) + .collect() + }; + println!("{} movies in the manifest\n", movies.len()); + let mut census: std::collections::BTreeMap = Default::default(); + let (mut with_header, mut with_leading, mut no_leading) = (0, 0, 0); + for movie in movies.iter().map(|s| s.as_str()) { + let Some((start, end)) = media::resolve_movie_voice_region(&src, movie, VoiceLang::English) + else { + continue; + }; + let len = end - start; + let Ok(bytes) = src.read_segment_range("dat/sound", start, len as usize) else { + println!("{movie:8} region {start}..{end} unreadable"); + continue; + }; + // Where does the first RIFF sit? Everything before it is the leading + // headerless packet region. + let first_riff = bytes + .windows(4) + .position(|w| w == b"RIFF") + .map(|p| p as i64) + .unwrap_or(-1); + let hdr = slb::bank_header_len(&bytes); + let riffs = slb::to_xma_riffs(&bytes); + let kind = if first_riff <= 0 { + no_leading += 1; + "no leading region".to_string() + } else if hdr == Some(first_riff as usize) { + with_header += 1; + format!("BANK HEADER ({first_riff} B = {} packets exactly)", first_riff / 2048) + } else { + with_leading += 1; + *census.entry(first_riff as usize % 2048).or_default() += 1; + format!( + "leading STREAM ({first_riff} B = {} packets + {} B)", + first_riff / 2048, + first_riff % 2048 + ) + }; + println!( + "{movie:10} {start:12}..{end:12} {len:9} B chunks {} {kind}", + riffs.len() + ); + if let Ok(dir) = std::env::var("VOICE_CHUNK_DUMP") { + for (i, r) in riffs.iter().enumerate() { + let _ = std::fs::write(format!("{dir}/{movie}-chunk{i}.wav"), r); + } + } + } + println!( + "\n{with_header} region(s) open with a BANK HEADER (bank_header_len fires)\n\ +{with_leading} open with a leading STREAM\n{no_leading} start at a RIFF" + ); + println!("leading-stream length mod 2048, i.e. the derived data offset:"); + for (rem, n) in &census { + println!(" {rem:5} B x{n}"); + } +} diff --git a/docs/port/HANDOFF.md b/docs/port/HANDOFF.md index 7c399e6d..656f2658 100644 --- a/docs/port/HANDOFF.md +++ b/docs/port/HANDOFF.md @@ -341,6 +341,46 @@ to 771 479 px, max Δ 254). Where no control was available the page says so. [data](../re/data/paint-order-tie-pixel-cost.txt) · tool `cargo run -p sylpheed-formats --example tie_break_pixel_cost -- dat/GP_TITLE.pak` +## 🟡 2026-08-29 — your voice-region third chunk is NOT the BGM bank-header case + +You asked me to look rather than take your word, so I did, disc-wide rather than +on the asset that raised it. **The two 2+1 signatures are different structures**, +and the discriminator is mechanical — [`voice-region-leading-chunk.md`](../re/structures/voice-region-leading-chunk.md), +census at [`data/voice-region-chunk-census.txt`](../re/data/voice-region-chunk-census.txt). + +Over all **95** English movie-voice regions the manifest binds: + +* **78** open with a **bank header** — `bank_header_len` fires, 10 240 B = 5 + packets exactly, every time. That is the BGM case and it is already consumed. +* **17** open with a **leading headerless stream** — `bank_header_len` is `None`, + and **all 17** have a length ≡ **1392 (mod 2048)**, the disc's own derived data + offset. That is a whole number of XMA1 packets after a 1392-byte preamble. +* **0** begin at a `RIFF`. + +⚠️ **Counting chunks cannot tell you which case you are in.** Eight bank-header +regions *also* yield three chunks. Test `bank_header_len`, not `riffs.len()`. + +🔴 **And "it is the previous cue's audio" fails a test.** Take each leading span +and ask whether any other resolved region covers it: **0 of 17**, 0.0 % on every +one. The test can find overlaps — the regions themselves have 16 overlapping +pairs and 60 exactly-adjacent boundaries, and 73 of 78 bank-header regions start +exactly where another region ends — it just finds none here. + +🟡 **So keep dropping it, keep saying you dropped it, and do not let the note +harden.** It is not a header and not junk; it is undecoded audio nothing else +claims. ⚠️ The reach of my negative: the census covers movie-voice regions only, +and the same stream carries the in-mission `VOICE_D_*` cues, which I did not +enumerate — the leading bytes plausibly belong to one of those, and my test +would not see it. **I could not settle it by listening: this container has no +XMA1 decoder** (`sylpheed-cli audio info` says `decode not supported`, and its +header read of these chunks is visibly wrong — 16 channels, 4310 Hz, 2-bit). +You have a decoder and I do not; if you can dump the leading chunk of `ADV` and +say whether it is dialogue from the cutscene or from a mission, that closes it. + +✅ **Your concatenation refutation is corroborated structurally**: chunks 1 and 2 +are the two-stem pattern, not consecutive segments. Do not concatenate, for the +same reason `BGM` must not be. + ## Status | | Question | State | Answer / link | diff --git a/docs/re/data/voice-region-chunk-census.txt b/docs/re/data/voice-region-chunk-census.txt new file mode 100644 index 00000000..9a6f9c72 --- /dev/null +++ b/docs/re/data/voice-region-chunk-census.txt @@ -0,0 +1,103 @@ +104 movies in the manifest + +ADV 433930240.. 437044592 3114352 B chunks 3 leading STREAM (808304 B = 394 packets + 1392 B) +S00A 452798464.. 455499120 2700656 B chunks 3 leading STREAM (1324400 B = 646 packets + 1392 B) +S01A 456003584.. 460117360 4113776 B chunks 3 leading STREAM (1154416 B = 563 packets + 1392 B) +RT01A 437044592.. 437345648 301056 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT01B 437345648.. 437712240 366592 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT01C_1 437712240.. 438080880 368640 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT01C_2 438080880.. 438451568 370688 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S02A 460117360.. 461518192 1400832 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +S02B 462022656.. 463838576 1815920 B chunks 3 leading STREAM (431472 B = 210 packets + 1392 B) +S02C 464343040.. 469970288 5627248 B chunks 3 leading STREAM (1869168 B = 912 packets + 1392 B) +RT02A 438451568.. 438789488 337920 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT02B 438789488.. 439192944 403456 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT02C 439192944.. 439645552 452608 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT02D_1 439645552.. 439852400 206848 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT02D_2 439852400.. 440112496 260096 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s02A 430128496.. 430196080 67584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s02H 430335344.. 430390640 55296 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S03A 470474752.. 472020336 1545584 B chunks 3 leading STREAM (253296 B = 123 packets + 1392 B) +RT03A 440112496.. 440448368 335872 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT03B 440448368.. 440730992 282624 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT03C 440730992.. 441144688 413696 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT03D 441144688.. 441773424 628736 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s03A 430128496.. 430196080 67584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s03H 430335344.. 430390640 55296 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S04B 480507904.. 482938224 2430320 B chunks 3 leading STREAM (556400 B = 271 packets + 1392 B) +RT04A 441773424.. 442019184 245760 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT04B 442019184.. 442254704 235520 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s02A 430265712.. 430335344 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S05A 482938224.. 483433840 495616 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +RT05A 442254704.. 442578288 323584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT05B 442578288.. 442938736 360448 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT05C 442938736.. 443143536 204800 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s02A 430128496.. 430196080 67584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S06A 483938304.. 485457264 1518960 B chunks 3 leading STREAM (351600 B = 171 packets + 1392 B) +S06B 485961728.. 488908144 2946416 B chunks 3 leading STREAM (607600 B = 296 packets + 1392 B) +RT06A 443143536.. 443460976 317440 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT06B 443460976.. 443802992 342016 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT06C 443802992.. 444046704 243712 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT06D 444046704.. 444183920 137216 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s06A 430128496.. 430196080 67584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s06H 430335344.. 430390640 55296 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S07A 489412608.. 491443568 2030960 B chunks 3 leading STREAM (505200 B = 246 packets + 1392 B) +S07B 491443568.. 491634032 190464 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +RT07A 444183920.. 444706160 522240 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT07B 444706160.. 444980592 274432 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT07C 444980592.. 445130096 149504 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s07A 430265712.. 430335344 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s07H 430390640.. 430464368 73728 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT08A 445130096.. 445326704 196608 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT08B 445326704.. 445517168 190464 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT08C 445517168.. 445676912 159744 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s08A 430265712.. 430335344 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S09B 492138496.. 493743472 1604976 B chunks 3 leading STREAM (177520 B = 86 packets + 1392 B) +RT09A 445676912.. 446000496 323584 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT09B 446000496.. 446485872 485376 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT09C 446485872.. 446748016 262144 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT09D 446748016.. 447149424 401408 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s09A 430196080.. 430265712 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s09H 430335344.. 430390640 55296 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT10A 447149424.. 447272304 122880 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT10B 447272304.. 447356272 83968 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S11A 501900656.. 502173040 272384 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +S11C 502677504.. 505619824 2942320 B chunks 3 leading STREAM (742768 B = 362 packets + 1392 B) +RT11A 447356272.. 447776112 419840 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT11B 447776112.. 447880560 104448 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT11C 447880560.. 448097648 217088 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s11A 430196080.. 430265712 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S12A 505619824.. 506191216 571392 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +S12B 506191216.. 506262896 71680 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +S12C 506767360.. 512406896 5639536 B chunks 3 leading STREAM (1844592 B = 900 packets + 1392 B) +RT12A 448097648.. 448374128 276480 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT12B_1 448374128.. 448544112 169984 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT12B_2 448544112.. 448767344 223232 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s07A 430265712.. 430335344 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s07H 430390640.. 430464368 73728 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S13A 512911360.. 514874736 1963376 B chunks 3 leading STREAM (402800 B = 196 packets + 1392 B) +S13B 514874736.. 515278192 403456 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +RT13A 448767344.. 449002864 235520 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT13B_1 449002864.. 449099120 96256 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT13B_2 449099120.. 449340784 241664 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s13A 430265712.. 430335344 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S14A 515782656.. 521794928 6012272 B chunks 3 leading STREAM (1817968 B = 887 packets + 1392 B) +RT14A 449340784.. 449721712 380928 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT14B 449721712.. 450033008 311296 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT14C 450033008.. 450280816 247808 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_DS_s14H 430390640.. 430464368 73728 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +S15A 522299392.. 524309872 2010480 B chunks 3 leading STREAM (255344 B = 124 packets + 1392 B) +S15B 524309872.. 525626736 1316864 B chunks 3 BANK HEADER (10240 B = 5 packets exactly) +S15C 526131200.. 529565040 3433840 B chunks 3 leading STREAM (931184 B = 454 packets + 1392 B) +RT15A 450280816.. 450985328 704512 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT15B 450985328.. 451282288 296960 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +RT15C 451282288.. 451614064 331776 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s15A 430196080.. 430265712 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s24A 430196080.. 430265712 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) +hokyu_LS_s27A 430196080.. 430265712 69632 B chunks 1 BANK HEADER (10240 B = 5 packets exactly) + +78 region(s) open with a BANK HEADER (bank_header_len fires) +17 open with a leading STREAM +0 start at a RIFF +leading-stream length mod 2048, i.e. the derived data offset: + 1392 B x17 diff --git a/docs/re/structures/voice-region-leading-chunk.md b/docs/re/structures/voice-region-leading-chunk.md new file mode 100644 index 00000000..ac2ac42f --- /dev/null +++ b/docs/re/structures/voice-region-leading-chunk.md @@ -0,0 +1,110 @@ +# 🟡 A movie-voice region's THIRD chunk is not the bank-header case — and nothing else claims it + +**Status:** ✅ the *structure* is decoded, disc-wide, 95/95 regions. 🟡 what the +leading chunk **contains** is open, and this page states the reach of that +negative rather than guessing. + +Raised by the port: `media::sound_bank_riffs("BGM_103.slb")` used to return three +sub-waves where [`bgm-two-stems`](bgm-two-stems.md) says two, and +[`slb-bank-header-not-a-wave`](slb-bank-header-not-a-wave.md) attributed the +extra to the **bank header**. The port then hit *the same 2+1 signature on a +different asset kind* — a resolved movie-voice region also decoding to three +chunks — and asked whether one explanation covers both. + +**It does not.** They are two different structures, and the corpus's own code +already tells them apart; what it does not do is say which one it is looking at. + +Tool: `cargo run -p sylpheed-formats --example voice_region_chunks -- $SYLPHEED_DISC`. +Census committed at [`data/voice-region-chunk-census.txt`](../data/voice-region-chunk-census.txt). + +## ✅ Disc-wide: a voice region never begins at a `RIFF` + +All 95 English movie-voice regions the manifest binds: + +| how the region opens | regions | chunks it yields | +|---|---|---| +| a **bank header** — `bank_header_len` fires, **10 240 B = 5 packets exactly**, every time | **78** | 1 (×70) or 3 (×8) | +| a **leading headerless stream** — `bank_header_len` is `None` | **17** | **3, every time** | +| directly at a `RIFF` | **0** | — | + +And the leading streams are not ragged. **All 17 have a length ≡ 1392 (mod +2048)** — no other residue occurs — which is exactly `HEADERLESS_DATA_OFFSET`, +the `\etc\` data offset that [`slb-data-offset`](slb-data-offset.md) +derives. So a leading stream is `1392 B` of preamble followed by a whole number +of 2048-byte XMA1 packets: 394 of them on `ADV`, 646 on `S00A`, 900 on `S12C`. + +That is the discriminator the port needed, and it is mechanical: + +``` +bank_header_len(region) == Some(n) -> n is 10240, a header, already consumed +bank_header_len(region) == None -> first_riff % 2048 == 1392, a real stream +``` + +## 🔴 So the BGM explanation does not transfer + +`slb.rs`'s own doc comment predicted this and disagrees with "drop it": the +header signature fires on 28 `sound.pak` entries, all music banks, with *"zero +false positives on the 7 993 mid-bank windows, **where the leading region IS +real**"*. A movie-voice region is a mid-bank window by construction — +`resolve_movie_voice_region` anchors its start at the **predecessor cue's +trailer**, deliberately, because the cue may sit either side of its own `.slb` +chunk. + +⚠️ **The 3-chunk count is not evidence of the leading region at all.** Eight +regions open with a bank header *and still yield three chunks* (`S11A`, `S12A`, +`S12B`, `S13B`, `S15B`, …). Counting chunks cannot distinguish the two cases; +only `bank_header_len` can. + +## 🔴 "It is the previous cue's audio, so dropping it is right" — TESTED, and it fails + +The obvious defence of dropping the leading chunk is that the region starts at +the predecessor's trailer, so those bytes are the previous line of dialogue. +That is checkable without decoding anything: take each leading span +`[start, start + first_riff)` and ask whether any *other* resolved region covers +it. + +| | | +|---|---| +| leading spans lying wholly or partly inside another movie-voice region | **0 of 17** | +| …expressed as covered fraction | **0.0 % on every one** | + +For contrast, the regions themselves are not disjoint — 16 overlapping pairs, 60 +exactly-adjacent boundaries, 18 gaps — so the test is capable of finding an +overlap, and it finds none here. **73 of 78** bank-header regions start exactly +where another region ends; **0 of 17** leading-stream regions do. + +So the leading chunk is not another *movie's* voice. + +## 🟡 What it is, is open — and here is the reach + +What is established: the leading chunk is a whole number of XMA1 packets at the +disc's own derived data offset, inside this movie's region, claimed by no other +movie-voice region. What is **not** established is what it sounds like. + +The reach of the negative, stated plainly: + +* The census enumerates the **95 movie-voice regions the manifest binds in + English**. The same stream also carries the in-mission voice cues + (`VOICE_D_*`), which are *not* enumerated here. The leading bytes could belong + to one of those, and this test would not see it. **That is the leading + hypothesis and it is untested.** +* 🔴 **It could not be settled by listening in this container.** There is no XMA1 + decoder here — `sylpheed-cli audio info` reports `decode not supported (needs + an XMA2 decoder + the sound-bank descriptor)`, and its header read of these + chunks is visibly wrong (16 channels, 4310 Hz, 2-bit depth), so it cannot even + be used for durations. Settling this needs a decoder run, which the port has + and this container does not. + +## What a consumer should do meanwhile + +🟡 Dropping the leading chunk is **defensible and should stay labelled**, which is +what the port already does. It is not junk and it is not a header — it is +undecoded audio — so the manifest note must not harden into "the bank had a +spurious chunk". If it turns out to be an in-mission line, dropping it is +correct; if it turns out to be part of the cutscene, it is a truncation. + +⚠️ **Do not "fix" it by concatenating.** The port measured a concatenated region +at 359 s against a 137 s movie, and chunks 1 and 2 are the two-stem pattern +[`bgm-two-stems`](bgm-two-stems.md) documents for music — equal duration, played +together, not in sequence. Concatenation is wrong here for the same reason it is +wrong there.