re: the resupply banks really are missing audio — the subtitles prove it

The corpus said 0.14 s is "far too short for the spoken line". That is a
judgement about audio, and audio judgements cannot be made in this container.
The subtitle tracks settle it without listening: each carries cue START
times, and a subtitle that appears at t seconds cannot sit inside a clip
shorter than t.

FFmpeg-measured (not estimated from a compression ratio -- the first version
of this example used an 8:1 guess, which is not good enough to hang a
conclusion on):

  hokyu_LS_s02A  D_450  cue 4.00 s  audio 1.41 s  MISSING
  hokyu_LS_s09A  D_451  cue 3.70 s  audio 1.81 s  MISSING
  hokyu_LS_s02H  D_453  cue 4.70 s  audio 0.07 s  MISSING
  hokyu_DS_s13A  D_452  cue 0.00 s  audio 1.21 s  no signal
  hokyu_DS_s07H  D_454  cue 0.00 s  audio 0.21 s  no signal

Three of five are decisive; the other two have their only cue at 0.0 s and
say nothing either way. So something is genuinely missing from these banks --
established independently of the leading-region work, and measured rather
than felt.

The fmt-variation probe I recorded as the next step is INCONCLUSIVE and is
written up as such: 36 combinations over VOICE_D_453's 22-packet leading
region all produced 0 PCM bytes, including ones that should be equivalent to
the crate's own synth_xma1_fmt, which does parse. So the probe tested my
hand-built fmt chunk, not the hypothesis, and it is NOT evidence that the
region is non-XMA. The retry should use the crate's helper.

Artifact: examples/voice_len_vs_subs.rs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
Sylpheed RE agent
2026-08-25 23:45:26 +00:00
parent 49a09a9496
commit af320466bb
3 changed files with 127 additions and 3 deletions

View File

