re: the voice region s leading chunk is the movie s OWN dialogue, and a guard puts it there

My own leading hypothesis -- that the leading chunk is an in-mission VOICE_D_*
line -- is refuted, on the route the port suggested: widen the enumeration past
the 95 manifest-bound movies and the byte-span test settles it without anyone
listening.

Scanning the stream for every trailer descriptor (the (id, 0x11) pair whose id
repeats at +0x800) gives the complete cue partition, mission lines included:
287 descriptors in a 116.2 MB window, all 287 carrying an id the 4280-name
registry names. Every one of the 17 leading spans is bracketed by
desc(N-1)..desc(N) where desc(N) is that movie s OWN cue id. Zero mission lines.

The mechanism is a guard in our own resolver. resolve_movie_voice_region takes
the predecessor trailer as the region start, guards it with
end - start < 1_500_000, and falls back to the .slb TOC anchor when that fails.
Cues with a true span over the guard: 17, of which 17 are stream-opening. Cues
under it: 78, of which 0. Perfect discrimination both ways. The anchor sits a
constant 504464 B after the true predecessor trailer on all 17, which is
unexplained.

Not established, and stated as such: this does NOT mean the export truncates N
seconds. The port s decode already has ADV s region at 359 s against a 137 s
movie, so it over-covers and the byte-to-time mapping is not linear. No XMA1
decoder in this container to check.

Also withdraws a claim this page had adopted from the port -- that chunks 1 and
2 are two stems of one performance. The port refuted its own claim by decoding:
S00A chunk 2 is digital silence, ADV chunk 2 is 0.60x chunk 1 with the residual
26.8 dB down. Equal duration was a shape match and Q10 s music census should not
have been carried across to voice on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
sylph-decoder
2026-08-29 15:03:58 +00:00
parent de2fe4a110
commit 432fb7450b
4 changed files with 361 additions and 39 deletions

View File

