Files
RP412/docs/SOUND.md
T
CydandClaude Opus 5 ce1b0ab9c3 Sounds fade, dull and doppler with distance again
The OpenAL port kept the whole authored audio model and then threw most of
its output away. Every frame the engine computed a distance-attenuation
curve, a high-frequency rolloff, doppler cents, a reverb level and a
front/rear placement, and every one of those consumers had been commented
out when the two AWE32 cards were replaced. What reached the speakers was
OpenAL's own defaults instead: a straight-line fade to silence, no
filtering, doppler at the wrong constants with an inverted velocity, no
reverb, and every cockpit sound dead centre.

Restored, per AUDIO.INI, which is byte-identical to the file that shipped
in August 1995:

  - the authored knee/rolloff distance curve, replacing AL_LINEAR_DISTANCE.
    This also un-blinds the transient cull, the voice-steal weighting and
    the mix ducking, which all key off it and were treating far sources as
    full presence
  - the CC7 squared volume law; writing the scale linearly ran everything
    about 6 dB hot at mid-scale
  - brightness and distance muffling, and the wet-exterior/dry-cockpit
    reverb split, both through a new OpenAL EFX bridge
  - doppler on the moving-source path only, as the original had it
  - front/rear placement from the authored position enum

The larger find is that AL_PITCH was never called anywhere in the tree, so
the entire pitch chain was inert - not only doppler but pitch_mix_offset,
which our own sequences author 97 times. Doppler alone would have changed
nothing audible.

Note pitch is applied for parity with the BT engine but is identity here:
our content predates NoteAudioControlID, so every source runs at note 60.

Builds clean on VS2022 Release|Win32. Smoke-tested against vRIO on COM1 -
reaches gameplay and holds a steady frame loop. ALC_EXT_EFX is present on
the build machine with all nine entry points, so the filter and reverb work
is live rather than inert. Not yet listened to on the pod, which is the
real test: the volume law changes the level of everything.

docs/SOUND.md documents the original two-card quadraphonic design, where
the surviving original assets are, and what remains.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 23:00:37 -05:00

24 KiB
Raw Blame History

Red Planet — the sound system, from two AWE32s to OpenAL

How a 1996 arcade pod produced true quadraphonic positional audio out of two consumer sound cards, what the modern port kept, what it silently dropped, and exactly where the original assets are.

Sources. The surviving engine in MUNGA_L4/ (the L4AUD* family) and the preserved hardware layer in MUNGA_L4/sos/; the complete original RP 4.10 C++ source and shipping assets in ../TeslaRel410/; and the BattleTech sibling tree ../BT411/, which shares this engine verbatim and has already fixed most of what's described here.

Companion docs: docs/audionotes.rtf (Stephen Baynham, 2007) covers the renderer's control flow — sources, sockets, the mix/running/dormant plugs. ../BT411/docs/AUDIO_FIDELITY.md is the 685-line fidelity audit this document maps onto RP. This doc covers the hardware model underneath both.

Two headlines:

  1. The quadraphonic engine is still in the tree, still runs every frame, and its output is discarded. Nothing was deleted in the port. Four channel gains and four time-delay offsets are computed for every sound in the world, then dropped, because the OpenAL back-end that replaced the sound cards never reads them.
  2. Red Planet's original soundbanks, authored sequences, source code and hardware configuration all survive in ../TeslaRel410/. Nothing about the original audio is lost. RP412 simply ships without them.
     source azimuth
          │
          ▼
 CalculateSpatialization()      ← quadrant pan + ITD, L4AUDIO.cpp:60
          │
          ├──► frontLeftScale / frontRightScale ─┐
          ├──► rearLeftScale  / rearRightScale   │   1996: CC7 volume to
          ├──► 4 × ITD delay targets             ├── 4 MIDI channels across
          └──► 4 × ITD pitch offsets (cents)     │   2 AWE32 cards
                                                 │
                                                 └── today: /* ... */ dead code,
                                                     OpenAL pans from AL_POSITION

1. Why two cards

AudioHardware held exactly two, named for what they drove (MUNGA_L4/L4AUDHDW.h:345-346):

AudioCard frontCard;
AudioCard rearCard;

The second card was not for extra voices. Each AWE32 gives you one stereo pair, and a four-corner speaker layout needs two. The allocator makes this explicit (MUNGA_L4/L4AUDRND.cpp:1309-1352): front-left and front-right channels are requested from front_card, rear-left and rear-right from rear_card. Either card refusing kills the whole allocation and the sound doesn't play.

