# ✅ 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. ## 🔴 Contested (2026-08-30, later) — read the XMA context page before using the offsets Reading `loop_start` / `loop_end` out of the running decoder ([`menu-bgm-loop-fields-conflict.md`](menu-bgm-loop-fields-conflict.md)) puts the cycle's **start** at ~11.6 % into the wave, where this page's locator put the observed offsets at 0.25 s. **Both cannot be right.** ⚠️ **And the weak link is likely this page's control.** It located slices **cut from the wave itself** — exact copies, a far easier problem than matching a real capture that differs by decoder, gain and mix. A control easier than the measurement does not bound the measurement's error. The clean +5.00 s stepping shows the locator is *self-consistent*; it does not show it locked to the right phrase, and music with repeated sections is where a locator aliases. **The 61.93 s period survives better than the placement**: it is corroborated by an autocorrelation that used no wave at all, and the port's trimmed loop plays without a seam. Treat the *length* as measured and the *start* as open. ## 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.