Files
Sylpheed/docs/re/structures/menu-bgm-loop-fields-conflict.md
sylph-decoder cf6be709ab 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
2026-08-30 09:52:13 +00:00

3.8 KiB
Raw Blame History

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