Per card the engine assumed a stock EMU8000 (MUNGA_L4/L4AUDHDW.h:80-82):

Constant Value
AWE_VOICE_COUNT 32
AWE_CHANNEL_COUNT 16
AWE_PERCUSSIVE_CHANNEL 9

64 hardware voices total, 32 MIDI channels, two independent stereo outputs.

2. The hardware layer: HMI SOS

Everything went through Human Machine Interfaces' Sound Operating System, selected at compile time (MUNGA_L4/L4AUDHDW.h:87):

#define _MIDI_DRIVER_TYPE _MIDI_AWE32

MUNGA_L4/sos/ still carries the complete driver headers in both flavours the build needed — bc4/ for Borland C++ 4 and wc/ for Watcom — alongside SOSMAWE.C, whose header comment reads "Module to handle AWE32 .SBK file uploads." That file is the bank loader: sosMIDIAWE32SetSBKFile, sosMIDIAWE32ReleaseSBKFiles, sosMIDIAWE32NoteOn/NoteOff.

AudioCard also poked the hardware directly for MPU-401 UART setup — the _inp/_outp port macros and MPU_RESET_CMD/MPU_ENTER_UART at MUNGA_L4/L4AUDHDW.cpp:14-25 are still there.

2.1 The actual pod hardware configuration

Card addresses were parsed by GetEnvironmentSettings (MUNGA_L4/L4AUDHDW.cpp:269-350) out of a BLASTER-format string. The standard single BLASTER= variable can only describe one card, so each got its own (MUNGA_L4/L4AUDHDW.cpp:935-936):

frontCard.GetEnvironmentSettings(FRONT_CARD_ENV_VAR);
rearCard.GetEnvironmentSettings(REAR_CARD_ENV_VAR);

Those two macros are referenced in four places and defined nowhere in this tree. The values survive in the shipping release — ../TeslaRel410/ALPHA_1/REL410/RP/SETENV.BAT:

set AWE_FRONT=A220 I5 D1 H5 P330 T6
set AWE_REAR=A240 I7 D3 H6 P300 T6
Front card Rear card
Base I/O 0x220 0x240
IRQ 5 7
DMA (8-bit) 1 3
DMA (16-bit) 5 6
MPU-401 0x330 0x300
Type 6 6

Two fully independent SB16/AWE32s, non-conflicting across every resource — a genuinely awkward ISA configuration to get stable, which is presumably why SETENV.BAT hardcodes it rather than probing.

Two cards were mandatory, not optional. L4Application::MakeAudioRenderer returned NULL — no audio renderer at all — unless both variables were present (MUNGA_L4/L4APP.cpp:505-514, now commented out). There was no one-card or stereo fallback in the shipping build.

SETENV.BAT also drove the SB16 mixer per card via sb16set, and carries three details worth recording:

  • Master volume defaults to AWE_MASTER_VOLUME=200, overridable by an operator file c:\setvol.bat — the per-cabinet volume trim.
  • Intercom mode (L4INTERCOM=ON) swaps audio\ctmix.cfg for audio\icom.cfg and adds sb16set /li:220;0 on the front card only. Diffing the two configs (ALPHA_1/REL410/RP/AUDIO/), the only change is line-in routing: LIL+/LIR+ into the input and output paths. The intercom fed the front card's line input.
  • There are two :SOUNDCOMMON labels. DOS batch jumps to the first, so the second block — which trims the cards differently from each other (bass 245 vs 240, treble 110 vs 135) — is unreachable dead code. Someone tuned front and rear separately and it never shipped.

3. Nothing streamed — the game was a MIDI sequencer

There is no mixer and no audio thread in the original design. Sound effects were SoundFont samples resident in each card's onboard sample RAM, and playing a sound meant allocating a MIDI channel and sending note-on plus CC7 volume. The game drove two samplers in real time.

That is why both cards were loaded with identical banks (dist/AUDIO/AUDIO.INI):

[AudioResources]
front_audio_resource=audio\audio1.res
front_audio_resource=audio\audio2.res
rear_audio_resource=audio\audio1.res
rear_audio_resource=audio\audio2.res

Same content in both cards' RAM, so any sound could be placed anywhere in the ring without a reload. The cost is that the entire sound set had to fit twice over in AWE32 sample memory.

It also explains the shape of the whole audio API. AudioChannel exposes SendNoteOn, SendProgramChange, SendPitchBend, SendNRPN, SelectBank — a MIDI abstraction, not a sample-playback abstraction. Every positional and DSP decision the engine makes has to be expressed as a MIDI controller value.

