#pragma once //########################################################################### // // L4AUDEFX.h -- OpenAL EFX bridge for the authored filter/reverb chains // (docs/SOUND.md, findings F9 and F11). // // The original drove the AWE32's initial-filter-cutoff NRPN (21) every frame // -- brightness x the distance high-frequency rolloff -- and sent CC91 reverb // on the 3D channels (global_reverb_scale=0.35 in RP's AUDIO.INI) while // keeping the cockpit DirectPatch channels dry. The OpenAL port computed // both and applied neither: GetHighFreqCutoffScale() had no callers at all // and every CC91 send site sat inside a comment block, so RP played // spectrally full-bright at every distance and bone-dry everywhere. // // This bridge reproduces both through OpenAL Soft's EFX extension: one // EAXReverb auxiliary slot plus a scratch AL_FILTER_LOWPASS whose parameters // are copied at attach time. Without ALC_EXT_EFX it stays inert and every // entry point below is a no-op, so the game still runs on a bare OpenAL. // //########################################################################### #include "openal/al.h" // // Load the EFX entry points, create the reverb slot (gain = the authored // global_reverb_scale) and the scratch lowpass. Call once, with the AL // context current. Returns false (and stays inert) without ALC_EXT_EFX. // bool EFX_Initialize(float global_reverb_scale); bool EFX_Available(); // // Per-frame direct-path filter: gainhf is the linear high-frequency gain at the // EFX 5 kHz reference, carrying the authored brightness x distance model. // Callers map the AWE cutoff through EFX_CutoffScaleToGainHF below. // // At unity the filter is detached rather than attached at no-op settings. // // NOTE: this is a LOWPASS and can only ever be one. The OpenAL this game ships // (Creative's) implements no other filter type -- see L4AUDEFX.cpp -- so the // bass trim could not ride here as a bandpass GAINLF and lives in the resource // loader instead (RPApplyBassTrim, L4AUDRES.cpp). // void EFX_SetSourceLowpassGainHF(ALuint source, float gainhf); // // AWE NRPN 21 curve -> EFX gainhf. cutoff_scale is [0,1] of the 100-8000 Hz // span; approximated as the attenuation of a 2-pole lowpass at the 5 kHz // reference. Curve shape is approximate, endpoints exact. // inline float EFX_CutoffScaleToGainHF(float cutoff_scale) { if (cutoff_scale < 0.0f) cutoff_scale = 0.0f; if (cutoff_scale > 1.0f) cutoff_scale = 1.0f; float cutoff_hz = 100.0f + cutoff_scale * 7900.0f; float g = (cutoff_hz / 5000.0f) * (cutoff_hz / 5000.0f); return (g > 1.0f) ? 1.0f : ((g < 0.001f) ? 0.001f : g); } // // Wet-exterior routing: attach the source's auxiliary send to the reverb slot // (Dynamic3D / Static3D). Direct cockpit sources stay dry. // void EFX_AttachReverbSend(ALuint source); // // Drop both the direct-path filter and the reverb send. Required when a source // is recycled through the pool: without it a dry cockpit sound can inherit the // wet send of the 3D source that used the name before it, and a full-bright // source can inherit a distant source's lowpass. // void EFX_ClearSourceEffects(ALuint source);