Put sound on the wire, and find three LSBs are worth 25 dB

ROADMAP P6, everything in the item except the bus half session 20 closed.
tools/encoder/adpcm.py is an MSM6258 codec, tools/encoder/extract_audio.py
takes the same seconds of the same stream the frames come from,
tools/bench/verify_adpcm.py is the gate, tools/analysis/32_audio_wire.py the
container arithmetic.

There is no reference encoder -- ffmpeg has a decoder for this format and none
the other way -- so what is gated is the decoder the encoder runs INSIDE its
own nibble search, sample-exact against ffmpeg's over 4,268 nibbles. An
encoder that agrees with its own wrong decoder is what that catches. The Singe
window: 156,250 samples -> 78,125 B at 21.97 dB, which is 7,812.5 B/s to the
byte. Normalising the disc's -13.4 dBFS level moves the SNR 21.97 -> 21.97, so
the level is not a lever.

And the two published delta formulas are not the same codec. They differ by at
most 3 in 12-bit units; encode for one and decode on the other and the SNR
goes 21.97 -> -2.88 dB, the noise louder than the signal, because ADPCM is
recursive the way the video codec is temporally recursive. Which one the chip
runs is now P6a and it is a precondition on shipping any audio.

And audio is the first thing the packed branch's simplification has cost
anything for. A record has no index BY DESIGN, so audio cannot be per-record
without making records variable; it rides a fixed cadence (F, A), the obvious
F=1 wastes 57.3% of every audio sector, and the pick is F=11 A=14 -- 0.09%
padding, 14,336 B held, wire 582.0 -> 589.6 KB/s. The codec container, which
kept its index, pays zero.

The MAME experiment did not work and 65.5 says so: :okim6258 is there at
$E92001/$E92003, read out of the machine's own program map, and feeding it
from Lua recorded silence across control 0..3 x port C 0..15. The register
semantics were not guessed at further.

FINDINGS 65. check.sh ALL GREEN before and after, with a new stage.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-25 09:11:03 -07:00
parent 6f698ca226
commit f925a1dd9a
12 changed files with 1069 additions and 6 deletions
+30 -1
View File
@@ -339,6 +339,34 @@ from taking. It believed it was at 12 fps; the screen was at **6.37**. Only the
host's raster count contradicts it, and the gate asserts on the difference
(FINDINGS 64.3).
**And the sound has an encoder, whose most useful output so far is a warning.**
The X68000's audio is an OKI MSM6258 — 4 bits a sample, 15,625 of them a second,
7,812.5 bytes a second exactly. `tools/encoder/adpcm.py` encodes the same ten
seconds the frames come from: **78,125 B at 21.97 dB**. There is no reference
encoder to check it against — ffmpeg has a decoder for this format and no encoder
— so what is gated is the decoder the encoder runs *inside its own nibble
search*, sample-exact against ffmpeg's over 4,268 nibbles. An encoder that agrees
with its own wrong decoder is what that catches.
**And the two published versions of this codec are not the same codec.** ffmpeg
computes a nibble's contribution as `((2*(n&7)+1) * step) >> 3`; the OKI
datasheet truncates per term. They differ by **at most 3 in 12-bit units**.
Encode for one and decode on the other and the signal-to-noise ratio goes from
**21.97 dB to 2.88 dB — the noise comes out louder than the signal**, because
ADPCM is recursive and a rounding difference does not stay where it happens. So
which one the chip runs is not a footnote; it is a precondition on shipping any
audio at all, and MAME's x68000 has the chip to ask (FINDINGS 65).
**And audio is what the packed container's best property finally costs
something for.** A packed record is 97 sectors and its address is arithmetic —
no index, and none can be needed. Audio is 651.0417 bytes a frame slot, a rate
with no arithmetic relationship to 12 fps, so it cannot ride the record without
making records variable length and bringing an index back. It rides a fixed
cadence instead — every 11 frames, 14 sectors — which wastes **0.09%**, where
the obvious one-lump-per-record cadence wastes **57.3%** of every audio sector.
The wire goes **582.0 → 589.6 KB/s**. The codec container, which already has the
index the packed one deleted, pays **zero**.
**The scene graph is in, and the worst gap between two decision points is
zero.** `tools/import/scenegraph.py` imports the arcade scene graph — 40 scenes,
516 sequences, 906 input windows — and 5.4% of the game's 612 branch transitions
@@ -371,7 +399,8 @@ cores, the ring and paced-ring passes, the DMAC configuration gate and the
load-time transforms on both cores, then imports and gates the scene graph
when a DirkSimple checkout is present, then builds the packed container and
renders it through px68k's own GVRAM model, then **runs the packed player for
120 frames off a real volume and compares every one of them**, then prints
120 frames off a real volume and compares every one of them**, then encodes the
same window's audio and gates it against ffmpeg's decoder, then prints
`ALL GREEN`.
## Reproducing this