4. The quad panner

L4AudioSpatialization::CalculateSpatialization(azimuth) (MUNGA_L4/L4AUDIO.cpp:60-290) is the whole positional model. It is character-for-character identical to the 1995 original at ../TeslaRel410/CODE/RP/MUNGA_L4/L4AUDIO.CPP:150-290, down to the // HACK comments.

Azimuth is rewrapped so 0° is dead ahead and the range is ±180°, then split into four 90° quadrants around azimuth_max = 45°:

                    front
              FL ─────┬───── FR
               │  Q1  │
               │      │
        Q2     │  ▲   │     Q4
      (left)   │  │   │   (right)
               │ +az  │
              RL ─────┴───── RR
                     Q3
                    rear

        +azimuth → left        azimuth → right

Within a quadrant it constant-power pans between the two bracketing speakers only — a source never feeds more than two of the four, which is correct for a four-corner layout:

tangent_ratio = (tan(azimuthOfSource) / tan(azimuth_max)) * 0.5f;

frontLeftScale  = Sqrt(0.5f + tangent_ratio);
frontRightScale = Sqrt(0.5f - tangent_ratio);

tangent_ratio runs ±0.5, so the gains trace sqrt(0.5±t) — sum of squares constant at 1.0, i.e. constant acoustic power across the sweep, no hole in the middle.

Quadrant Arc Active pair
Q1 45° … +45° front-left / front-right
Q2 +45° … +135° rear-left / front-left
Q3 ±135° … 180° rear-right / rear-left
Q4 135° … 45° front-right / rear-right

Q1/Q3 use tan(azimuth_max) as the half-width while Q2/Q4 use tan(DEG_90 - azimuth_max). With azimuth_max = 45° these are equal and all four arcs are 90°, but the code is written so front/rear arcs could be widened against the side arcs independently. azimuth_max is hardcoded with a // HACK - should come from audio.ini comment at L4AUDIO.cpp:103.

Q3 relies on tan having period 180° to handle the wrap at ±180° — for az < 135 the expression azimuthOfSource - DEG_180 goes below 315°, and the result is only correct because tangent is periodic. It works; it is not obvious.

5. The ITD trick

Amplitude panning alone gives direction but not much externalization. The engine also modelled interaural time difference — the sub-millisecond arrival-time gap between your ears that the brain actually uses to localize. AUDIO.INI:

distance_between_ears=12.0
itd_difference=0.0015

The problem: an EMU8000 has no delay line. You cannot ask an AWE32 to play a voice 1.5 ms late. There is no such MIDI message and no such hardware path.

The solution: don't delay the voice — detune it. To make a voice arrive progressively earlier or later, momentarily shift its pitch, which shifts its playback rate, which slides it through time. Return the pitch to normal and the voice stays there, phase-shifted. Doppler used as a phase-steering primitive.

CalculateSpatialization sets a delay target per channel; the caller converts the rate of change of that target into a cents offset (MUNGA_L4/L4AUDIO.cpp:414-438):

const Scalar itd_pitch_offset_constant =
    0.003831f / 0.000002f;              // period / delay

frontLeftITDPitchOffset =
    itd_pitch_offset_constant *
        (spatialization.frontLeftDelay - currentFrontLeftDelay) /
            (Scalar)itd_delta_time;
currentFrontLeftDelay = spatialization.frontLeftDelay;

Only one of the two active channels gets a nonzero delay target, scaled by the same tangent_ratio as the gain, so maximum offset at full pan is exactly itd_difference — 1.5 ms. The rear quadrant negates the sign (rearLeftDelay = -(itd_delay * tangent_ratio * 2.0f), L4AUDIO.cpp:214), flipping the lead/lag relationship behind the listener.

On the magic constant — a derivation, not something the source states. 0.003831 / 0.000002 = 1915.5 cents per unit of delay slew. The exact small-signal value for a Doppler-style rate-to-pitch conversion is 1200 / ln 2 ≈ 1731 cents. They agree within about 10%, which confirms the mechanism: a hand-tuned first-order approximation, presumably trimmed by ear on the pod.

