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:
sylph-decoder
2026-08-30 09:52:13 +00:00
parent 5f386111ad
commit cf6be709ab
4 changed files with 171 additions and 0 deletions

View File

@@ -2529,6 +2529,39 @@ whatever it returns — the same reason a truncated log and a t=0 render both lo
fine from inside. Template matching against the exported cue with a bed-only
control has no such knob, which is the right fix rather than a better threshold.
## 🟡 2026-08-30 (later) — the loop IS a runtime field. **Change nothing yet.**
`loop_start` / `loop_end` live in the **XMA decoder context**, set by
`XMASetLoopData`, and Xenia already logs them — no patch needed. Read from the menu:
| ctx | wave | loop_start | loop_end | loop_count |
|---|---|---|---|---|
| 0 | 3 876 864 B | 3 605 682 | 25 640 423 | 255 |
| 1 | 3 930 112 B | 3 539 158 | 26 216 351 | 255 |
✅ Also decoded: the stream plays from offset **32**, and `loop_start` is where it
returns *after* `loop_end` — so the first pass is longer than later cycles. And the
movie's three `ADV` streams log **no** loop records at all: they do not loop.
🔴 **But two of my predictions are refuted and there is a conflict I cannot
resolve**, so **do not re-author `loop_end_s` from this**:
* `loop_start` is **not ~0** — it is 11.6 % into the stream.
* A linear bits→seconds conversion gives **62.34 s** and **63.29 s** for two stems
that must stay sample-synchronous. 0.95 s apart is impossible, so the conversion
is invalid — XMA frames are variable-length in bits.
* That implies a cycle of roughly **[10 s, 72 s]**, against the **0.25 … 57.18 s**
my audio tracking reported. Both cannot be right.
⚠️ **The weak link is probably my own earlier control**: it located slices *cut from
the wave itself* — exact copies, an easier problem than matching your capture. A
control easier than the measurement does not bound its error.
**The 61.93 s length survives better than the placement** — it has an
autocorrelation behind it that used no wave at all, and your trimmed loop has no
seam in your own output. Keep what you shipped.
[`menu-bgm-loop-fields-conflict.md`](../re/structures/menu-bgm-loop-fields-conflict.md)
## ✅ 2026-08-30 — the menu BGM loops at **61.93 s**, and there is **no seam**
**Measured, 240 s parked on the menu.** Your authored loop is wrong in both

View File

@@ -0,0 +1,40 @@
# The XMA context's loop fields for the menu bed, read from the running game.
#
# 2026-08-30. run-canary --gpu=null --xma_param_probe=true --log_mask=13
# --log_level=3 (log_mask DISABLES categories: 13 = Kernel+Cpu+Gpu off, APU ON,
# and XELOGAPU is debug level, hence level 3). No Canary patch was needed --
# UpdateLoopStatus already logs these.
#
# Menu reached by the log oracle in 26.8 s; 45 s hold; 8 734 'Looped Data'
# lines, ALL of them after BGM_103's contexts appear. The movie's three ADV
# streams produce NONE, i.e. loop_count = 0 on them.
#
# ctx -> wave, from the probe's own byte_size:
# ctx0 packets=1893 byte_size=3876864 (wave 0)
# ctx1 packets=1919 byte_size=3930112 (wave 1)
#
# LOOP FIELDS (bit offsets; loop_count 255 = infinite):
# ctx0 loop_start = 3 605 682 loop_end = 25 640 423 len = 22 034 741
# ctx1 loop_start = 3 539 158 loop_end = 26 216 351 len = 22 677 193
#
# READ-OFFSET TRAJECTORY over the 45 s hold:
# ctx0 min 32 max 16 944 845 ctx1 min 32 max 17 165 054
# 20.0 % of samples are BELOW loop_start in both.
# Neither reaches loop_end, so NO WRAP was observed in this run.
#
# => playback starts at offset 32 (the first packet header) and runs forward;
# loop_start is where it returns AFTER loop_end. The first pass is longer
# than the cycles that follow.
#
# 🔴 A LINEAR bits->seconds conversion is INVALID. Applied to each stem with
# its own byte_size it gives:
# ctx0 62.34 s ctx1 63.29 s
# Two stems that play sample-synchronously cannot have loop durations 0.95 s
# apart, so the linearity assumption is refuted by the data itself. XMA frames
# are variable-length in bits.
#
# 🔴 AND IT CONFLICTS with the audio measurement. loop_start at 3.6 M of
# 31.0 M bits is 11.6 % in; linearly that is ~10 s, so the cycle would be
# roughly [10 s, 72 s] of the wave. The audio wave-offset tracking
# (menu-bgm-loop-measured.txt) put the observed offsets at 0.25 .. 57.18 s.
# Both cannot be right. Unresolved.

View 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.53.6 M bits, 11.411.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.

View File

@@ -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.