@@ -366,20 +366,48 @@ 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.
**RESOLVED, same day, by your own suggestion — and my leading hypothesis was
wrong.** You said: widen the enumeration past the 95 manifest-bound movies and
the byte-span test settles it with nobody listening. It does. Scanning the stream
for **every** trailer descriptor (287 found in a 116.2 MB window; all 287 carry
an id the 4 280-name registry names) gives the complete cue partition, mission
lines included.
**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.
🔴 **The leading chunk is the MOVIE'S OWN dialogue — 17 of 17.** Every leading
span is bracketed by `desc(N-1) .. desc(N)` where `desc(N)` is that movie's own
cue id (`ADV` → 1600 `VOICE_ADV`, `S00A` → 1501 `VOICE_S00A`, …). **Zero** are
in-mission `VOICE_D_*` lines. So "drop it, it is somebody else's audio" is dead.
**And the mechanism is a guard in our own resolver.**
`resolve_movie_voice_region` takes the predecessor trailer as the region start,
guards it with `end - start < 1_500_000`, and falls back to the `.slb` anchor
when that fails. Cues with a true span ≥ 1.5 MB: **17, of which 17 are
stream-opening.** Cues under it: **78, of which 0 are.** Perfect discrimination
both ways. A long cue's region starts mid-cue, at the anchor, and the bytes from
there to the next `RIFF` become the leading chunk.
⚠️ **But do NOT turn that into "the export truncates N seconds".** Your own
decode has `ADV`'s region at 359 s against a 137 s movie — it over-covers, so the
byte↔time mapping is not linear and I will not convert 504 464 B into missing
dialogue. I have no XMA1 decoder here to check.
**What is still open is narrower and better posed**: not *whose audio is this*
(answered — the movie's own), but *why one cue's byte span decodes to ~2.6× the
movie*, and what `ADV` chunk 2 (0.60 × chunk 1) is. Both are mine.
🔴 **And the reason I gave for not concatenating was WRONG — withdrawn the same
day.** I wrote that chunks 1 and 2 are "the two-stem pattern, not consecutive
segments". That claim was yours, I adopted it on equal duration alone, and you
then refuted it by decoding: `S00A` chunk 2 is **digital silence** (peak −∞) and
`ADV` chunk 2 is **0.60 × chunk 1**, residual 26.8 dB down. Equal duration was a
shape match, and Q10's *music* census should not have been carried across to
voice on it. ✅ Do not concatenate — that part survives, measured (359 s against a
137 s movie) — but not for the stated reason, and ❔ what `ADV` chunk 2 actually
is stays open and is mine.
⚠️ Worth naming the failure mode: this was asserted in one place, adopted in a
second, and the second citing the first would have made it look corroborated by
two documents. It was caught only because you measured your own claim.
## Status

View File

@@ -0,0 +1,50 @@
registry: 4280 cue names, 4280 distinct ids
scanning dat/sound 421739888..537953648 (116.2 MB)
287 trailer descriptors found
of those, 287 carry an id the registry names, 0 do not
leading span -> owning cue
ADV lead 808304 B bracketed by desc@Some((433425776, 1528)) .. desc@Some((437044592, 1600)) owner VOICE_ADV [movie cue]
S00A lead 1324400 B bracketed by desc@Some((452294000, 1500)) .. desc@Some((455499120, 1501)) owner VOICE_S00A [movie cue]
S01A lead 1154416 B bracketed by desc@Some((455499120, 1501)) .. desc@Some((460117360, 1502)) owner VOICE_S01A [movie cue]
S02B lead 431472 B bracketed by desc@Some((461518192, 1503)) .. desc@Some((463838576, 1504)) owner VOICE_S02B [movie cue]
S02C lead 1869168 B bracketed by desc@Some((463838576, 1504)) .. desc@Some((469970288, 1506)) owner VOICE_S02C [movie cue]
S03A lead 253296 B bracketed by desc@Some((469970288, 1506)) .. desc@Some((472020336, 1507)) owner VOICE_S03A [movie cue]
S04B lead 556400 B bracketed by desc@Some((480003440, 1508)) .. desc@Some((482938224, 1509)) owner VOICE_S04B [movie cue]
S06A lead 351600 B bracketed by desc@Some((483433840, 1510)) .. desc@Some((485457264, 1511)) owner VOICE_S06A [movie cue]
S06B lead 607600 B bracketed by desc@Some((485457264, 1511)) .. desc@Some((488908144, 1512)) owner VOICE_S06B [movie cue]
S07A lead 505200 B bracketed by desc@Some((488908144, 1512)) .. desc@Some((491443568, 1513)) owner VOICE_S07A [movie cue]
S09B lead 177520 B bracketed by desc@Some((491634032, 1514)) .. desc@Some((493743472, 1515)) owner VOICE_S09B [movie cue]
S11C lead 742768 B bracketed by desc@Some((502173040, 1517)) .. desc@Some((505619824, 1518)) owner VOICE_S11C [movie cue]
S12C lead 1844592 B bracketed by desc@Some((506262896, 1520)) .. desc@Some((512406896, 1521)) owner VOICE_S12C [movie cue]
S13A lead 402800 B bracketed by desc@Some((512406896, 1521)) .. desc@Some((514874736, 1522)) owner VOICE_S13A [movie cue]
S14A lead 1817968 B bracketed by desc@Some((515278192, 1523)) .. desc@Some((521794928, 1524)) owner VOICE_S14A [movie cue]
S15A lead 255344 B bracketed by desc@Some((521794928, 1524)) .. desc@Some((524309872, 1525)) owner VOICE_S15A [movie cue]
S15C lead 931184 B bracketed by desc@Some((525626736, 1526)) .. desc@Some((529565040, 1527)) owner VOICE_S15C [movie cue]
verdicts: {"movie cue": 17}
cue span vs the 1.5 MB guard, and what the region actually starts at:
ADV true cue span 3618816 B (> guard: true) region starts at 433930240, true start 433425776 -> 504464 B of the cue's own audio is OUTSIDE the region
S00A true cue span 3205120 B (> guard: true) region starts at 452798464, true start 452294000 -> 504464 B of the cue's own audio is OUTSIDE the region
S01A true cue span 4618240 B (> guard: true) region starts at 456003584, true start 455499120 -> 504464 B of the cue's own audio is OUTSIDE the region
S02B true cue span 2320384 B (> guard: true) region starts at 462022656, true start 461518192 -> 504464 B of the cue's own audio is OUTSIDE the region
S02C true cue span 6131712 B (> guard: true) region starts at 464343040, true start 463838576 -> 504464 B of the cue's own audio is OUTSIDE the region
S03A true cue span 2050048 B (> guard: true) region starts at 470474752, true start 469970288 -> 504464 B of the cue's own audio is OUTSIDE the region
S04B true cue span 2934784 B (> guard: true) region starts at 480507904, true start 480003440 -> 504464 B of the cue's own audio is OUTSIDE the region
S06A true cue span 2023424 B (> guard: true) region starts at 483938304, true start 483433840 -> 504464 B of the cue's own audio is OUTSIDE the region
S06B true cue span 3450880 B (> guard: true) region starts at 485961728, true start 485457264 -> 504464 B of the cue's own audio is OUTSIDE the region
S07A true cue span 2535424 B (> guard: true) region starts at 489412608, true start 488908144 -> 504464 B of the cue's own audio is OUTSIDE the region
S09B true cue span 2109440 B (> guard: true) region starts at 492138496, true start 491634032 -> 504464 B of the cue's own audio is OUTSIDE the region
S11C true cue span 3446784 B (> guard: true) region starts at 502677504, true start 502173040 -> 504464 B of the cue's own audio is OUTSIDE the region
S12C true cue span 6144000 B (> guard: true) region starts at 506767360, true start 506262896 -> 504464 B of the cue's own audio is OUTSIDE the region
S13A true cue span 2467840 B (> guard: true) region starts at 512911360, true start 512406896 -> 504464 B of the cue's own audio is OUTSIDE the region
S14A true cue span 6516736 B (> guard: true) region starts at 515782656, true start 515278192 -> 504464 B of the cue's own audio is OUTSIDE the region
S15A true cue span 2514944 B (> guard: true) region starts at 522299392, true start 521794928 -> 504464 B of the cue's own audio is OUTSIDE the region
S15C true cue span 3938304 B (> guard: true) region starts at 526131200, true start 525626736 -> 504464 B of the cue's own audio is OUTSIDE the region
cues over the 1.5 MB guard: 17, of which stream-opening: 17
cues under the guard: 78, of which stream-opening: 0

View File

@@ -2,7 +2,9 @@
**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.
negative rather than guessing. 🔴 One claim this page carried — that chunks 1 and
2 are two stems of one performance — is **withdrawn**; see
[the bottom of the page](#what-a-consumer-should-do-meanwhile).
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
@@ -75,36 +77,107 @@ 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
## ✅ RESOLVED 2026-08-29 — the leading chunk is the MOVIE'S OWN cue, and the mechanism is a guard
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 section that stood here left this open and named an in-mission `VOICE_D_*`
line as the leading hypothesis. **That hypothesis is refuted.** The port pointed
out that the byte-span test already built settles it without anyone listening, if
the enumeration is widened past the 95 manifest-bound movies — and it does.
The reach of the negative, stated plainly:
Rather than resolving cues one at a time, scan the stream for **every** trailer
descriptor: the `(id: u32be, 0x11, …)` pair whose id repeats at `+0x800`, which
[`movie_voice`](../../crates/sylpheed-formats/src/movie_voice.rs) documents as
the end of a cue's audio, with a false-match probability of ~2⁻⁶⁴. The full
descriptor list **is** the stream's complete cue partition, movie and mission
alike. Over a 116.2 MB window covering every region: **287 descriptors, and all
287 carry an id the registry names** (4 280 cue names).
* 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.
Tool: `cargo run -p sylpheed-formats --example voice_stream_cue_map -- $SYLPHEED_DISC`,
output at [`data/voice-stream-cue-map.txt`](../data/voice-stream-cue-map.txt).
### The leading span belongs to the movie itself — 17 of 17
Each leading span is bracketed by `desc(N-1) .. desc(N)`, and in every case
`desc(N)` is **that movie's own cue id**:
| movie | leading span ends at descriptor | |
|---|---|---|
| `ADV` | id 1600 = `VOICE_ADV` | movie cue |
| `S00A` | 1501 = `VOICE_S00A` | movie cue |
| `S14A` | 1524 = `VOICE_S14A` | movie cue |
| …all 17 | | **movie cue, 0 mission lines** |
By the stream's own rule — cue N's audio is `[desc(N-1) .. desc(N)]` — those
bytes are **this movie's dialogue**. 🔴 So "it is an in-mission `VOICE_D_*` line"
is dead, and so is any reading in which the leading chunk is foreign audio.
### ✅ And the mechanism is `resolve_movie_voice_region`'s own guard
`resolve_movie_voice_region` takes the predecessor trailer as the region start,
but guards it with `end - start < 1_500_000` and falls back to the `.slb` TOC
**anchor** when that fails. If the guard is the cause, the stream-opening regions
should be exactly the cues whose true span exceeds it:
| | cues | of which stream-opening |
|---|---|---|
| true cue span **≥ 1.5 MB** | **17** | **17** |
| true cue span **< 1.5 MB** | **78** | **0** |
**Perfect discrimination, both ways.** A long cue's region does not start at its
cue boundary; it starts at the anchor, mid-cue, and everything from the anchor to
the next `.slb` `RIFF` becomes the leading chunk. That is the whole phenomenon.
⚠️ **The anchor sits a constant `504 464 B` after the true predecessor trailer on
all 17** — not an approximate constant, the same number every time. That
regularity is unexplained and is worth someone's attention; it says the `.slb`
chunk boundary is placed at a fixed distance from a trailer.
### ⚠️ What this does NOT establish — do not convert bytes into missing seconds
It is tempting to read "504 464 B of the cue's own audio lies outside the region"
as *the export truncates 246 packets of dialogue*. **Do not.** The port's decode
of `ADV`'s region already yields **359 s against a 137 s movie**, so the region
over-covers rather than under-covers, and the byte↔time mapping is plainly not
linear — consistent with more than one sub-stream being interleaved. Bytes are
what was measured here; seconds are not, and this container has no XMA1 decoder
to get them.
**So what remains open is narrower and better posed than before:** not *whose
audio is this* (answered: the movie's own), but *why one cue's byte span decodes
to ~2.6× the movie's duration*, and what `ADV` chunk 2 — a 0.60 × scaled copy of
chunk 1 — is doing in it.
## 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.
🔴 **Dropping the leading chunk is dropping the cutscene's own dialogue** — that
is now measured, not suspected, and the manifest note must not say or imply that
the bank had a spurious chunk. ⚠️ It does **not** follow that simply including it
is right: the region already decodes to ~2.6× the movie's length, so inclusion is
a decoding question that is still open, and this page has moved it rather than
closed it. What is settled is the *provenance* of those bytes.
⚠️ **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.
at 359 s against a 137 s movie.
🔴 **But the REASON this page gave was wrong, and is withdrawn (2026-08-29).**
It said chunks 1 and 2 are "the two-stem pattern [`bgm-two-stems`](bgm-two-stems.md)
documents for music — equal duration, played together". That claim originated
with the port, I adopted it here on the strength of equal duration, and the port
then refuted its own claim by decoding the content:
* **`S00A` chunk 2 is digital silence** — 4 497 300 samples, peak −∞. Not a quiet
stem. Nothing at all.
* **`ADV` chunk 2 is `0.60 ×` chunk 1** — best-fit scalar, residual **26.8 dB
below** the target. ~95 % of its energy is a 4.4 dB copy of chunk 1, not an
independent performance.
Equal duration was a *shape* match and Q10's music census should not have been
carried across to voice on it. ⚠️ **This is how a wrong belief hardens**: it was
asserted in one place, adopted in a second, and the second citing the first would
have made it look corroborated. It was caught because the port measured its own
claim rather than the other agent's.
**What `ADV`'s near-duplicate chunk 2 is remains open** — a decoding question,
not a port one. What is *not* open is that summing a digitally silent chunk at
`1/n` costs 6.02 dB for nothing; the port drops silent chunks before summing,
which is arithmetic rather than a content judgement.