re: RETRACTED "audio is missing" — a subtitle cue is a START time
The load-bearing error of the whole voice-bank thread, and it is mine. It stood for three iterations across two write-ups that each called the result proven. I treated a subtitle cue as a timestamp that must fall INSIDE the voice clip, and concluded a 0.07 s clip could not host a cue at 4.70 s. A cue is when the line STARTS. The voice plays from the cue, so the clip only has to fit the window between the cue and the end of the movie. Under that reading every bank fits at plain 48 kHz: bank samples @48kHz cue window D_450 158967 3.31 4.00 5.30 D_451 76084 1.59 3.70 5.60 D_452 119562 2.49 0.00 8.34 D_453 108608 2.26 4.70 4.60 D_454 167828 3.50 0.00 9.50 2-3.5 s is also the right length for the lines. Nothing is missing, and the 17091-20563 Hz window from the previous commit is void with it -- its lower bound came from the same misreading. What survives, because it was measured rather than interpreted: the leading region is XMA1 mono, the decode runs to the final frame, and cue values are seconds. Separately settled, and it is what exposed the error: each shared bank holds ONE generic line. The 3-5 movies bound to a bank have IDENTICAL subtitle text, 5 banks out of 5 -- "Rhino 3 has landed. Commencing resupply.", "Resupply complete. You are cleared for take-off!", and so on. That also explains the historical in-game rejection of hokyu_DS_s13A -> VOICE_D_452 that started this whole thread. The line is generic, identical for s02A/s07A/s08A/s13A. Someone expecting a stage-13-specific line would hear the generic one and call it wrong -- while the binding is exactly right. The disc said so; the subtitle text now says so independently. Still open: whether the leading mono region is additional audio or an alternate take, since the totals above add it to the RIFF sub-waves. Artifact: examples/shared_bank_takes.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:
58
crates/sylpheed-formats/examples/shared_bank_takes.rs
Normal file
58
crates/sylpheed-formats/examples/shared_bank_takes.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! Does a shared resupply voice bank hold ONE line or SEVERAL takes?
|
||||
//!
|
||||
//! This decides whether the `samples/movie` lower bound on the sample rate is
|
||||
//! real. The test is static and does not need audio judgement: the movies that
|
||||
//! share a bank each have their OWN subtitle table. If those texts are identical
|
||||
//! the bank plausibly holds one generic line; if they differ, one bank is
|
||||
//! serving several distinct spoken lines.
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use sylpheed_formats::{movie_subtitle, PakArchive};
|
||||
|
||||
fn main() {
|
||||
let disc = std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC");
|
||||
let lang = PakArchive::open(format!("{disc}/dat/movie/eng.pak")).expect("eng.pak");
|
||||
let text = PakArchive::open(format!("{disc}/dat/GP_MAIN_GAME_E.pak")).expect("text pak");
|
||||
let demo_text = movie_subtitle::build_demo_text(&text);
|
||||
|
||||
// bank → the movies bound to it, from the record table (see movie-subtitle-link).
|
||||
let banks: BTreeMap<&str, Vec<&str>> = BTreeMap::from([
|
||||
("VOICE_D_450", vec!["hokyu_LS_s02A", "hokyu_LS_s03A", "hokyu_LS_s06A"]),
|
||||
(
|
||||
"VOICE_D_451",
|
||||
vec!["hokyu_LS_s09A", "hokyu_LS_s11A", "hokyu_LS_s15A", "hokyu_LS_s24A", "hokyu_LS_s27A"],
|
||||
),
|
||||
("VOICE_D_452", vec!["hokyu_DS_s02A", "hokyu_DS_s07A", "hokyu_DS_s08A", "hokyu_DS_s13A"]),
|
||||
("VOICE_D_453", vec!["hokyu_LS_s02H", "hokyu_LS_s03H", "hokyu_LS_s06H", "hokyu_LS_s09H"]),
|
||||
("VOICE_D_454", vec!["hokyu_DS_s07H", "hokyu_DS_s14H"]),
|
||||
]);
|
||||
|
||||
for (bank, movies) in &banks {
|
||||
println!("\n== {bank}");
|
||||
let mut seen: BTreeMap<String, Vec<&str>> = BTreeMap::new();
|
||||
for m in movies {
|
||||
let cues = movie_subtitle::track_voice_cues(&lang, m);
|
||||
let mut line = String::new();
|
||||
for (demo, t) in &cues {
|
||||
let txt = demo_text
|
||||
.get(demo)
|
||||
.map(|v| v.join(" "))
|
||||
.unwrap_or_else(|| format!("<demo {demo}>"));
|
||||
line.push_str(&format!("[{t:.2}] {txt} "));
|
||||
}
|
||||
if line.is_empty() {
|
||||
line.push_str("<no cues>");
|
||||
}
|
||||
seen.entry(line.trim().to_string()).or_default().push(m);
|
||||
}
|
||||
for (line, ms) in &seen {
|
||||
println!(" {:?}", ms);
|
||||
println!(" {line}");
|
||||
}
|
||||
println!(
|
||||
" -> {} distinct subtitle text(s) across {} movies",
|
||||
seen.len(),
|
||||
movies.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1033,6 +1033,24 @@ premise was wrong.**
|
||||
Candidates: the **7 `.embsec_` sections** (VAs 0x84D0000–0x86AC000, ~129 KB
|
||||
total, executable) or a hashed record in `hidden/MiscBin.pak`. **Finding it
|
||||
gives the actual per-phase clear condition for every stage.**
|
||||
* ❌ **(2026-08-26) RETRACTED — "the resupply banks are missing audio."** The
|
||||
error was mine and it stood for three iterations across two write-ups that each
|
||||
called the result proven: I treated a subtitle cue as a timestamp that must
|
||||
fall *inside* the voice clip. **A cue is when the line STARTS** — the voice
|
||||
plays from the cue to at most the movie's end. Under that reading all five
|
||||
banks fit at plain **48 kHz** (3.31 s in a 5.30 s window, 2.26 s in 4.60 s, …),
|
||||
and 2–3.5 s is the right length for the lines. Nothing is missing. The
|
||||
17 091–20 563 Hz window from the previous entry is void with it.
|
||||
✅ **Each shared bank is ONE generic line** — the 3–5 movies sharing a bank have
|
||||
**identical subtitle text**, 5/5 banks (`examples/shared_bank_takes.rs`).
|
||||
🎯 **That also explains the historical in-game rejection** of
|
||||
`hokyu_DS_s13A → VOICE_D_452`: the line is the generic "Resupply complete. You
|
||||
are cleared for take-off!", the same for s02A/s07A/s08A/s13A. Someone expecting
|
||||
a stage-13-specific line would call the generic one wrong while the binding is
|
||||
right.
|
||||
❔ Still open: whether the leading XMA1-mono region is *additional* audio or an
|
||||
alternate take of the same line — the totals above add it to the RIFF sub-waves
|
||||
and would double-count if it is a duplicate.
|
||||
* ❌ **(2026-08-25) The `.slb` "multi-subwave" guess is REFUTED, and the voice
|
||||
decoder is discarding up to 87 % of a bank.** The record table gives a
|
||||
**direct** binding `hokyu_DS_s13A -> VOICE_D_452` where the corpus records the
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# The resupply voice banks — the decoder discards up to 87 % of them
|
||||
|
||||
Status: ❌ the recorded "multi-subwave / not cleanly sliced" explanation is
|
||||
**REFUTED**. ✅ the real defect is measured. 🟡 the in-game verdict that rejected
|
||||
the `hokyu_DS_s13A` voice binding is therefore **not** evidence against it. ❔ the
|
||||
binding still needs audio verification, which needs a human.
|
||||
**REFUTED**. ✅ each shared bank is **one generic line**, which explains the
|
||||
in-game verdict that rejected the `hokyu_DS_s13A` binding — the line really is
|
||||
generic. ❌ **my own "audio is missing" conclusion is RETRACTED**: a subtitle cue
|
||||
is a START time, not a point inside the clip, and under the correct reading every
|
||||
bank fits at ordinary 48 kHz.
|
||||
|
||||
Artifact: `crates/sylpheed-formats/examples/voice_bank_shape.rs`.
|
||||
|
||||
@@ -120,7 +122,39 @@ 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
|
||||
## ❌ RETRACTED — "audio is missing". The cue is a START time.
|
||||
|
||||
**This is the load-bearing error of this whole file, and it is mine.** I treated
|
||||
a subtitle cue as a timestamp that must fall *inside* the voice clip, and
|
||||
concluded that a 0.07 s clip could not host a cue at 4.70 s.
|
||||
|
||||
A cue is when the line **starts**. The voice plays *from* the cue, so the clip
|
||||
only has to fit in the window between the cue and the end of the movie. Under
|
||||
that reading every bank fits comfortably — at plain **48 kHz**:
|
||||
|
||||
| bank | samples | at 48 kHz | cue | window to movie end | fits |
|
||||
|---|---|---|---|---|---|
|
||||
| `VOICE_D_450` | 158 967 | 3.31 s | 4.00 s | 5.30 s | ✅ |
|
||||
| `VOICE_D_451` | 76 084 | 1.59 s | 3.70 s | 5.60 s | ✅ |
|
||||
| `VOICE_D_452` | 119 562 | 2.49 s | 0.00 s | 8.34 s | ✅ |
|
||||
| `VOICE_D_453` | 108 608 | 2.26 s | 4.70 s | 4.60 s | ✅ |
|
||||
| `VOICE_D_454` | 167 828 | 3.50 s | 0.00 s | 9.50 s | ✅ |
|
||||
|
||||
2–3.5 s is also the right length for the lines themselves ("Rhino 3 has landed.
|
||||
Commencing resupply."). So there is **no evidence any audio is missing**, and the
|
||||
sample rate is plausibly the ordinary 48 kHz.
|
||||
|
||||
❌ The **17 091 – 20 563 Hz window** from the previous entry is void with it: its
|
||||
lower bound came from the same misreading.
|
||||
|
||||
What survives from that work: the leading region is XMA1 mono, the decode runs to
|
||||
the final frame, and the cue values really are seconds. Those were measured. Only
|
||||
the interpretation of what a cue *means* was wrong — and it was wrong for three
|
||||
iterations, through two write-ups that each called the result "proven".
|
||||
|
||||
### The original argument, kept
|
||||
|
||||
Below is what I wrote when I believed a cue had to fall inside the clip.
|
||||
|
||||
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
|
||||
@@ -144,6 +178,30 @@ Three of the five are decisive; the other two have their only cue at 0.0 s, whic
|
||||
tells us nothing in either direction. So **something is genuinely missing from
|
||||
these banks** — independent of anything above, and now measured rather than felt.
|
||||
|
||||
## ✅ Each shared bank is ONE generic line
|
||||
|
||||
The banks are bound to 3–5 movie slots each, and the movies sharing a bank have
|
||||
**identical subtitle text** — 5 banks out of 5 (`examples/shared_bank_takes.rs`):
|
||||
|
||||
| bank | movies | the line |
|
||||
|---|---|---|
|
||||
| `VOICE_D_450` | 3 | "Rhino 3 has landed. Commencing resupply." |
|
||||
| `VOICE_D_451` | 3 (+2 with no cue) | "Rhino Leader has landed. Commencing resupply." |
|
||||
| `VOICE_D_452` | 4 | "Resupply complete. You are cleared for take-off!" |
|
||||
| `VOICE_D_453` | 4 | "All right, beginning resupply." |
|
||||
| `VOICE_D_454` | 2 | "Resupply complete. Switching to take-off sequence." |
|
||||
|
||||
So a bank holds one line, reused across stages — not several takes.
|
||||
|
||||
### 🎯 And this explains the historical in-game rejection
|
||||
|
||||
`hokyu_DS_s13A → VOICE_D_452` was rejected as "the wrong recording". But
|
||||
`VOICE_D_452`'s line is the **generic** "Resupply complete. You are cleared for
|
||||
take-off!", identical for `s02A`, `s07A`, `s08A` *and* `s13A`. Someone expecting
|
||||
a stage-13-specific line would hear the generic one and call it wrong — while the
|
||||
binding is exactly right. The disc says so, and now the subtitle text says so
|
||||
independently.
|
||||
|
||||
## ✅ The leading region IS XMA1 — but **mono**, not stereo
|
||||
|
||||
Retried with the `fmt ` chunk built to `slb::synth_xma1_fmt`'s exact byte layout
|
||||
|
||||
Reference in New Issue
Block a user