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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
97
crates/sylpheed-formats/examples/voice_region_chunks.rs
Normal file
97
crates/sylpheed-formats/examples/voice_region_chunks.rs
Normal file
@@ -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 -- <disc-dir>
|
||||
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<String> = {
|
||||
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<usize, usize> = 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}");
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
103
docs/re/data/voice-region-chunk-census.txt
Normal file
103
docs/re/data/voice-region-chunk-census.txt
Normal file
@@ -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
|
||||
110
docs/re/structures/voice-region-leading-chunk.md
Normal file
110
docs/re/structures/voice-region-leading-chunk.md
Normal file
@@ -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 `<lang>\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.
|
||||
Reference in New Issue
Block a user