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:
78
crates/sylpheed-formats/examples/voice_len_vs_subs.rs
Normal file
78
crates/sylpheed-formats/examples/voice_len_vs_subs.rs
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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`
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user