aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Audio/OpenAlAudio.Reverb.cs
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
committergodosa <godosa@godosa.eu>2026-10-06 23:39:36 +0200
commit39066900773e7857faf2d02e7ed51b71d219d97d (patch)
treeffab8e4ddd971626776c3b1f4ce1da2c186c2187 /src/Godosa.Audio/OpenAlAudio.Reverb.cs
downloadgodosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz
godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip
godosa-engine: initial public history
Diffstat (limited to 'src/Godosa.Audio/OpenAlAudio.Reverb.cs')
-rw-r--r--src/Godosa.Audio/OpenAlAudio.Reverb.cs83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/Godosa.Audio/OpenAlAudio.Reverb.cs b/src/Godosa.Audio/OpenAlAudio.Reverb.cs
new file mode 100644
index 0000000..b93e74a
--- /dev/null
+++ b/src/Godosa.Audio/OpenAlAudio.Reverb.cs
@@ -0,0 +1,83 @@
+using Silk.NET.OpenAL.Extensions.Creative;
+
+namespace Godosa.Audio;
+
+/// <summary>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.</summary>
+public sealed unsafe partial class OpenAlAudio
+{
+ private EffectExtension? _efx;
+ private uint _effect, _effectSlot;
+ private ReverbPreset? _reverbApplied;
+ private ReverbPreset? _reverbWanted;
+ private bool _reverbInitialised;
+
+ /// <summary>The current region's reverb, or null for none (UI thread).</summary>
+ 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;
+ }
+}