re: the loop IS a runtime XMA field -- and reading it contradicts my audio measurement
No Canary patch was needed: UpdateLoopStatus already logs loop_start/loop_end, they just need the Apu category (--log_mask=13 --log_level=3). Decoded, from the menu, 8734 records all after BGM_103's contexts appear: ctx0 (wave 3876864) loop_start 3605682 loop_end 25640423 loop_count 255 ctx1 (wave 3930112) loop_start 3539158 loop_end 26216351 loop_count 255 The movie's three ADV streams log NO loop records -- they do not loop. Semantics visible in the trajectory: read_offset runs from 32 upward and 20 % of samples sit below loop_start, so the stream plays from the beginning and loop_start is where it returns AFTER loop_end. No wrap was observed -- the 45 s hold ended with read_offset at 17 M against a loop_end of 25.6 M. Two registered predictions REFUTED. loop_start is not ~0 but 11.6 % in. And a linear bits-to-seconds conversion is invalid: it gives 62.34 s and 63.29 s for two stems that must play sample-synchronously, which is impossible, so the data refutes the assumption on its own. That leaves a conflict I am not resolving: the field implies a cycle of roughly [10 s, 72 s]; my audio tracking reported offsets 0.25..57.18 s. Recorded as contested, with the likely weak link named as mine -- that locator's control used slices cut from the wave itself, exact copies, which is an easier problem than matching a real capture, and a control easier than the measurement does not bound its error. The port is told to change nothing: its trimmed 61.93 s loop is verified in its own output, and the length survives better than the placement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
This commit is contained in:
80
docs/re/structures/menu-bgm-loop-fields-conflict.md
Normal file
80
docs/re/structures/menu-bgm-loop-fields-conflict.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 🟡 The loop point IS a runtime field — and reading it contradicts the audio measurement
|
||||
|
||||
**Classification: decoded** (the fields and their values) **plus an unresolved
|
||||
conflict** (what they mean in seconds). Xenia Canary, 2026-08-30, one boot, 45 s on
|
||||
the menu. **The port should change nothing on the strength of this page.**
|
||||
|
||||
## ✅ The loop point is not absent from the format
|
||||
|
||||
[`bgm-two-stems.md`](bgm-two-stems.md) says *"no loop-point field has been
|
||||
identified in the XMA header, so a menu loop is authored"*. That is true of the
|
||||
**file header** and it left the wrong impression. The loop lives in the **XMA
|
||||
decoder context**, set at runtime by `XMASetLoopData`, and Xenia's
|
||||
`UpdateLoopStatus` already logs it — **no patch was needed**, only the Apu log
|
||||
category (`--log_mask=13 --log_level=3`).
|
||||
|
||||
| ctx | wave | `loop_start` | `loop_end` | `loop_count` |
|
||||
|---|---|---|---|---|
|
||||
| 0 | 3 876 864 B | **3 605 682** | **25 640 423** | 255 (infinite) |
|
||||
| 1 | 3 930 112 B | **3 539 158** | **26 216 351** | 255 |
|
||||
|
||||
Bit offsets. **8 734 records, every one after `BGM_103`'s contexts appear** — the
|
||||
movie's three `ADV` streams log none at all, i.e. they do not loop.
|
||||
|
||||
✅ **And the semantics are visible in the trajectory.** `input_buffer_read_offset`
|
||||
runs from **32** (the first packet header) upward, and **20 % of samples sit below
|
||||
`loop_start`** — so the stream plays from the *beginning*, and `loop_start` is where
|
||||
it returns *after* `loop_end`. The first pass is longer than the cycles after it.
|
||||
|
||||
⚠️ **No wrap was observed.** Max read offset was 16.9 M / 17.2 M against a
|
||||
`loop_end` of 25.6 M / 26.2 M — the 45 s hold was too short. The jump itself is
|
||||
inferred from the field semantics, not watched.
|
||||
|
||||
## 🔴 Two things I predicted are refuted
|
||||
|
||||
**1. "`loop_start` ≈ 0" — no.** It is ~3.5–3.6 M bits, 11.4–11.6 % into the stream.
|
||||
I registered that prediction before the run and it is wrong.
|
||||
|
||||
**2. A linear bits→seconds conversion — invalid, and the data proves it.** Applied
|
||||
to each stem with its own `byte_size`:
|
||||
|
||||
| | linear loop duration |
|
||||
|---|---|
|
||||
| ctx0 | **62.34 s** |
|
||||
| ctx1 | **63.29 s** |
|
||||
|
||||
Two stems that play **sample-synchronously** cannot have loop durations 0.95 s
|
||||
apart — they would drift a second per cycle. So the assumption fails on its own
|
||||
output. XMA frames are variable-length in bits, which is exactly why.
|
||||
|
||||
## 🔴 The conflict, stated rather than resolved
|
||||
|
||||
`loop_start` at 11.6 % of the stream implies a cycle of roughly **[10 s, 72 s]** of
|
||||
the 87.744 s wave. But
|
||||
[`menu-bgm-loop-measured.md`](menu-bgm-loop-measured.md) put the observed wave
|
||||
offsets at **0.25 … 57.18 s**. **Both cannot be true.**
|
||||
|
||||
⚠️ **And the weakness is probably mine.** That page's locator control used slices
|
||||
**cut from the wave itself**, which are exact copies — a far easier matching problem
|
||||
than a real capture, which differs by decoder, gain and mix. **A control that is
|
||||
easier than the measurement does not bound the measurement's error**, and music
|
||||
with repeated phrases is exactly where a locator aliases. The clean +5.00 s stepping
|
||||
shows the locator is *self-consistent*; it does not show it locked to the right
|
||||
phrase.
|
||||
|
||||
So the honest position is:
|
||||
|
||||
| claim | status |
|
||||
|---|---|
|
||||
| the loop is a runtime field, with these values | ✅ decoded |
|
||||
| the movie streams do not loop | ✅ decoded |
|
||||
| the cycle is **61.93 s** | 🟡 measured from audio, and its control was too easy |
|
||||
| where the cycle *starts* in the wave | 🔴 **contested** — 0.25 s from audio, ~10 s from the field |
|
||||
| bits → seconds | ❔ needs an XMA frame walk; not done |
|
||||
|
||||
## What the port should do: nothing yet
|
||||
|
||||
Its shipped loop — trim to 61.930 s — is verified in its own output and has no seam.
|
||||
**Do not re-author it from this page.** What would settle it is walking the XMA
|
||||
frame headers to convert `loop_start`/`loop_end` honestly, and a hold long enough to
|
||||
**watch a wrap** rather than infer it. Neither is done.
|
||||
@@ -83,6 +83,24 @@ nothing every cycle". **The game never reaches the fade.** It loops before it.
|
||||
* **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.
|
||||
|
||||
Reference in New Issue
Block a user