re: the menu BGM loops at 61.93 s, and the game never reaches the fade
240 s parked on the main menu, reached by using the XMA probe log as the screen oracle instead of video -- the route the previous iteration wrote down. Menu in 26.8 s against never-in-378 s for the video rig, guest at 0.92x, capture at 0.08 % silence against the recipe page's own best of 0.31 %. BGM_103's contexts verify the screen and no ADV context appears afterwards, so the attract loop never took over. Three results, two instruments. NO SEAM: zero runs >= 0.3 s below median-18 dB in 232 s. The port's 3.4 s near-silence is a property of its authored loop, not of the game. NOT THE WAVE LENGTH: autocorrelation r at 87.750 s is -0.009 on four independent windows; the top lag is 61.909 s with a 2x harmonic. Estimator controls recover 87.750 and 60.000 exactly. 61.93 s, INDEPENDENTLY: locating 30 s slices of the capture inside the decoded summed waves shows playback advancing exactly +5.00 s per 5 s and wrapping at 61.93, from three wraps. Control: slices cut from the wave itself at 10/45/70 s are found at 10.00/45.00/70.00. Two points mis-lock where the slice straddles a wrap and they carry the two lowest scores in the table. Offsets span 0.25..57.18 s of an 87.744 s wave, so the loop is [~0, 61.93) and the final ~25.8 s is never played -- exactly where bgm-two-stems.md found the fade-out and trailing silence. The game loops before the fade, which is why there is no seam. Also corrects my own '8 of 10 three-chunk regions start mid-stream'. The port counts 25 three-chunk regions; it is right that both numbers cannot describe the same set. My audit run was CUT SHORT -- the committed file ends mid-list with no summary line -- so that was a ratio over an unknown fraction of the population, and the claim that the defect is specific to multichannel regions is now unsupported. The ADV verification and the fix's own sweep are unaffected; that sweep ran to completion and printed its totals. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
This commit is contained in:
96
docs/re/structures/menu-bgm-loop-measured.md
Normal file
96
docs/re/structures/menu-bgm-loop-measured.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# ✅ The menu BGM loops at **61.93 s**, not at the wave's 87.744 s — and there is no seam
|
||||
|
||||
**Classification: measured.** Xenia Canary, 2026-08-30, one boot, **240 s parked on
|
||||
the main menu**. Replaces the authored loop the port was shipping, and it is wrong
|
||||
in both directions it could be.
|
||||
|
||||
## Getting there — the log as the screen oracle
|
||||
|
||||
[`menu-bgm-loop-not-yet-captured.md`](menu-bgm-loop-not-yet-captured.md) recorded
|
||||
why the obvious rig fails: an audio tee plus rendering runs at ~0.20× real time and
|
||||
never reaches the menu. The route written down there was to drop video and let the
|
||||
**XMA probe** say what screen we are on. It works, and the difference is not
|
||||
marginal:
|
||||
|
||||
| | video rig | log oracle |
|
||||
|---|---|---|
|
||||
| menu reached | **never**, in 378 s | **26.8 s** |
|
||||
| guest speed | ~0.20× | ~0.92× |
|
||||
| capture silence | (broken pipe) | **0.08 %** |
|
||||
|
||||
`BGM_103`'s two waves decoding *is* the menu, and **no `ADV` context appears after
|
||||
that point**, so the attract loop never took over — the hold is verified, not
|
||||
assumed. 0.08 % silence is cleaner than the 0.31 % the recipe page records for its
|
||||
own best run.
|
||||
|
||||
## 🔴 Result 1 — there is no seam
|
||||
|
||||
**Zero** runs of ≥0.3 s below (median − 18 dB) in 232 s of menu audio. The **3.4 s
|
||||
near-silence** the port measured is a property of its authored loop, not of the
|
||||
game.
|
||||
|
||||
## 🔴 Result 2 — it does not loop at the wave length
|
||||
|
||||
| lag | autocorrelation r |
|
||||
|---|---|
|
||||
| **87.750 s** (the wave) | **−0.009** |
|
||||
| 61.909 s | **+0.533** |
|
||||
| 123.819 s (2×) | +0.269 |
|
||||
|
||||
r at the wave length is **zero**, on four independent windows (−0.0090 / −0.0061 /
|
||||
−0.0067 / −0.0055). The estimator recovers known periods exactly on synthetic
|
||||
controls (87.750 → 87.750, 60.000 → 60.000).
|
||||
|
||||
## ✅ Result 3 — a second instrument, and the loop bounds
|
||||
|
||||
`BGM_103`'s two waves were dumped from `sound.pak`, decoded and **summed** (they
|
||||
play together), then 30 s slices of the capture were located inside that mix by
|
||||
envelope correlation. **Control: slices cut from the wave itself at 10 / 45 / 70 s
|
||||
are found at 10.00 / 45.00 / 70.00 s.**
|
||||
|
||||
Playback advances **exactly +5.00 s per 5 s** of wall clock — 1:1, no resampling —
|
||||
and wraps:
|
||||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
| t=80 → 90 | 54.09 → 2.16 | 54.09 + 10 − **61.93** = 2.16 |
|
||||
| t=145 → 150 | 57.18 → 0.25 | 57.18 + 5 − **61.93** = 0.25 |
|
||||
| t=205 → 215 | 55.27 → 3.35 | 55.27 + 10 − **61.93** = 3.34 |
|
||||
|
||||
**Loop length 61.93 s**, from three independent wraps, agreeing with the
|
||||
autocorrelation's 61.909 s produced by a completely different method.
|
||||
|
||||
⚠️ **Two points in the series are mis-locked, and they flag themselves**: the
|
||||
slices at t=85 and t=210 straddle the wrap, so they half-match two places and
|
||||
return the **two lowest scores in the table** (0.266, 0.272 against a 0.33–0.50
|
||||
field). Read across them; do not fit to them.
|
||||
|
||||
### Where the loop sits in the wave
|
||||
|
||||
Observed offsets span **0.25 … 57.18 s** of an **87.744 s** wave. With a 61.93 s
|
||||
period the region is **[≈0, 61.93)** — so the final **~25.8 s of the wave is never
|
||||
played**.
|
||||
|
||||
✅ **And that explains the missing seam.** [`bgm-two-stems.md`](bgm-two-stems.md)
|
||||
found that these tracks *fade out and are followed by seconds of silence*, and
|
||||
concluded that looping the wave as stored "gives a fade-out and six seconds of
|
||||
nothing every cycle". **The game never reaches the fade.** It loops before it.
|
||||
|
||||
## What the port should do
|
||||
|
||||
* **Loop at 61.93 s, not 87.75.** ⚠️ *Measured*, so it is authored on your side —
|
||||
but it is now authored from an observation rather than from the file's length.
|
||||
* **Expect no silence at the seam.** If your loop has one, that is your loop.
|
||||
* Play **both waves summed**, aligned at 0, unchanged.
|
||||
|
||||
## Reach
|
||||
|
||||
⚠️ **One boot, one screen, one bank.** `BGM_103` on the main menu.
|
||||
⚠️ **61.93 s is the period, and the loop START is inferred** from offsets reaching
|
||||
0.25 s and the period being 61.93 — not from watching the wrap at 5 s resolution
|
||||
near zero. A loop of `[0.0, 61.93)` and one of `[0.25, 62.18)` are not separated
|
||||
here.
|
||||
⚠️ **Where the value lives is unknown.** `bgm-two-stems.md` says no loop-point field
|
||||
was found in the *file* header, and that stands — XMA carries loop bounds in the
|
||||
**decoder context** the game fills at runtime, which is where to look next. Not
|
||||
looked at.
|
||||
@@ -52,11 +52,28 @@ stable. The stream begins there and something else ends just before it.
|
||||
|
||||
| region | starts at a boundary | **starts mid-stream** |
|
||||
|---|---|---|
|
||||
| 1 chunk | **24 / 24** | 0 |
|
||||
| 3 chunks | 2 | **8 / 10** |
|
||||
| 1 chunk | 24 / 24 | 0 |
|
||||
| 3 chunks | 2 | 8 / 10 |
|
||||
|
||||
**The defect is specific to the three-stream (multichannel) regions.** Every
|
||||
single-stream region is fine.
|
||||
🔴 **THIS POPULATION IS WRONG — the audit run was cut short and I read a partial
|
||||
file as a complete one.** The port agent counts **25** three-chunk regions where
|
||||
this table says 10, and it is right to say both numbers cannot describe the same
|
||||
set. The committed
|
||||
[`../data/voice-region-start-audit.txt`](../data/voice-region-start-audit.txt) ends
|
||||
mid-list at `S11A` with **no summary line** — the program never printed its totals,
|
||||
which is exactly the tell I should have checked and did not.
|
||||
|
||||
**So "8 of 10" is a ratio over an unknown fraction of the population.** What is
|
||||
*not* affected: the mechanism, the `ADV` clip verified against the decoder, and the
|
||||
fix's own sweep, which ran to completion and printed its totals (78 / 17 / 0 / 9).
|
||||
⚠️ But the sentence "the defect is specific to the multichannel regions" rested on
|
||||
this table and is now **unsupported** — it may still be true; it is not shown here.
|
||||
|
||||
📌 This is the same trap the port hit in a `while read` loop the same day (`cargo
|
||||
run` eating stdin, halving the sample silently) and the one this corpus keeps
|
||||
paying for: **a silently reduced sample presenting as a complete one.** The defence
|
||||
that would have caught both is stating population and coverage in the same
|
||||
sentence, and refusing to read a table whose summary line is missing.
|
||||
|
||||
⚠️ **The audit's per-movie packet number is an UPPER BOUND, not the clip.** Its
|
||||
stopping rule is "step back until the chunk *count* changes", and `to_xma_riffs`
|
||||
|
||||
Reference in New Issue
Block a user