Measure the level off the whole disc, and find the headroom is not worth buying

FINDINGS 69, ROADMAP P6 -- the item 66.3 reopened in session 34 and sessions 35
and 36 both deferred.  The chip clamps its accumulator at 10 bits INSIDE the
recursion, and the ten seconds every audio figure in this tree is quoted on peak
at 435 of 511: it fits, and it fits by accident, because that window is a
-13.4 dBFS passage.  Nothing knew what the loudest passage of the game was.

tools/analysis/35_audio_level.py reads every stream of the unique scene footage
(00000-00201) through extract_audio.py's own chain -- 1,291.6 s, 201 of 202
streams -- and encodes windows of it with adpcm.CHIP.  The disc peaks at 946 of
2048 = -6.71 dBFS (00200 @ 2.11 s), which is 5.35 dB over the clamp, and the
census behind that peak is 687 samples of 20,182,000 (0.0034%) in 402 events,
44.0 ms, longest 0.90 ms.

THE HEADLINE IS A NEGATIVE: THE LEVEL DOES NOT CHANGE.  Forty 2 s windows drawn
over the game's timeline at six gains -- the disc's own level (gain 1.0) has the
best mean SNR (22.03 dB) and the best median, and loses the worst-window column
to -3 dB by 0.04 dB.  The gain that guarantees zero clamping disc-wide (0.5402)
costs 0.85 dB of mean SNR across the whole game to buy back 1.90 dB on the
2.11 s that clamp, because the OKI step table's floor is a constant 16 and does
not scale with the signal.

AND 66.3's MECHANISM DOES NOT SURVIVE A CONTROL.  Error after a clamp run is
elevated ~5x -- and so is the same window at a gain that never clamps, read at
the same indices, because those samples are simply loud.  Worst ratio 1.28 over
64 offsets, and the clamped encode's whole-window mean |error| is the LOWER of
the two (4.71 vs 5.05).  adpcm.encode runs the chip's clamp inside its own
sixteen-way search, so it never loses the chip's state.  The worry was right
about the mechanism and aimed one layer too late: an encoder clamping at 12 bits
while the chip clamps at 10 is exactly that divergence, and 66 closed it.

pack.py gains --audio-gain (default 1.0) so the level is a named parameter with
a measurement behind it instead of a shift buried in a list comprehension, and
prints the encoded window's peak against the clamp.  tmp/packed_singe.dlxp
rebuilds byte-identical, all 6,039,040 B.  New check.sh stage, ~18 s.

Three rig facts in 69.4, because a shipping encoder meets all three: 00176 has
no audio track at all; 00199 is 61.31 s of video with 1.25 s of audio; and 18
stream pairs share duration, peak and RMS, 7 of them byte-identical.

