diff --git a/context/wintesla-port.md b/context/wintesla-port.md index 0260f8d..269263a 100644 --- a/context/wintesla-port.md +++ b/context/wintesla-port.md @@ -16,6 +16,21 @@ on top of it. Full detail: `docs/PROGRESS_LOG.md §5b, §8`. ## What WinTesla already did (do NOT rebuild) - **Renderer bypass: DONE** — `MUNGA_L4/L4D3D.cpp` + `DXUtils` replace libDPL / the IG board with Direct3D9. No `libdpl.lib` in the tree. (The early from-scratch D3D9 viewer was retired and removed.) [T1] +- **Audio SOURCE BUDGET — dropouts in heavy combat, FIXED 2026-07-28** [T2]. Field report "audio + cutting in and out toward the end of the match"; night-5 logs carried **2017 `ACQUIRE FAILED` lines in + a single match, every one at `live=256`**. It is **NOT a leak** — the census sits at a healthy 45-60 + live, spikes during firefights, and drains straight back, so the engine's priority steal loop + (`AudioSourceStop`/`SuspendMaintenance` → `ReleaseChannels` → `ReleaseSourceSet` → + `alDeleteSources`) works. The cause: `L4AudioRenderer` called `alcCreateContext(device, NULL)` with + **no attribute list**, so OpenAL Soft applied its DEFAULT budget of 256 mono sources — an OpenAL + default, unrelated to the 1995 audio hardware. A busy match logs ~7200 explosions × 3 + `DPLIndependantEffect` voices each; while the pool is pinned every acquire fails and that sound is + dropped outright. FIX: pass `ALC_MONO_SOURCES` (default **1024**, `BT_AUDIO_SOURCES` overrides) and + **read back what was granted** — a request is not a promise. Measured: 1024→1024, 2048→2048, and + 64→**240** (OpenAL Soft's own floor, which is why the NULL default landed at 256). Observed bursts + peaked at 226 against the old 256 cap, i.e. they were clipping the ceiling. Note the earlier + 2026-07-23 fix (`a999e5c`, per-source delete instead of the spec-atomic bulk `alDeleteSources`) was + real and is still needed — it is what makes the pool drain; it just was not the ceiling problem. - **Audio: BACKEND now REAL + soundbank loaded; only game-triggering remains** [T2, updated 2026-07-15]. OpenAL replaces the HMI "SOS" engine and the renderer→device→buffer→source→play chain IS all implemented (`L4AUDRND`/`L4AUDRES`/`L4AUDLVL`; `PatchLevelOfDetail::PlayNote` really calls diff --git a/engine/MUNGA_L4/L4AUDRND.cpp b/engine/MUNGA_L4/L4AUDRND.cpp index 09abb06..386aee4 100644 --- a/engine/MUNGA_L4/L4AUDRND.cpp +++ b/engine/MUNGA_L4/L4AUDRND.cpp @@ -380,8 +380,47 @@ void ALCdevice *device = alcOpenDevice(NULL); if (device) { - ALCcontext *context = alcCreateContext(device,NULL); + // AUDIO DROPOUTS IN HEAVY COMBAT (field: "audio cutting in and out toward + // the end of the match"; night-5 logs show 2017 ACQUIRE FAILED lines in a + // single match, every one of them at live=256). + // + // We were passing NULL for the attribute list, so OpenAL Soft applied its + // DEFAULT budget of 256 mono sources -- an OpenAL default, nothing to do + // with the 1995 audio hardware. The pool is not leaking: the night-5 + // census sits at a healthy 45-60 live, spikes to the 256 ceiling during + // firefights, and drains straight back afterwards, so the engine's + // priority steal loop (AudioSourceStop/SuspendMaintenance -> ReleaseChannels + // -> ReleaseSourceSet) is doing its job. It simply cannot keep up with the + // burst: a busy match logs ~7200 explosions, each spawning three + // DPLIndependantEffect voices, and while the pool is pinned every new + // acquire fails outright and that sound is silently dropped. + // + // Ask for a budget that fits the content. OpenAL Soft mixes in software, + // so this costs mixing headroom, not hardware voices, and it clamps to what + // it can honour -- hence the read-back below rather than an assumption. + // BT_AUDIO_SOURCES overrides for A/B testing. + ALCint monoWanted = 1024; + if (const char *sv = getenv("BT_AUDIO_SOURCES")) + { + int n = atoi(sv); + if (n >= 64 && n <= 4096) + monoWanted = n; + } + ALCint attrs[5]; + attrs[0] = ALC_MONO_SOURCES; attrs[1] = monoWanted; + attrs[2] = ALC_STEREO_SOURCES; attrs[3] = 16; + attrs[4] = 0; + ALCcontext *context = alcCreateContext(device, attrs); alcMakeContextCurrent(context); + // Report what the driver actually GRANTED -- a request is not a promise. + { + ALCint monoGot = 0, stereoGot = 0; + alcGetIntegerv(device, ALC_MONO_SOURCES, 1, &monoGot); + alcGetIntegerv(device, ALC_STEREO_SOURCES, 1, &stereoGot); + DEBUG_STREAM << "[audio] source budget: requested mono=" << monoWanted + << " granted mono=" << monoGot << " stereo=" << stereoGot + << std::endl << std::flush; + } // Master volume: AL_GAIN on the listener scales EVERY source. Default 0.6 // (the raw samples are hot). Priority: BT_AUDIO_VOLUME env > // content\volume.cfg (written by the runtime -/+ keys, issue #26) > 0.6.