New L4AUDEFX bridge (OpenAL Soft ALC_EXT_EFX): one EAXReverb aux slot at the authentic AUDIO.INI global_reverb_scale (0.3) + a scratch AL_FILTER_LOWPASS (params copied at attach). F9 filters (was: computed then thrown away -- everything full-bright at all distances): Dynamic3D ExecuteModel drives GAINHF from highFreqCutoffScale x brightnessScale x maxMIDIFilterCutoff, UNGATED (decomp part_008.c:7496, 7589-7604 -- every moving 3D sound dulls with distance per the AUDIO.INI knee-60/exp-2.0 model); Static3D from brightness x max (:7831-7884); Direct inside its existing gated NRPN-rate block. AWE 100-8000 Hz curve -> EFX 5 kHz-reference gainhf via a 2-pole approximation [T3 curve, endpoints exact]. F11 reverb (was: bone-dry everywhere): 3D patch sources attach an aux send at Start, exactly where the original sent CC91 = global_reverb_scale (part_008.c:7278-7394); Direct cockpit sources keep CC91=0 -- dry. The wet-exterior vs dry-cockpit contrast is back. F12 placement (was: every cockpit sound dead-center): DirectPatchSource Start places sources by the authored 6-value position enum (front/rear card + pan CC10, decomp @00463848/@004638a8) as listener-relative directions, composed with the zone L/C/R pan. New PatchResource GetBankID/GetPatchID pass-throughs (the LOD accessor is protected). Regression (35s drive+fire): EFX READY, stable, deliveries unregressed, no AL errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
130 lines
4.4 KiB
C++
130 lines
4.4 KiB
C++
//###########################################################################
|
|
//
|
|
// L4AUDEFX.cpp -- OpenAL EFX bridge (task #50, AUDIO_FIDELITY F9/F11).
|
|
// See L4AUDEFX.h for the fidelity rationale.
|
|
//
|
|
//###########################################################################
|
|
#include <cstdlib>
|
|
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4audefx.h"
|
|
#include "openal/alc.h"
|
|
#include "openal/efx.h"
|
|
|
|
#ifndef AL_EFFECT_EAXREVERB
|
|
#define AL_EFFECT_EAXREVERB 0x8000 // newer efx.h constant; OpenAL Soft supports it
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
bool s_available = false;
|
|
ALuint s_reverbSlot = 0;
|
|
ALuint s_reverbEffect = 0;
|
|
ALuint s_scratchFilter = 0;
|
|
|
|
LPALGENEFFECTS p_alGenEffects = 0;
|
|
LPALEFFECTI p_alEffecti = 0;
|
|
LPALEFFECTF p_alEffectf = 0;
|
|
LPALGENAUXILIARYEFFECTSLOTS p_alGenAuxiliaryEffectSlots = 0;
|
|
LPALAUXILIARYEFFECTSLOTI p_alAuxiliaryEffectSloti = 0;
|
|
LPALAUXILIARYEFFECTSLOTF p_alAuxiliaryEffectSlotf = 0;
|
|
LPALGENFILTERS p_alGenFilters = 0;
|
|
LPALFILTERI p_alFilteri = 0;
|
|
LPALFILTERF p_alFilterf = 0;
|
|
}
|
|
|
|
bool EFX_Available()
|
|
{
|
|
return s_available;
|
|
}
|
|
|
|
bool EFX_Initialize(float global_reverb_scale)
|
|
{
|
|
ALCcontext *context = alcGetCurrentContext();
|
|
if (context == 0)
|
|
{
|
|
return false;
|
|
}
|
|
ALCdevice *device = alcGetContextsDevice(context);
|
|
if (device == 0 || !alcIsExtensionPresent(device, "ALC_EXT_EFX"))
|
|
{
|
|
if (getenv("BT_AUDIO_LOG"))
|
|
DEBUG_STREAM << "[audio] EFX: ALC_EXT_EFX NOT present -- filters/reverb inert\n" << std::flush;
|
|
return false;
|
|
}
|
|
|
|
p_alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
|
|
p_alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
|
|
p_alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
|
|
p_alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
|
|
p_alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
|
|
p_alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
|
|
p_alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
|
|
p_alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
|
|
p_alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
|
|
|
|
if (!p_alGenEffects || !p_alEffecti || !p_alEffectf
|
|
|| !p_alGenAuxiliaryEffectSlots || !p_alAuxiliaryEffectSloti || !p_alAuxiliaryEffectSlotf
|
|
|| !p_alGenFilters || !p_alFilteri || !p_alFilterf)
|
|
{
|
|
if (getenv("BT_AUDIO_LOG"))
|
|
DEBUG_STREAM << "[audio] EFX: entry points missing -- filters/reverb inert\n" << std::flush;
|
|
return false;
|
|
}
|
|
|
|
alGetError();
|
|
p_alGenAuxiliaryEffectSlots(1, &s_reverbSlot);
|
|
p_alGenEffects(1, &s_reverbEffect);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// EAXReverb where available (OpenAL Soft: yes), plain reverb otherwise.
|
|
p_alEffecti(s_reverbEffect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
p_alEffecti(s_reverbEffect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
|
|
}
|
|
p_alAuxiliaryEffectSloti(s_reverbSlot, AL_EFFECTSLOT_EFFECT, (ALint)s_reverbEffect);
|
|
// The authentic wet level: the original sent CC91 = global_reverb_scale
|
|
// (0.3 -> value 38) on every 3D channel; one global slot gain reproduces
|
|
// the same uniform send.
|
|
p_alAuxiliaryEffectSlotf(s_reverbSlot, AL_EFFECTSLOT_GAIN,
|
|
(global_reverb_scale < 0.0f) ? 0.0f :
|
|
(global_reverb_scale > 1.0f) ? 1.0f : global_reverb_scale);
|
|
|
|
p_alGenFilters(1, &s_scratchFilter);
|
|
p_alFilteri(s_scratchFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
|
|
|
|
s_available = (alGetError() == AL_NO_ERROR);
|
|
if (getenv("BT_AUDIO_LOG"))
|
|
DEBUG_STREAM << "[audio] EFX: " << (s_available ? "READY" : "FAILED")
|
|
<< " (reverb slot gain=" << global_reverb_scale << ")\n" << std::flush;
|
|
return s_available;
|
|
}
|
|
|
|
void EFX_SetSourceLowpassGainHF(ALuint source, float gainhf)
|
|
{
|
|
if (!s_available)
|
|
{
|
|
return;
|
|
}
|
|
if (gainhf < 0.001f) gainhf = 0.001f;
|
|
if (gainhf > 1.0f) gainhf = 1.0f;
|
|
// filter params are COPIED at attach, so one scratch filter serves all
|
|
p_alFilterf(s_scratchFilter, AL_LOWPASS_GAIN, 1.0f);
|
|
p_alFilterf(s_scratchFilter, AL_LOWPASS_GAINHF, gainhf);
|
|
alSourcei(source, AL_DIRECT_FILTER, (ALint)s_scratchFilter);
|
|
}
|
|
|
|
void EFX_AttachReverbSend(ALuint source)
|
|
{
|
|
if (!s_available)
|
|
{
|
|
return;
|
|
}
|
|
alSource3i(source, AL_AUXILIARY_SEND_FILTER, (ALint)s_reverbSlot, 0, AL_FILTER_NULL);
|
|
}
|