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

@@ -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.