Three further details:

  • Computed against real elapsed frame time (Now() - lastITDFrameTime), so the slew is framerate-independent.
  • Applied only while the target is moving. A stationary source contributes zero pitch offset and sits at whatever phase it reached.
  • distance_between_ears=12.0 is commented "-> average size of cockpit". BT uses 2.0. This is the clearest surviving fingerprint of the pod build: the head model was scaled to the physical cabinet, because the speakers really were in the corners around the player. Confirmed authentic — RP412's AUDIO.INI is byte-identical to the shipping 4.10 file dated 31 August 1995 (../TeslaRel410/ALPHA_1/REL410/RP/AUDIO/AUDIO.INI). Every tuning constant in this repo is the original; there has been zero config drift in thirty years.

6. The rest of the per-frame model

All of it expressed as MIDI, all driven from AUDIO.INI:

Effect Mechanism INI keys
Distance attenuation CC7 volume, knee + rolloff curve amplitude_rolloff, _knee, _distance_scale
Distance muffling AWE initial-filter-cutoff NRPN 21 (1008000 Hz) high_frequency_rolloff, _knee, _distance_scale
Doppler pitch bend in cents doppler_range, speed_of_sound
Reverb CC91 send, wet exterior / dry cockpit global_reverb_scale
Source compression gain curve on the summed mix compression_scale, compression_exponent
Clipping hard cull sphere clipping_radius

NRPN constants are still declared at MUNGA_L4/L4AUDHDW.h:63-68 (AWE_FILTER_CUTOFF_NRPN 21, AWE_VOL_ATTACK_TIME_NRPN 11, AWE_PITCH_NRPN 16).

AUDIOMR.INI is a shipped variant differing from AUDIO.INI in exactly one respect — compression is far more aggressive (compression_scale=0.1, compression_exponent=9.0 vs 0.92/8.5). Everything else is identical.

7. What the port did

Both trees replaced the AWE32/SOS back-end with OpenAL Soft, by commenting out rather than deleting. MUNGA_L4/L4AUDHDW.h is 530 lines of which the great majority is preserved-in-amber AWE code: AudioChannel, AudioCard and AudioHardware are entirely inside /* */. The quad CC7 volume switch survives the same way from MUNGA_L4/L4AUDIO.cpp:1964.

The replacement is SourceSet (MUNGA_L4/L4AUDHDW.h:9-13):

struct SourceSet
{
    int count;
    ALuint sources[5];
};

Four MIDI channels-per-sound became up to five OpenAL sources — one per sample zone in the preset, not one per speaker. Placement is handed to OpenAL via alSource3f(..., AL_POSITION, ...) (MUNGA_L4/L4AUDIO.cpp:1413).

The consequence is the first headline. CalculateSpatialization is still called every frame from UpdateSpatialModelImplementation, still computes four gains and four ITD pitch offsets. Every consumer of those values is commented out.

8. Where the original assets actually are

RP412 ships 223 loose .wav files loaded through libsndfile, a hand-maintained preset table in MUNGA_L4/L4AUDLVL.cpp + WTPresets.cpp, and a 1-byte stub AUDIO.RES. The front_audio_resource/rear_audio_resource lines in AUDIO.INI are not stale leftovers — they are the original authored configuration, and the banks they name exist. They were simply not carried into dist/.

Everything below is verified present in ../TeslaRel410/:

