aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Audio/OpenAlAudio.Reverb.cs
blob: b93e74a4d27d64f4808da6b4bb180cb604c8577f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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;
    }
}