Vendored 86Box EMU8000 core (GPL-2, emu8k.cpp/.h + shim) plus vweawe.cpp: two cards at the production addresses (0x620/0x640 register triplets), rear-card DSP/mixer stub at 0x240, synthesis on an autonomous render thread with direct winmm output so sustained voices ride through emulation-thread stalls (RIO staging retries) like the real silicon did. WC sample-counter interpolation between render chunks keeps HMI SOS busy-waits fast. VWE_AWE32/VWE_AWE_ROM/VWE_AWE_RAM_KB/VWE_AWE_SHIFT/ VWE_AWE_LEAD_MS/VWE_AWE_DUMP/VWE_AWE_LOG env knobs. emulator/roms: the AWE32 1MGM GM ROM dumped from our own pod card (SF2) plus the reconstructed raw image the emulated EMU8000 loads. The HMI driver refuses the AUDIO*.RES SoundFont upload without it (banks declare irom=1MGM) -- that was the root cause of the first silent runs; full debug chain in SOUND-NOTES.md. Repo serves the pod-owner community only. .gitignore: drop the ROM excludes and un-ignore ALPHA_1/ and sda4/ (committed separately). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
170 lines
9.7 KiB
Markdown
170 lines
9.7 KiB
Markdown
# Sound subsystem — recon & plan
|
||
|
||
Goal: bring the pod's audio up in the DOSBox-X fork, faithful to the
|
||
production hardware: **two AWE32 ISA cards driving four cockpit speakers**
|
||
(front pair + rear pair).
|
||
|
||
## What the production system actually was (recon 2026-07-04)
|
||
|
||
- **Cards** (`REL410/BT/SETENV.BAT:158`, `VGL_LABS/PARAMETR.BAT:172`):
|
||
- `AWE_FRONT = A220 I5 D1 H5 P330 T6`
|
||
- `AWE_REAR = A240 I7 D3 H6 P300 T6`
|
||
Sound is enabled by SETENV **arg 3 = `s`** (`n` = NOSOUND — our current
|
||
emulator confs run `call setenv.bat r s n g`, i.e. sound explicitly OFF).
|
||
Mixer preset via `c:\sb16\sb16set` + `audio\ctmix.cfg` (not in the ALPHA_1
|
||
image — lived on the pod's local C:); `icom.cfg` variant opens **line-in
|
||
on the front card** when `L4INTERCOM=ON` (analog intercom mix).
|
||
`AWE_MASTER_VOLUME` / `c:\setvol.bat` = per-pod volume.
|
||
|
||
- **The game does NOT stream PCM through the SB16 DSP.** The audio engine
|
||
(`CODE/RP/MUNGA_L4/L4AUD*.CPP|HPP`, shared MUNGA "Audio Manager" brick,
|
||
1995) is **MIDI-only wavetable synthesis**:
|
||
- HMI **Sound Operating System** MIDI layer (`sosm.h`,
|
||
`sosMIDISendMIDIData`), `_MIDI_DRIVER_TYPE _MIDI_AWE32`, driver **linked
|
||
into BTL4OPT.EXE** (`sosMIDIInitDriver` string present; no external
|
||
.MDI).
|
||
- Each `AudioCard` parses its BLASTER-style env var (`AWE_FRONT` /
|
||
`AWE_REAR` -> srcAddx/irq/dma/**mpuAddx**/**emuAddx**) and drives the
|
||
card's **EMU8000** sampler: note on/off, program change, pitch bend,
|
||
and the AWE NRPNs (pitch 16, filter cutoff 21, vol attack 11). 32
|
||
voices, 16 channels per card. `LoadSBK()` uploads SoundFont banks into
|
||
card sample DRAM.
|
||
- `AudioHardware` = `frontCard` + `rearCard`.
|
||
- **All sound content is SoundFont banks**: `REL410/BT/AUDIO/AUDIO1.RES`
|
||
(3.7MB) and `AUDIO2.RES` (3.4MB) are RIFF `sfbk` (SBK/SF1, `isng` =
|
||
EMU8000) — renamed SoundFonts. `AUDIO.INI [AudioResources]` loads BOTH
|
||
into EACH card. Both banks per card ⇒ >7MB samples ⇒ **the pod AWE32s
|
||
had DRAM upgrades** (stock is 512KB); emulate with 8MB.
|
||
- **3D audio is computed in software** by the game's AudioRenderer
|
||
(`AUDIO.INI [AudioRenderer]`): amplitude + HF rolloff with knees, doppler
|
||
(speed_of_sound 250), **ITD 1.5ms**, source compression, reverb scale —
|
||
rendered by steering wavetable voices per ear across the front/rear
|
||
cards. Quad positional audio, 1995.
|
||
- **HARDWARE CONFIRMED (Cyd, 2026-07-04)**: pulled a card from a cockpit —
|
||
they are indeed **AWE32s, each with 2× 30-pin memory SIMMs** fitted (the
|
||
predicted DRAM upgrade; both banks per card ⇒ likely 2×4MB SIMMs = 8MB).
|
||
Also confirmed: **the intercom hardware was removed from the cockpits**
|
||
at some point — the intercom path (line-in mix, `icom.cfg`,
|
||
`L4INTERCOM`) is documented here for the record but will NOT be
|
||
restored; plan item 8 is closed as won't-do (mic/comms, if ever wanted,
|
||
would be a new host-side feature, not a restoration).
|
||
- **Period test suite exists**: `VWETEST/VGLTEST/` — TEST.BAT, SETENV1-9,
|
||
and diagnostic SBKs (PINKNOIS pink noise, SPKPLACE/SPKBASE placement,
|
||
QCHNTSTF/QCHNTSTB quad front/back, phase tests) + PINKNOIS.TXT
|
||
procedure. This is VWE's own speaker validation battery — use it to
|
||
validate the emulation before the game.
|
||
|
||
## The gap
|
||
|
||
DOSBox-X emulates one SB16 (DSP/mixer/OPL) and an MPU-401 — but **no
|
||
EMU8000**, and no second card. The game's entire audio path is EMU8000
|
||
register programming at `base+0x400/+0x800/+0xC00` (0x620/0xA20/0xE20 and
|
||
0x640/0xA40/0xE40). Nothing will sound until the EMU8000 exists.
|
||
|
||
## Plan
|
||
|
||
1. **EMU8000 core in the fork** (the big rock). Port the emu8k core from
|
||
86Box (GPL-2, same license as DOSBox-X; mature implementation incl.
|
||
sample-RAM read/write via SMALR/SMALW, 32 voices, filters, envelopes,
|
||
chorus/reverb). Two instances (front/rear), I/O hooks at both cards'
|
||
EMU8000 port triplets, 8MB sample DRAM each, each mixed into its own
|
||
DOSBox mixer channel (`AWEFRONT`, `AWEREAR`) at 44.1kHz. Config via env
|
||
or conf section, following the vpxlog device pattern.
|
||
2. **Second-card detection plumbing.** Keep DOSBox-X's native SB16 as the
|
||
front card — conf `[sblaster] sbtype=sb16 irq=5 dma=1 hdma=5` to match
|
||
`I5`. Add a minimal stub at 0x240 (DSP reset/version 4.xx + mixer
|
||
register file) so probes/sb16set succeed for the rear card. No DMA
|
||
audio needed anywhere (MIDI-only engine; SB IRQ/DMA unused — I5/I7
|
||
conflicts are a non-issue in emulation).
|
||
3. **Source verification pass** (before/while coding): read
|
||
`L4AUDIO.CPP`/`L4AUDRND.CPP`/`L4AUDRES.CPP` for the SOS init sequence
|
||
(timer rate, card detection order, failure modes), the exact SBK load
|
||
path, and what `InitMIDIReceive`/`GetMIDIByte` (MPU input!) is used for
|
||
in pod mode — open question.
|
||
4. **Enable sound**: gauge confs arg3 `n`→`s`. sb16set calls will error
|
||
harmlessly (not in image) — emulated mixer defaults stand in; optionally
|
||
add period SB16 utils to a `SB16/` dir later for authenticity. Do NOT
|
||
flip the flag before step 1 lands (unknown hang risk probing absent
|
||
EMU8000 — or do one throwaway run first to record the failure mode).
|
||
5. **Validate with VGLTEST** in the emulator: pink noise per speaker, quad
|
||
front/back tests — the way VWE techs did it.
|
||
6. **In-mission validation**: arena1 — engine pitch vs throttle (NRPN),
|
||
weapons, front/rear pan while turning past sources. Archive captures via
|
||
DOSBox-X mixer WAV record.
|
||
7. **Quad host output (cockpit build)**: start with front+rear summed to
|
||
stereo; then map to a 4.0 SDL output or send the rear card's channel to
|
||
a second host audio device for the physical rear pair.
|
||
8. ~~Intercom~~ — **closed, won't-do**: the intercom hardware was removed
|
||
from the cockpits (confirmed 2026-07-04). Remaining deferred items:
|
||
MPU MIDI-receive consumer, AUDIOMR.INI (mission-review audio profile)
|
||
differences.
|
||
|
||
## STATUS 2026-07-04: FIRST SOUND ACHIEVED (steps 1-4 done)
|
||
|
||
Implemented in the fork: vendored 86Box EMU8000 core (`emu8k.cpp/.h`, GPL-2)
|
||
+ `vweawe.cpp` glue (two cards at 0x620/0x640 triplets, per-card mixer
|
||
channels AWE32F/AWE32R, 0x240 DSP/mixer stub, `VWEAWE_Init` from sdlmain,
|
||
env: VWE_AWE32=1, VWE_AWE_RAM_KB, VWE_AWE_ROM, VWE_AWE_SHIFT, VWE_AWE_DUMP
|
||
(s16 stream dump), VWE_AWE_LOG=1 (port trace + 10s activity reports incl.
|
||
smldW upload counter + active voices + peak)).
|
||
|
||
Debug chain that got to sound, for the record:
|
||
1. Game run: SOS driver alive, EMU8000 detected, DRAM probe PASSES
|
||
(FFFF/AAAA/5555 bank scan verified in the SM trace), notes played
|
||
(voices>0) but **smldW=99** -- SoundFont upload never ran, voices played
|
||
zeroed DRAM = silence.
|
||
2. AWEUTIL /S (production boot runs it per card, PARAMETR.BAT:181-186)
|
||
hung polling WC: first lead. WC itself verified good after adding
|
||
block-interpolation (emu8k_shim_wc_extra; killed the 268M-poll storm).
|
||
3. Root cause: the banks declare `irom=1MGM` and the HMI AWE32 driver (and
|
||
AWEUTIL) verify the GM ROM before accepting SBKs. Emulated cards had a
|
||
zeroed ROM -> silent rejection.
|
||
4. Fix: reconstructed a 1MB ROM image from a user-supplied 1MGM.SF2 (smpl
|
||
chunk 1,047,588 B end-aligned into 1MB + ID words 0x314D/0x474D at word
|
||
offsets 2/3) -> `VWE_AWE_ROM=<path>` -> **both cards uploaded 3,513,581
|
||
sample words (full 7MB) and the mission plays real audio on both
|
||
channels** (front peak 11.8k, rear hit full-scale 32768).
|
||
|
||
**Architecture rev 2 (same day): autonomous synth thread.** User feedback:
|
||
sound great, but RIO stalls (staging-phase drops pre-date sound; the game
|
||
can't service the board while loading — button-press resync is the player
|
||
ritual) made audio chop, because DOSBox's mixer renders on the emulation
|
||
thread. Fix: synthesis moved to a dedicated render thread with its OWN
|
||
winmm output (10ms slots, VWE_AWE_LEAD_MS queue, default 80ms), EMU8000
|
||
state guarded by one critical section against guest port I/O. Like the
|
||
real silicon, sustained voices keep sounding through emulation-thread
|
||
stalls. Both cards sum to one stereo stream for now (user runs a headset);
|
||
per-card devices for the 4-speaker cockpit later. Note: our audio no
|
||
longer routes through the DOSBox mixer (its volume controls don't affect
|
||
it). Also: first render-path rev needlessly memset 64KB/card/ms — fixed
|
||
(clear only consumed region) before the thread move; both changes reduce
|
||
emulation-thread load vs the first-sound build.
|
||
|
||
Follow-ups:
|
||
- ROM provenance settled: `emulator/roms/1mgm.sf2` was dumped from one of
|
||
OUR OWN pod AWE32 cards; the repo serves the pod-owner community only,
|
||
so it is committed along with the reconstructed `emulator/roms/awe32.raw`
|
||
(recipe: SF2 smpl chunk end-aligned into 1MB + ID words 314D/474D at
|
||
word offsets 2-3). Point `VWE_AWE_ROM` at `emulator\roms\awe32.raw`.
|
||
The reconstruction may misalign ROM-resident GM presets (VWE banks are
|
||
RAM samples, unaffected) -- a raw linear re-dump of the ROM chips would
|
||
perfect it if GM playback ever matters.
|
||
- Rear card hit peak 32768 = clipping; consider VWE_AWE_SHIFT=1 or mixer
|
||
channel trim once real gameplay levels are assessed.
|
||
- Upload pacing is ~32K words/s (driver polls between words) -> adds ~2min
|
||
per card to boot. Optimization candidate later.
|
||
- Re-enable `aweutil /s` in the boot sequence now that a ROM exists
|
||
(currently skipped in the scratch conf) -- production authenticity.
|
||
- Promote the scratch gauge_sound.conf changes into the repo gauge confs
|
||
once validated: [sblaster] irq=5, production DIAGNOSE(+AWEUTIL) init
|
||
block, setenv arg3 `s`.
|
||
|
||
## Risks / notes
|
||
|
||
- emu8k port: integration surface is small (ISA I/O + mixer callback), but
|
||
verify the driver's DRAM size probe and chip ID/revision reads pass.
|
||
- HMI SOS hooks the PIT for MIDI timing — routine under DOSBox.
|
||
- Ran fine under 32RTM/DPMI on real pods, so no new protected-mode risk.
|
||
- RP (Red Planet) uses the same MUNGA audio brick and its own AUDIO*.RES —
|
||
everything here carries over.
|