@@ -0,0 +1,78 @@
//! Is audio actually missing from the resupply banks? Ask the subtitles.
//!
//! The `.slb` decode of `VOICE_D_453` yields 0.14 s, which "looks too short" —
//! but that judgement was an impression. The subtitle track for each movie
//! carries cue times, so it says independently how long the spoken line runs.
//! If the last cue lands near the decoded length, nothing is missing; if it lands
//! far past it, audio really is being lost.
use sylpheed_formats::{movie_subtitle, slb, PakArchive};
/// XMA1 at 48 kHz stereo, 16-bit → bytes per second of PCM.
const PCM_BYTES_PER_SEC: f32 = 48000.0 * 2.0 * 2.0;
/// Decode one sub-wave with FFmpeg and return its length in seconds. Measured,
/// not estimated from a compression ratio — the ratio guess was the first version
/// of this and it is not good enough to hang a conclusion on.
fn decoded_secs(riff: &[u8]) -> f32 {
use std::io::Write;
use std::process::{Command, Stdio};
let Ok(mut c) = Command::new("ffmpeg")
.args(["-v", "error", "-i", "pipe:0", "-f", "s16le", "pipe:1"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
else {
return f32::NAN;
};
let buf = riff.to_vec();
let mut stdin = c.stdin.take().unwrap();
std::thread::spawn(move || {
let _ = stdin.write_all(&buf);
});
let out = c.wait_with_output().expect("ffmpeg");
out.stdout.len() as f32 / PCM_BYTES_PER_SEC
}
fn main() {
let disc = std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC");
let snd = PakArchive::open(format!("{disc}/dat/sound.pak")).expect("sound.pak");
let lang = PakArchive::open(format!("{disc}/dat/movie/eng.pak")).expect("eng.pak");
// movie → the bank the record table binds it to.
let bound = [
("hokyu_LS_s02A", 450),
("hokyu_LS_s09A", 451),
("hokyu_DS_s13A", 452),
("hokyu_LS_s02H", 453),
("hokyu_DS_s07H", 454),
];
println!(
"{:<18} {:>6} {:>10} {:>12} {:>12}",
"movie", "bank", "last cue s", "decoded s", "verdict"
);
for (movie, n) in bound {
let cues = movie_subtitle::track_voice_cues(&lang, movie);
let last = cues.iter().map(|(_, t)| *t).fold(0.0f32, f32::max);
let path = format!("eng\\etc\\VOICE_D_{n}.slb");
let Some(entry) = snd.find_by_name(&path) else { continue };
let bytes = snd.read(entry).expect("read");
// Sum the sub-waves' payloads as the decoder currently sees them.
let riffs = slb::to_xma_riffs(&bytes);
let secs: f32 = riffs.iter().map(|r| decoded_secs(r)).sum();
// One-directional: a subtitle that appears at t seconds cannot sit inside
// a clip shorter than t. A cue at 0.0 tells us nothing either way.
let verdict = if last == 0.0 {
"no cue signal"
} else if last > secs {
"AUDIO MISSING"
} else {
"consistent"
};
println!(
"{movie:<18} {:>6} {last:>10.2} {secs:>12.2} {verdict:>12} cues={}",
format!("D_{n}"),
cues.len()
);
}
}

View File

@@ -1057,9 +1057,18 @@ premise was wrong.**
Byte coverage was the wrong success metric. The rule also matches **1524 of
8021** RIFF-bearing `sound.pak` entries, including `RT*` banks that work today,
so it risked a wide regression to not-fix five banks.
▶️ Next cheap probe: vary the synthesised `fmt` (channels / streams / sample
rate) instead of assuming the container. Then a human has to listen; audio
judgement cannot be done here. See
**(same day) Audio really IS missing — proven by the subtitle cue times**,
not by the "sounds too short" impression the docs recorded. A subtitle that
appears at *t* seconds cannot sit inside a clip shorter than *t*, and three of
five banks fail that: `D_450` cue 4.00 s vs 1.41 s decoded, `D_451` 3.70 vs
1.81, `D_453` **4.70 vs 0.07**. The other two have their only cue at 0.0 s and
give no signal. Artifact `examples/voice_len_vs_subs.rs`, FFmpeg-measured.
**The fmt-variation probe was INCONCLUSIVE** — 36 combinations over
`VOICE_D_453`'s leading region all produced 0 PCM bytes, *including* ones
equivalent to the crate's working `synth_xma1_fmt`, so the probe tested my
hand-built `fmt` chunk rather than the hypothesis. Not evidence the region is
non-XMA. ▶️ Retry building the chunk with the crate's own helper and varying
its parameters. See
[`voice-bank-leading-region.md`](voice-bank-leading-region.md).
***(2026-08-25) My own boot-nav diagnosis, MEASURED AND WITHDRAWN.** I said
the run died because `skip_intro.sh` gates the title test at `rmse <= 1500`

View File

@@ -120,6 +120,43 @@ it.** Two measurements killed it:
The refuted attempt is recorded in `slb.rs` beside the code, so the next person
does not re-derive the arithmetic and re-make the same change.
## ✅ Audio really is missing — proven by the subtitles, not by impression
The corpus's original wording was that 0.14 s is "far too short for the spoken
line". That is a judgement, and judgements about audio cannot be made in this
container. The subtitle tracks settle it instead: each carries **cue start
times**, and a subtitle that appears at *t* seconds cannot sit inside a clip
shorter than *t*.
Decoded with FFmpeg (measured, not estimated from a compression ratio — the
first version of this used an 8:1 guess and that is not good enough to hang a
conclusion on), artifact `examples/voice_len_vs_subs.rs`:
| movie | bank | last cue | decoded audio | verdict |
|---|---|---|---|---|
| `hokyu_LS_s02A` | `D_450` | 4.00 s | 1.41 s | **audio missing** |
| `hokyu_LS_s09A` | `D_451` | 3.70 s | 1.81 s | **audio missing** |
| `hokyu_LS_s02H` | `D_453` | 4.70 s | **0.07 s** | **audio missing** |
| `hokyu_DS_s13A` | `D_452` | 0.00 s | 1.21 s | no signal |
| `hokyu_DS_s07H` | `D_454` | 0.00 s | 0.21 s | no signal |
Three of the five are decisive; the other two have their only cue at 0.0 s, which
tells us nothing in either direction. So **something is genuinely missing from
these banks** — independent of anything above, and now measured rather than felt.
## ❔ Varying the XMA format — inconclusive, and for a boring reason
The recorded next step was to vary the synthesised `fmt` (channels, streams,
sample rate) rather than assume the container. I tried 36 combinations over
`VOICE_D_453`'s 22-packet leading region and **every one produced 0 PCM bytes**
including combinations that should be equivalent to the crate's own
`synth_xma1_fmt(2, 2, 48000)`, which does at least parse (it yields 1792 bytes).
That means the probe tested **my hand-built `fmt` chunk**, not the hypothesis. It
is not evidence that the region is non-XMA. The next attempt should build the
chunk with the crate's own helper and vary its parameters, rather than
hand-rolling the WAVEFORMATEX.
## What this does not settle
***What the leading region holds.** Its *size* is now exact (1392 + n·2048)