port: a voice region is three presentations of one take -- keep one stream, sum nothing
Third reading of the same bytes in one session, and the first one that is
decoded rather than inferred.
1. concatenate -> 359 s of dialogue for a 137 s movie. Dead on measurement.
2. sum as Q10's two stems -> refuted here: S00A's second full-length chunk is
DIGITAL SILENCE and ADV's is 0.60x the first with 26.8 dB of residual. That
claim was mine, and the Decoder had already adopted it before I tested it;
it is withdrawn in both places.
3. keep ONE stream. Decoded disc-wide by the Decoder, counting stream starts
inside every inter-descriptor span: 258 spans hold one stream, 28 hold
three, nothing holds two. So 359 = 84.55 + 137.32 + 137.32.
Summing was therefore wrong a third time, and for a third reason: a take plus a
0.60x copy of itself is ~4 dB louder and coloured, not a mix of parts. The filter
is now `[0:a]anull` plus the mono fold -- no gain applied at all.
`check` moves `voice` off the strict peak bound as a consequence. It sat with
`bgm` because it was a sum this exporter produced; it is now a single wave off
the disc, mastered near full scale -- ADV's louder presentation measures +0.0003
dBFS at source -- so refusing that would be refusing the disc's own mastering.
The leading chunk is dropped as a DUPLICATE, not a truncation. It is this
movie's own dialogue (the Decoder, 17 of 17) and I measured it to be the TAIL of
the kept stream: sliding envelope correlation r=0.998 / 0.932 with the lag flush
against that stream's end, controls 1.000 self and 0.289 for a different movie,
confirmed in the sample domain at 16.7 / 23.2 dB of residual.
STILL OPEN, and flagged rather than absorbed: WHICH presentation to keep. Highest
byte rate is the Decoder's recommendation and not a decoded field, and on ADV it
selects the QUIETER of two (-8.3 dBFS against 0.0). Said in the manifest with the
consequence, so it is visible and reversible. A capture of the intro with
dialogue audible settles it.
Not converted, and the Decoder has since withdrawn the ask with a better reason
than mine: its 504464 B anchor constant is structural, not proportional --
identical on all 17 regions, and a proportional prediction lands within 8 bytes
on ADV while being 4305 B out on S00A.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -463,16 +463,15 @@ pub fn export_bgm<S: DiscSource + ?Sized>(
|
||||
///
|
||||
/// ## Three choices made here, and the reason each is not a guess
|
||||
///
|
||||
/// * **One file per movie** (MODDING rule 1), and the region's `RIFF`s are
|
||||
/// **summed, not concatenated** — the reverse of what this function did when
|
||||
/// it was first written an hour earlier. Concatenating produced a 359 s voice
|
||||
/// track for a 137 s movie.
|
||||
/// * **One file per movie** (MODDING rule 1), and **exactly one region chunk is
|
||||
/// kept**. Not concatenated, not summed. This function got that wrong twice
|
||||
/// before it got it right, and the history is kept below because each wrong
|
||||
/// reading was ended by a measurement, not by an argument.
|
||||
///
|
||||
/// ## The chunks are stems, and only a measurement showed it
|
||||
/// ## A region holds THREE PRESENTATIONS OF ONE TAKE — decoded, and not by me
|
||||
///
|
||||
/// A resolved region decodes to several chunks, and the two readings — segments
|
||||
/// to join end to end, or stems to mix — look identical in the bytes. Decoded
|
||||
/// and timed against the movies' own lengths:
|
||||
/// A resolved region decodes to several chunks. Decoded and timed against the
|
||||
/// movies' own lengths:
|
||||
///
|
||||
/// | movie | movie | chunk 0 | chunk 1 | chunk 2 |
|
||||
/// |---|---|---|---|---|
|
||||
@@ -480,29 +479,50 @@ pub fn export_bgm<S: DiscSource + ?Sized>(
|
||||
/// | `S00A` | 93.779 s | 68.072 | **93.694** | **93.694** |
|
||||
/// | `RT01A` | — | 0.009 | **34.034** | — |
|
||||
///
|
||||
/// Chunks 1 and 2 are **equal to six decimals and each span the whole movie**.
|
||||
/// That is exactly HANDOFF Q10's decoded shape — *a bank is two stems of one
|
||||
/// performance, played together; do not concatenate* — arriving on a different
|
||||
/// asset kind, which is why they are summed at `1/n` like [`export_bgm`]'s.
|
||||
/// **Reading 1, concatenate:** 359 s of dialogue for a 137 s movie. Dead.
|
||||
///
|
||||
/// ⚠️ **Chunk 0 is dropped, and dropping it may be a TRUNCATION.** This comment
|
||||
/// first guessed it was the same thing as `BGM_103`'s third sub-wave — a bank
|
||||
/// header — and a disc-wide census over all 95 English movie-voice regions
|
||||
/// showed that is a *different structure*: 78 regions open with a 10 240 B bank
|
||||
/// header, 17 with a leading headerless stream at the disc's own `1392 mod 2048`
|
||||
/// data offset, and the chunk count discriminates neither. Then the byte-span
|
||||
/// test settled what it holds: **the leading chunk is this movie's own dialogue,
|
||||
/// 17 of 17** — not an in-mission line, which was the standing hypothesis.
|
||||
/// **Reading 2, sum them as HANDOFF Q10's two stems** — equal duration, each
|
||||
/// spanning the movie, which is exactly Q10's *music* shape. ❌ **Refuted here,
|
||||
/// and the claim had already been adopted into the RE corpus before I tested
|
||||
/// it**: `S00A`'s second full-length chunk is **digital silence** (peak −inf)
|
||||
/// and `ADV`'s is **0.60 × the first** with 26.8 dB of residual. Equal duration
|
||||
/// was a shape match and carrying a music census across on it was my error.
|
||||
///
|
||||
/// It is dropped anyway, and only for this reason: **the region over-covers.**
|
||||
/// Taking everything measures 2.6× the movie's length, which nothing explains
|
||||
/// yet. So the omission is a *bounded* choice, not junk removal, and the
|
||||
/// manifest says so in those words — a consumer must not read a dropped chunk
|
||||
/// here as a defect the exporter cleaned up.
|
||||
/// **Reading 3, one stream. ✅ Decoded disc-wide by the Decoder**, by counting
|
||||
/// stream starts inside every inter-descriptor span: **258 spans hold one
|
||||
/// stream, 28 hold three, and nothing holds two or any other number.** The 95
|
||||
/// movie-voice regions decompose 70 + 8 + 17. So a region is three presentations
|
||||
/// of one take, and `359 = 84.55 + 137.32 + 137.32`. Summing a take with a
|
||||
/// scaled copy of itself adds ~4 dB and colours it.
|
||||
///
|
||||
/// The selection rule is therefore stated in terms of what was measured — *keep
|
||||
/// the longest duration and everything tying with it, minus anything digitally
|
||||
/// silent* — and every dropped chunk is named with its length and peak.
|
||||
/// 🟡 **Which presentation to keep is a recommendation, not a field.** The
|
||||
/// selector is the **highest byte rate** among the equal-duration survivors, on
|
||||
/// the Decoder's advice. Nothing on the disc says which one the game plays, and
|
||||
/// on `ADV` this picks the **quieter** of the two — −8.3 dBFS against 0.0. It is
|
||||
/// stated in the manifest with that consequence so the choice is reversible; a
|
||||
/// capture of the intro with dialogue audible settles it.
|
||||
///
|
||||
/// ## Chunk 0 is dropped, and it is a DUPLICATE rather than a truncation
|
||||
///
|
||||
/// This comment first guessed it was `BGM_103`'s third sub-wave — a bank header
|
||||
/// — and a census over all 95 regions showed that is a *different structure*:
|
||||
/// 78 open with a 10 240 B bank header, 17 with a leading headerless stream at
|
||||
/// the disc's own `1392 mod 2048` data offset, and **the chunk count
|
||||
/// discriminates neither**. The byte-span test then found it is **this movie's
|
||||
/// own dialogue, 17 of 17** — not an in-mission line, which was the standing
|
||||
/// hypothesis.
|
||||
///
|
||||
/// Which raised the real question: is dropping it a truncation? **No.** Measured
|
||||
/// here with a decoder the RE container does not have — sliding envelope
|
||||
/// correlation, overhang allowed, normalised over the overlap: **r = 0.998**
|
||||
/// (`ADV`) and **0.932** (`S00A`), against controls of 1.000 (self) and 0.289 (a
|
||||
/// different movie), with both lags placing chunk 0 **flush against the end** of
|
||||
/// the kept stream. Confirmed in the sample domain at 16.7 / 23.2 dB of
|
||||
/// residual. It is the tail of the take, presented again.
|
||||
///
|
||||
/// So the selection rule is stated in terms of what was measured — *the longest
|
||||
/// duration, ties broken by byte rate, minus anything digitally silent* — and
|
||||
/// every dropped chunk is named in the manifest with its length and peak.
|
||||
/// * **Mono**, with the fold chosen from the stream's own declared channel
|
||||
/// count rather than by passing `-ac 1` and hoping. A voice track that is
|
||||
/// already mono is passed through untouched.
|
||||
@@ -559,9 +579,31 @@ pub fn export_voice<S: DiscSource + ?Sized>(
|
||||
// A tie at 1 ms. The two stems agree to six decimals and the chunk that is
|
||||
// not one of them misses by tens of seconds, so nothing sits near this
|
||||
// bound: it separates the measured cases without being a tuned threshold.
|
||||
let keep: Vec<usize> = (0..all.len())
|
||||
// ONE STREAM, NOT A SUM -- and this is the third reading of these chunks, each
|
||||
// one refuted by a measurement rather than by an argument.
|
||||
//
|
||||
// They were concatenated (359 s for a 137 s movie), then summed as HANDOFF
|
||||
// Q10's two stems (refuted here: `S00A`'s second is silence, `ADV`'s is
|
||||
// 0.60x the first). The Decoder then decoded the shape disc-wide -- counting
|
||||
// stream starts inside every inter-descriptor span gives 258 spans with ONE
|
||||
// stream and 28 with THREE, and nothing with two or any other number, so a
|
||||
// region carries **three presentations of one take**, not a mix. Summing a
|
||||
// take with a scaled copy of itself adds ~4 dB and colours it.
|
||||
//
|
||||
// The selector is the **highest byte rate** among the equal-duration
|
||||
// survivors, on the Decoder's recommendation. 🟡 That is a recommendation and
|
||||
// not a decoded field: no flag on the disc says which presentation the game
|
||||
// plays, and on `ADV` it picks the quieter of the two (-8.3 dBFS against
|
||||
// 0.0). Recorded in the manifest so the choice is visible and reversible.
|
||||
let tied: Vec<usize> = (0..all.len())
|
||||
.filter(|&i| !silent.contains(&i) && (longest - lengths[i]).abs() < 0.001)
|
||||
.collect();
|
||||
let keep: Vec<usize> = tied
|
||||
.iter()
|
||||
.copied()
|
||||
.max_by_key(|&i| riffs[i].len())
|
||||
.into_iter()
|
||||
.collect();
|
||||
let dropped: Vec<String> = (0..all.len())
|
||||
.filter(|i| !keep.contains(i))
|
||||
.map(|i| {
|
||||
@@ -619,16 +661,9 @@ pub fn export_voice<S: DiscSource + ?Sized>(
|
||||
argv.push("-i".into());
|
||||
argv.push(s.display().to_string());
|
||||
}
|
||||
// SUMMED at 1/n, with the coefficient written out rather than left to
|
||||
// `amix`'s `normalize=1` default, so it appears in the manifest's command
|
||||
// line. Same reasoning as `export_bgm`: a default is a decision nobody made
|
||||
// and it can move under an ffmpeg upgrade.
|
||||
let filter = format!(
|
||||
"{}amix=inputs={n}:normalize=0,volume={:.6}{fold}[a]",
|
||||
(0..staged.len()).map(|i| format!("[{i}:a]")).collect::<String>(),
|
||||
1.0 / staged.len() as f64,
|
||||
n = staged.len(),
|
||||
);
|
||||
// One input, so no mix and no normalising coefficient: the stream reaches the
|
||||
// Ogg at the level the disc has it, and the only filter is the mono fold.
|
||||
let filter = format!("[0:a]anull{fold}[a]");
|
||||
argv.push("-filter_complex".into());
|
||||
argv.push(filter);
|
||||
argv.push("-map".into());
|
||||
@@ -665,31 +700,39 @@ pub fn export_voice<S: DiscSource + ?Sized>(
|
||||
movie -> token -> sound id -> byte region [{start}, {end}) of the continuous \
|
||||
voice stream. NOT matched by filename: RT01A's voice lives inside \
|
||||
VOICE_ADV.slb, so the name is right for this movie by luck and wrong for \
|
||||
others. Of {} region chunk(s), {} were SUMMED at 1/{} -- they are \
|
||||
equal-duration and each spans the whole movie, which is HANDOFF Q10's decoded \
|
||||
two-stem shape, so joining them end to end would play the dialogue twice.{} \
|
||||
Folded to mono from the {} of {channels} declared channel(s) that carry \
|
||||
signal -- averaging a silent channel in would cost 6.02 dB, and channel 2 of \
|
||||
both voice streams IS silent.{against}",
|
||||
live.len(),
|
||||
others. Of {} region chunk(s), exactly ONE is kept -- not summed. A region \
|
||||
carries THREE PRESENTATIONS OF ONE TAKE, decoded disc-wide by counting stream \
|
||||
starts inside every inter-descriptor span: 258 spans hold one stream and 28 hold \
|
||||
three, and nothing holds two. This exporter read these chunks wrongly twice \
|
||||
before that landed -- first concatenating them (359 s for a 137 s movie), then \
|
||||
summing them as HANDOFF Q10's two stems, which its own measurements refuted: \
|
||||
S00A's second full-length chunk is DIGITAL SILENCE and ADV's is 0.60x the first \
|
||||
with 26.8 dB of residual. Summing a take with a scaled copy of itself adds ~4 dB \
|
||||
and colours it. 🟡 The kept stream is the HIGHEST BYTE RATE among the \
|
||||
equal-duration survivors, which is a recommendation and NOT a decoded field: \
|
||||
nothing on the disc says which presentation the game plays, and on ADV this \
|
||||
picks the quieter of two.{} Folded to mono from the {} of {channels} declared \
|
||||
channel(s) that carry signal -- channel 2 of both voice streams is digitally \
|
||||
silent, and averaging it in cost 5.94 dB until this was measured rather than \
|
||||
read off the declared count.{against}",
|
||||
riffs.len(),
|
||||
staged.len(),
|
||||
staged.len(),
|
||||
if dropped.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(
|
||||
" DROPPED, and NOT because it is spurious -- the leading chunk is DECODED \
|
||||
to be this movie's OWN dialogue, 17 of 17 regions (the Decoder's \
|
||||
" DROPPED, and NOT as junk -- the leading chunk is DECODED to be this \
|
||||
movie's OWN dialogue, 17 of 17 regions (docs/re/structures/\
|
||||
voice-region-leading-chunk.md; an earlier note here wrongly equated it \
|
||||
with BGM_103's third sub-wave, which a disc-wide census showed is a \
|
||||
different structure). It is dropped because the region OVER-COVERS: \
|
||||
including everything measures 2.6x the movie's length. So this may be a \
|
||||
TRUNCATION, it is an open decoding question, and a consumer must not read \
|
||||
the omission as junk removal. See docs/port/BLOCKED.md: {}.",
|
||||
different structure). This port then measured it to be the TAIL of the \
|
||||
kept stream -- sliding envelope correlation r=0.998 (ADV) and 0.932 \
|
||||
(S00A), the lag placing it flush against that stream's end, against \
|
||||
controls of 1.000 self and 0.289 for a different movie -- so dropping it \
|
||||
removes a DUPLICATE, not dialogue. Dropped: {}.",
|
||||
dropped.join(", ")
|
||||
)
|
||||
}
|
||||
},
|
||||
live.len()
|
||||
),
|
||||
peak_dbfs: peak,
|
||||
duration_s: dur,
|
||||
|
||||
@@ -340,13 +340,18 @@ fn check_audio(root: &Path, m: &Value, errors: &mut Vec<String>) {
|
||||
// the overshoot distribution across a corpus of cues, and if a cue
|
||||
// ever trips this the right response is that measurement, not a
|
||||
// looser bound.
|
||||
// `voice` joins `bgm` on the strict side of this bound for the same
|
||||
// reason: it is a sum of stems this exporter produced, not a single
|
||||
// wave taken off the disc, so a peak at full scale is our arithmetic.
|
||||
Some(p) if matches!(kind, "bgm" | "voice") && p >= 0.0 => errors.push(format!(
|
||||
// `voice` was on the strict side of this bound while it was a SUM of a
|
||||
// region's chunks. It no longer is: a region carries three
|
||||
// presentations of one take, so the exporter keeps ONE stream and
|
||||
// performs no arithmetic on it. That puts `voice` with `se` -- a
|
||||
// single wave off the disc, mastered near full scale, whose lossy
|
||||
// decode overshoots by a fraction of a dB. `ADV`'s louder
|
||||
// presentation measures +0.0003 dBFS at source; refusing that would
|
||||
// be refusing the disc's own mastering.
|
||||
Some(p) if kind == "bgm" && p >= 0.0 => errors.push(format!(
|
||||
"{file}: peak is {p:.1} dBFS -- a SUM we produced clips"
|
||||
)),
|
||||
Some(p) if !matches!(kind, "bgm" | "voice") && p > 1.0 => errors.push(format!(
|
||||
Some(p) if kind != "bgm" && p > 1.0 => errors.push(format!(
|
||||
"{file}: peak is {p:.1} dBFS -- too far over full scale to be decode overshoot"
|
||||
)),
|
||||
Some(_) => {}
|
||||
|
||||
Reference in New Issue
Block a user