using Silk.NET.OpenAL.Extensions.Creative;
namespace Godosa.Audio;
/// One EFX reverb for the region (audio spec ยง2): positioned sources send to it, 2D
/// ones (beds, music, interface) don't. Air absorption dulls distant sounds.
public sealed unsafe partial class OpenAlAudio
{
private EffectExtension? _efx;
private uint _effect, _effectSlot;
private ReverbPreset? _reverbApplied;
private ReverbPreset? _reverbWanted;
private bool _reverbInitialised;
/// The current region's reverb, or null for none (UI thread).
public ReverbPreset? Reverb
{
set { lock (_poseLock) _reverbWanted = value; }
}
private void InitReverb()
{
if (!_al!.TryGetExtension(out _efx)) _efx = null;
if (_efx is null)
{
if (EngineDebug.Audio) EngineDebug.Write("audio", "no EFX: no reverb");
return;
}
_effect = _efx.GenEffects(1)[0];
_efx.SetEffectProperty(_effect, EffectInteger.EffectType, (int)EffectType.Reverb);
_effectSlot = _efx.GenAuxiliaryEffectSlots(1)[0];
_reverbApplied = null;
_reverbInitialised = false;
}
private void UpdateReverb()
{
if (_efx is null) return;
ReverbPreset? wanted;
lock (_poseLock) wanted = _reverbWanted;
if (_reverbInitialised && _reverbApplied == wanted) return;
_reverbInitialised = true;
_reverbApplied = wanted;
if (wanted is not { } p)
{
// No effect in the slot: sends to it are dropped and the mixer runs no reverb.
_efx.SetAuxiliaryEffectSlotProperty(_effectSlot, EffectSlotInteger.Effect, 0);
if (EngineDebug.Audio) EngineDebug.Write("audio", "reverb off");
return;
}
_efx.SetEffectProperty(_effect, EffectFloat.ReverbDensity, p.Density);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbDiffusion, p.Diffusion);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbGain, p.Gain);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbGainHF, p.GainHF);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbDecayTime, p.DecayTime);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbDecayHFRatio, p.DecayHFRatio);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbReflectionsGain, p.ReflectionsGain);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbReflectionsDelay, p.ReflectionsDelay);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbLateReverbGain, p.LateReverbGain);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbLateReverbDelay, p.LateReverbDelay);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbAirAbsorptionGainHF, p.AirAbsorptionGainHF);
_efx.SetEffectProperty(_effect, EffectFloat.ReverbRoomRolloffFactor, p.RoomRolloffFactor);
_efx.SetEffectProperty(_effect, EffectInteger.ReverbDecayHFLimit, p.DecayHFLimit ? 1 : 0);
_efx.SetAuxiliaryEffectSlotProperty(_effectSlot, EffectSlotInteger.Effect, (int)_effect); // re-attach applies the change
if (EngineDebug.Audio) EngineDebug.Write("audio", $"reverb {(p == ReverbPreset.Forest ? "forest" : p == ReverbPreset.Cave ? "cave" : "plain")}");
}
private void SendToReverb(uint source, bool positioned)
{
if (_efx is null) return;
_efx.SetSourceProperty(source, EFXSourceInteger3.AuxiliarySendFilter, positioned ? (int)_effectSlot : 0, 0, 0);
_efx.SetSourceProperty(source, EFXSourceFloat.AirAbsorptionFactor, positioned ? 1f : 0f);
}
private void ReleaseReverb()
{
if (_efx is null) return;
_efx.SetAuxiliaryEffectSlotProperty(_effectSlot, EffectSlotInteger.Effect, 0);
_efx.DeleteAuxiliaryEffectSlot(_effectSlot);
_efx.DeleteEffect(_effect);
_efx = null;
}
}