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; } }