The 10-bit clamp is a DRIVER SETTING, not a chip constant -- x68k.cpp:1089 sets
OUTPUT_10BITS -- so it is MAME's reading of the board, and hardware item 5 is
what settles it.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-25 14:20:55 -07:00
parent 191f2b47bb
commit ab15c0749a
7 changed files with 885 additions and 5 deletions
+113
View File
@@ -1,3 +1,116 @@
# Status & next-session handoff — end of session 37 (2026-08-25)
## Session 37: the audio level is measured off the whole disc, and the headroom is not worth buying
**FINDINGS 69.** ROADMAP P6, the item 66.3 reopened in session 34 and sessions
35 and 36 both deferred. It needed no new machinery on the machine and it did
need the whole disc: `tools/analysis/35_audio_level.py` reads every stream of
the unique scene footage (`00000`-`00201`, FINDINGS 32.1) through
`extract_audio.py`'s own chain and encodes windows of it with `adpcm.CHIP`.
**What was open.** The chip clamps its accumulator at 10 bits INSIDE the
recursion, so anything the encoder is handed above 511 (12-bit units) is
unreachable. The ten seconds this tree gates everything on peak at 435 of 511 —
it fits, and it fits by accident, because that window is 13.4 dBFS. Nothing
knew what the loudest passage of the game was.
**What it measures.**
| | |
|---|---:|
| audio on the game's own footage | 1,291.6 s = 21.5 min, 201 of 202 streams |
| **disc peak** | **946 of 2048 = 6.71 dBFS** (`00200` @ 2.11 s) |
| the clamp | 511 = 12.06 dBFS — the disc is **5.35 dB over** |
| samples above it | 687 of 20,182,000 = **0.0034%** |
| ...in | 402 events, **44.0 ms**, longest 0.90 ms, median run 1 |
**THE HEADLINE, and it is a negative: the level does not change.** Forty 2 s
windows drawn over the game's timeline, encoded at six gains: the disc's own
level (`s16 >> 4`, gain 1.0) has the best mean SNR (**22.03 dB**) and the best
median, and loses the worst-window column to 3 dB by **0.04 dB**. The gain that
guarantees zero clamping disc-wide (0.5402 = 511/946) costs **0.85 dB of mean
SNR across the whole game** to buy back **1.90 dB on the 2.11 s that clamp**.
The OKI step table's floor is a constant 16 and does not scale with the signal,
so attenuating walks the programme down toward a quantiser that stays put.
**And 66.3's mechanism does not survive a control.** The worry was that a
clamped accumulator is a wrong state the next nibble is applied to. Error after
a clamp run IS elevated ~5x — and so is the same window at a gain that never
clamps, read at the same indices, because those samples are simply LOUD. Worst
ratio against the control over 64 offsets: **1.28**, and the clamped encode's
whole-window mean |error| is **lower** (4.71 vs 5.05). The reason is that
`adpcm.encode` runs the chip's clamp inside its own sixteen-way search, so it
always knows the state the chip is in, saturated included. **The worry was
correct about the mechanism and aimed one layer too late**: an encoder clamping
at 12 bits while the chip clamps at 10 — what this tree shipped until session
34 — is exactly the divergence described, and 66 already closed it.
**What changed in the tree.** `--audio-gain` on `pack.py` (default 1.0), which
makes the level a named parameter with a measurement behind it instead of a
shift buried in a list comprehension; `pack.py` now prints the encoded window's
peak against the clamp and the count above it (gate window: **435 of 511, 0
clamped**). `tmp/packed_singe.dlxp` was rebuilt and is **byte-identical** to the
one session 36 ran off a volume, all 6,039,040 B. A new check.sh stage (~18 s)
gates the disc peak and the clamp census.
**Three rig facts recorded in 69.4**, because a shipping encoder meets all
three: `00176` has **no audio track at all** (ffmpeg exits non-zero — "silent
clip" has to be distinguishable from "extract failed"); `00199` is 61.31 s of
video with 1.25 s of audio, which is 60 s of the 75 s gap between the disc's
1,366.6 s of footage and its 1,291.6 s of audio, and a cadence-driven container
computes its lump count from the FRAME count; and 18 stream pairs share
duration, peak and RMS, of which 7 are byte-identical audio (25.0 s, all of them
3.5 s clips — the reused death animations).
## HANDOFF — start here
**THE TREE IS ALL GREEN**, session 37's stage included.
### The work, in the order it should be done
**1. THE REFILL CLIMB WITH A SECOND CONSUMER, through a real branch point**
(51.3, 55.4). Now the oldest open item in P6 and unchanged by this session: the
slack table is in `32_audio_wire.py`, nothing has been run, and session 36 gave
it the player that holds two streams at once. Pointing it at a scene change is
the work; nothing has to be built.
**2. THE BUFFERING DEPTH, which is cheap.** `PG_ANBUF` is 3 and `PG_APRE` is a
mailbox. Two slots is one constant and one run, and it is 7,168 B of a machine
that has 99,328 B of record buffer to find elsewhere (68.6).
**3. THE SILENT-CLIP AND SHORT-AUDIO CASES (69.4).** Neither is hard and
neither has an answer in the tree: a scene whose audio is shorter than its
frames, and a scene with no audio track. Both are decided by what the lump
count is computed from, and both are cheap to get wrong quietly, which is this
format's whole failure mode (67.4).
### What is still BLOCKED, so it is not picked up by mistake
**K4 — the packed player that is on screen — is conditional on B2**, a board
question. **E7, E4 and C1** are parked (61.8), and **P4a's wiring** is parked
with the ring K3 deleted.
**The hardware list is unchanged and is the user's**: B1 (sustained AND the
data-phase BURST rate, 64.2 — acceptance is **589.6 KB/s**), B2 (blanking), B3
(`#EXREQ`), B4 (a byte write to a palette register), and session 34's fifth,
play a known nibble stream on a real MSM6258V and record the line out. **Session
37 sharpens the fifth**: the 10-bit clamp this whole session is about is a
DRIVER SETTING — `x68k.cpp:1089` calls `set_outbits(OUTPUT_10BITS)` — not a chip
constant, so it is MAME's reading of the board. If real silicon runs 12-bit the
disc clears the clamp by 6.7 dB and the answer here is unchanged and cheaper.
### Reproducing this session
python3 tools/analysis/35_audio_level.py --gate # ~18 s
python3 tools/analysis/35_audio_level.py --survey 40 --ladder --recover
# ~7 min: the ladder, the disc-wide survey and the clamp-recovery control
./tools/bench/check.sh # ALL GREEN
**WHAT IS NEXT.** The refill climb with the second consumer, through a real
branch point.
---
# Status & next-session handoff — end of session 36 (2026-08-25)
## Session 36: the container is played, and holding the bus costs the audio