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:
+113
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user