Asset Location Detail
RP soundbanks ALPHA_1/REL410/RP/AUDIO/AUDIO1.RES, AUDIO2.RES Genuine SoundFonts (RIFF…sfbk), 3,781,754 B (Oct 1996) and 3,708,348 B (May 1996)
Earlier bank revision sda4/RPLIVE/AUDIO/ Nov 1995 / Oct 1995; AUDIO1 differs by 4 bytes
Authored sequences sda4/RPLIVE/AUDIO/*.SCP 70 files including STATIC.SCP
Sequences (partial) CONTENT/RP/AUDIO/*.SCP 62 files, no STATIC.SCP
Original C++ source CODE/RP/MUNGA_L4/L4AUD*.CPP Complete pre-port DOS source
Hardware config ALPHA_1/REL410/RP/SETENV.BAT The AWE_FRONT/AWE_REAR strings in §2.1
Mixer configs ALPHA_1/REL410/RP/AUDIO/CTMIX.CFG, ICOM.CFG Normal and intercom routing

Three things this settles:

  1. RP's banks are its own. MD5s differ from BT's, which are byte-identical between TeslaRel410/ALPHA_1/REL410/BT/AUDIO/ and BT411/content/AUDIO/ — so the provenance chain is proven on the BT side, and RP's distinct content is sitting unused.
  2. The .SCP files are build-time sources, not runtime assets. CreateStaticAudioStreamResource (MUNGA_L4/L4AUDRES.cpp:769) is called only from the asset tool (MUNGA/TOOL.cpp:100), which compiles them into RPL4.RES. RP412 ships a working RPL4.RES, so the authored audio objects are present — what's missing is the editable source form, now recovered.
  3. RP has a reference BT lacks. BT411's audit had to Ghidra-decompile BTL4OPT.EXE to confirm F4, F9, F10, F11 and F12. For Red Planet the actual C++ source exists, so every one of those can be verified directly rather than inferred.

9. Fidelity gaps — the BT411 audit mapped onto RP412

BT411's audit graded its OpenAL port across 23 findings and has since fixed most of them. Its sections C and D (dead attribute bindings, ReportLeak, torso-twist servos) are BattleTech-entity-specific and do not transfer. Its synthesis and spatial findings do.

Every gap below was re-verified against RP412's own code, not assumed. The comment-block state of each cited line was checked programmatically.

Engine-side — asset-independent

Status: all fixed (2026-08-05). Line references are to the pre-fix tree.

# Gap Evidence found in RP412 What landed
F3 Authored distance curve computed then discarded; AL_LINEAR_DISTANCE used instead volume_scale *= GetDistanceVolumeScale() commented at L4AUDIO.cpp:1449; alDistanceModel(AL_LINEAR_DISTANCE) live at MUNGA/AUDIO.cpp:97; AL_MAX_DISTANCE written at :1081,1416,1941 alDistanceModel(AL_NONE); multiply restored on Dynamic3D; new Static3DPatchSource::CalculateSourceVolumeScale override; the three AL_MAX_DISTANCE writes dropped
F4 Volume written linearly where the original used the CC7 squared law three live alSourcef(..., AL_GAIN, volume_scale) at L4AUDIO.cpp:1082,1414,1939 AL_GAIN, volume_scale * volume_scale at all three
F9 Brightness / HF-rolloff chain dead GetHighFreqCutoffScale() had zero callers new L4AUDEFX lowpass: Dynamic3D takes HF-rolloff × brightness, Static3D and Direct take brightness alone
F10 Doppler wrong constants and wrong sign alDopplerFactor(0.3f); GetDopplerCents() zero callers alDopplerFactor(0.0f) + pitch_offset += GetDopplerCents() on the dynamic path only
F11 Reverb wet-exterior / dry-cockpit split dead CC91 sends commented at L4AUDIO.cpp:1227,1717 EFX EAXReverb aux slot at global_reverb_scale; sends attached on Dynamic3D/Static3D, Direct left dry
F12 Direct placement dead — everything dead-centre all three switch (audioPosition) blocks commented AL_POSITION written per the authored enum after SetupPatch
P1 AL_PITCH never called anywhere in the tree relativePitch computed at :1034,1408,1922 and discarded at all three pitch applied at all three sites
F22 Quad + ITD model dead §45 above still open — needs multichannel output (§10 step 4)

P1 is an RP-specific find with no BT counterpart, and it is larger than F10 alone. RP412 had no AL_PITCH call at all, so the entire pitch chain was inert — not just doppler but pitch_mix_offset / PitchAudioControlID, which RP's own sequences author 97 times. Fixing F10 without this would have changed nothing audible.

A note on note-pitch: BT411 applies 2^((note-60)/12), because its SF2-derived presets carry authored key-splits. RP is different — SAMPLEINFO has no root-key field, and RP's authored content predates NoteAudioControlID entirely (its AudioControlID enum stops at AttackTimeAudioControlID), so every source runs at DEFAULT_NOTE=60 and the factor is identically 1.0. It is applied anyway for engine parity, clearly marked as inert for current content.

F3 was the highest-leverage single change: restoring the authored curve also repairs the distance-blind transient cull, the voice-steal weighting, and the mix-ducking chain, all of which were treating far sources as full-presence.

F22 is the one where RP is the lead repo rather than the follower. BT411 classes it low-priority because it "matters mostly for pod-hardware target" — which is precisely what this project is.

Verified on this machine: ALC_EXT_EFX is present and all nine EFX entry points resolve, so F9/F11 are live rather than silently inert. The driver grants 256 mono sources — OpenAL Soft's default budget, which BT411 raised explicitly via context attributes. RP412 still accepts the default; worth revisiting if voice starvation shows up in a busy match.

Asset-side — unlocked by §8, blocked until the banks are wired in

These are all bank-derived, so they cannot even be assessed against RP412's flat WAV set. Prevalences are BT's; RP's own numbers need measuring once its banks are parsed.

# Gap What is lost
F1 Multi-zone preset collapse The extractor keeps only the first sample-bearing zone: key-splits, layers and stereo pairs dropped. In BT, 68/115 and 94/126 presets are multi-zone
F2 Root-key and tuning metadata dropped Everything plays as if rooted at MIDI 60. In BT, ~83% of presets land ≥1 semitone off, worst 36 st. Fix is algebraically exact: bake tuning into each WAV's declared sample rate
F13 Loop regions and release envelopes Whole-buffer looping instead of authored sub-regions; instant cuts where 1.13.9 s releases were authored
F14 Per-zone generators initialAttenuation (inverted scale in SBK: 127 = full volume), initialFilterFc/Q, volume envelopes — SAMPLEINFO has no fields for any of it

Ordering hazard, inherited from BT's F13: loop-region support must ship with or before multi-zone extraction. Some layer zones carry loop regions covering as little as 1.5% of the sample; whole-buffer looping over those would replay an entire explosion on every cycle.

Tooling already exists — ../BT411/tools/sf2extract.py — but note it is the source of F1 and F2 in its current form. It needs the multi-zone and tuning fixes before being pointed at RP's banks.

10. A recovery path, in order

  1. Engine-side fidelity first. Done (2026-08-05). F3, F4, F9, F10, F11, F12 and P1 all landed; L4AUDEFX.cpp/.h ported and added to Munga_L4.vcxproj. Builds clean on VS2022 Release|Win32; smoke-tested against vRIO on COM1 with RP412STEAM=0 — reaches gameplay and holds a steady frame loop. Not yet listened to on the pod, which is the real acceptance test: F4 in particular changes the level of everything.
  2. Wire RP's banks in. Copy AUDIO1.RES/AUDIO2.RES from ALPHA_1/REL410/RP/AUDIO/ into dist/AUDIO/ — the AUDIO.INI already names them. Fix sf2extract.py for multi-zone (F1), tuning (F2) and loop regions (F13) before regenerating, then rebuild the preset table.
  3. Recover the .SCP sources from sda4/RPLIVE/AUDIO/ into the asset pipeline, so authored audio becomes editable again rather than frozen in RPL4.RES.
  4. Then quad. With the above in place:
    • Ask ALC for a multichannel format instead of accepting the stereo default (MUNGA_L4/L4AUDRND.cpp:380).
    • Place four AL_SOURCE_RELATIVE sources at fixed corner positions and drive their AL_GAIN from the existing GetFrontLeftScale() family, bypassing OpenAL's panner.
    • Feed the ITD offsets to AL_PITCH — or implement a real fractional delay, which a software mixer can do and the EMU8000 could not. The detune path is already written and is the authentic behaviour.
    • Re-derive azimuth_max from the actual cabinet speaker angles instead of the hardcoded 45°.

Steps 1 and 2 are where nearly all the audible improvement is. Step 4 is what made the pod feel like the sound was in the room with you.

11. Verifying any of this

File What's in it
MUNGA_L4/L4AUDHDW.h AWE/MIDI constants, AudioCard/AudioHardware (commented), SourceSet
MUNGA_L4/L4AUDHDW.cpp MPU-401 port I/O, BLASTER parsing (:269), card init (:935)
MUNGA_L4/L4AUDIO.cpp CalculateSpatialization (:60), ITD pitch (:414), dead quad CC7 path (:1964)
MUNGA_L4/L4AUDRND.cpp renderer, OpenAL init (:380), dead quad channel allocator (:1309)
MUNGA_L4/L4AUDRES.cpp resource manager, WAV → AL buffers, SCP compile path (:769)
MUNGA_L4/L4APP.cpp:505 the dead two-card gate on renderer creation
MUNGA_L4/sos/ HMI SOS driver headers (bc4 + wc), SOSMAWE.C bank uploader
dist/AUDIO/AUDIO.INI every tuning constant — byte-identical to the 1995 original
../TeslaRel410/CODE/RP/MUNGA_L4/ the original DOS source, for anything the comments don't answer
../BT411/docs/AUDIO_FIDELITY.md the full 23-finding audit this section maps from

The commented-out regions are a faithful copy of the original — verified against TeslaRel410/CODE/RP/MUNGA_L4/L4AUDIO.CPP, which matches character-for-character in the spatialization and ITD paths. They were preserved deliberately and they describe exactly how the pod's audio hardware was driven.