chore(audio): Campaign A slice A6 — delete what retail does not have

Retail EoR has no music system: the linked winmm MIDI player has zero
callers, 'music' appears zero times in the 65 MB decomp, SoundType has no
music member, InitPrefs registers no music key, and the install ships no
music files. So PlayMusic/StopMusic/MusicVolume and the AudioSettings
Music knob are deleted rather than left as an API modelling dead code —
the string-keyed signature was the tell, since every other entry point is
DID-keyed. Old settings.json files carrying a 'music' key still load; the
reader ignores unknown keys and the next save drops it.

The Ambient slider is now surfaced, because slice A5 gave it something to
drive, and its default returns to retail's 1.0 from an invented 0.8 —
InitPrefs defaults every sound preference to unity. The panel rule is
unchanged: no slider that does nothing.

r05-audio-sound.md gets a SUPERSEDED banner naming its five wrong
sections (falloff, pan, voice pool, selection, music, ambient) so a
future reader reaches the lane notes instead of the Ghidra-era reads that
this campaign spent its first two slices undoing.

TS-9 re-scoped from 'any MP3 cue' to the measured blast radius: exactly 1
MP3 among 786 shipped waves, a ~2 s mono clip. Its original framing
assumed a music system that does not exist. The ADPCM count remains
unmeasured and is named as the open question.

Deferred deliberately: #321's sound-cache decode-dedup race. It is a
pre-existing concurrency flake rather than audio-parity behaviour, and
shipping a speculative fix to a race I have not reproduced is exactly the
shortcut this project's no-workarounds rule exists to prevent.

Campaign A is code-complete; the plan carries the closeout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-08 22:58:49 +02:00
parent 7c4dd1ade7
commit dd2cb92b99
13 changed files with 152 additions and 65 deletions

View file

@ -1,5 +1,35 @@
# R5 — Audio System Deep Dive
> **SUPERSEDED 2026-08-08 (Campaign A).** This document predates the named
> retail decomp and the byte-level verification pass; several of its
> load-bearing sections are wrong and were used to build code that had to be
> undone. Read the six lane notes instead —
> `docs/research/2026-08-08-audio-retail-*.md` — and
> `docs/plans/2026-08-08-audio-parity-campaign.md` for the summary.
>
> Specifically corrected:
>
> - **§5.1 falloff.** The `FUN_00550c30` reading is not retail's curve.
> `SoundManager::GetAttenuation` @ `0x00550020` is
> `g = dist < 5 ? vol : 25·vol/dist²`, clamped BEFORE one master multiply,
> then `ceil(20·log10 g)` with a hard 50 dB no-allocate floor (audible
> radius ≈94.2 m). Binary Ninja elides the x87 constants and prints `* 0f`;
> porting its rendering yields silence at every distance.
> - **§5.2 pan / §5.3 pool.** Pan is `(int)(15·sin Δbearing)`, truncating
> toward zero, dead centre inside 5 m, with no front/back and no elevation.
> The voice allocator is `PlaySoundInternal` @ `0x0054FEC0` and it evicts on
> DAT-authored float PRIORITY, never gain; `FUN_00550AD0` is a hash-table
> constructor.
> - **§4 selection.** Probability is a Bernoulli play/skip GATE at the play
> site, not a selection weight; selection is a uniform index over `(n1)`.
> - **§6 music.** There is no music system in retail EoR. The linked MIDI
> player has zero callers. The `PlayMusic` API this section inspired modelled
> dead code and was deleted at slice A6.
> - **§7 ambient.** Ambients are region-authored, per-LAND-CELL over a 3×3
> landblock ring, and play as re-fired one-shots off an absolute-deadline
> queue — not landblock-attached loops. Indoors is silent by design.
Ground-truth port plan for acdream's audio subsystem, derived from decompiled
`acclient.exe`, cross-referenced against ACE (server), ACViewer (dat loader),
holtburger (client protocol), AC2D (C++ client), and DatReaderWriter (dat