to_xma_riffs now emits the leading headerless segment when it sits at a whole number of XMA1 packets and carries a non-zero byte. VOICE_D_453 goes from a 0.14 s trailing fragment to a 45116-byte leading sub-wave that dominates it. I withdrew this exact change earlier for two reasons. Both are now answered rather than argued away: * "It recovers no audio" -- it used the STEREO format. At two channels every bank yields exactly 1792 bytes, one frame, whatever its size. Mono yields up to 113x more. * "It matches 1524 of 8021 RIFF-bearing entries" -- the byte-level reach is still 1524, but the audible reach is not. Across the 84 movie-bound banks the segment adds >1 s to exactly 7, the hokyu_*_H tankers on D_453/D_454 -- precisely the broken ones -- and <=0.25 s to 66 of the rest. The largest non-resupply addition is S04A at +0.66 s on a 256 s movie. The safety oracle is recorded with its limits: 8 of the 84 banks ALREADY exceed their movie's duration before the change, by hundredths of a second, so it cannot resolve differences at that scale. It establishes scoping, not correctness. Callers clamp to the movie length regardless. VOICE_D_451's all-zero leading region is skipped by the non-zero guard, so the rule cannot prepend silence to a bank that does not need it. Pinned, as is the packet arithmetic (n = 8, 1, 7, 22, 29) which has no tunable. slb_disc, movie_subtitle_disc and movie_manifest_disc all still pass. NOT verified by ear -- that needs a human. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
87 lines
3.2 KiB
Rust
87 lines
3.2 KiB
Rust
//! The `.slb` leading segment — recovered, and scoped.
|
|
//!
|
|
//! `VOICE_D_453` used to decode to 0.14 s because its line lives in a headerless
|
|
//! packet stream *before* the first `RIFF`, and the decoder started at the
|
|
//! `RIFF`. The banks that looked fine were the ones whose leading segment is
|
|
//! silence. One rule, two outcomes.
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use sylpheed_formats::{slb, PakArchive};
|
|
|
|
fn disc_root() -> Option<PathBuf> {
|
|
if let Ok(p) = std::env::var("SYLPHEED_DISC") {
|
|
let p = PathBuf::from(p);
|
|
if p.join("dat").is_dir() {
|
|
return Some(p);
|
|
}
|
|
}
|
|
let default = Path::new(
|
|
"/home/fabi/RE - Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja)",
|
|
);
|
|
default.join("dat").is_dir().then(|| default.to_path_buf())
|
|
}
|
|
|
|
macro_rules! skip_without_disc {
|
|
($root:ident) => {
|
|
let Some($root) = disc_root() else {
|
|
eprintln!("SKIP: set SYLPHEED_DISC");
|
|
return;
|
|
};
|
|
};
|
|
}
|
|
|
|
fn bank(root: &Path, n: u32) -> Vec<u8> {
|
|
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
|
|
let path = format!("eng\\etc\\VOICE_D_{n}.slb");
|
|
let entry = snd.find_by_name(&path).expect("bank present");
|
|
snd.read(entry).expect("read")
|
|
}
|
|
|
|
/// The boundary is arithmetic and has no tunable: the first `RIFF` sits at
|
|
/// exactly `HEADERLESS_DATA_OFFSET + n*XMA1_PACKET` in every resupply bank.
|
|
#[test]
|
|
fn leading_segment_is_a_whole_number_of_packets() {
|
|
skip_without_disc!(root);
|
|
for (n, packets) in [(450u32, 8usize), (451, 1), (452, 7), (453, 22), (454, 29)] {
|
|
let b = bank(&root, n);
|
|
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
|
|
assert!(ri > slb::HEADERLESS_DATA_OFFSET, "VOICE_D_{n}");
|
|
let lead = ri - slb::HEADERLESS_DATA_OFFSET;
|
|
assert_eq!(lead % slb::XMA1_PACKET, 0, "VOICE_D_{n} not a whole packet count");
|
|
assert_eq!(lead / slb::XMA1_PACKET, packets, "VOICE_D_{n} packet count");
|
|
}
|
|
}
|
|
|
|
/// The two banks whose line lives in the leading segment now yield it.
|
|
#[test]
|
|
fn broken_banks_recover_their_line() {
|
|
skip_without_disc!(root);
|
|
// (bank, sub-waves expected, payload of the leading one)
|
|
for (n, waves, lead_len) in [(453u32, 2usize, 45116usize), (454, 2, 59452)] {
|
|
let riffs = slb::to_xma_riffs(&bank(&root, n));
|
|
assert_eq!(riffs.len(), waves, "VOICE_D_{n} sub-wave count");
|
|
assert_eq!(riffs[0].len(), lead_len, "VOICE_D_{n} leading segment");
|
|
// It must be the LARGER part: that is the whole point.
|
|
assert!(
|
|
riffs[0].len() > riffs[1].len() * 5,
|
|
"VOICE_D_{n}: leading segment should dominate"
|
|
);
|
|
}
|
|
}
|
|
|
|
/// `VOICE_D_451`'s leading region is all zeros — the guard must skip it, so the
|
|
/// rule cannot prepend silence to a bank that does not need it.
|
|
#[test]
|
|
fn all_zero_leading_region_is_skipped() {
|
|
skip_without_disc!(root);
|
|
let b = bank(&root, 451);
|
|
let ri = b.windows(4).position(|w| w == b"RIFF").unwrap();
|
|
assert!(
|
|
b[slb::HEADERLESS_DATA_OFFSET..ri].iter().all(|x| *x == 0),
|
|
"expected an all-zero leading region"
|
|
);
|
|
// Two sub-waves, both from the RIFF section — no synthesised third.
|
|
assert_eq!(slb::to_xma_riffs(&b).len(), 2);
|
|
}
|