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()
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user