aboutsummaryrefslogtreecommitdiffstats
path: root/al/eax/effect.h
blob: b57bf24027f7812504430c89fc47b0f9a54e351c (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#ifndef EAX_EFFECT_INCLUDED
#define EAX_EFFECT_INCLUDED


#include <cassert>
#include <memory>

#include "alnumeric.h"
#include "AL/al.h"
#include "core/effects/base.h"
#include "call.h"

struct EaxEffectErrorMessages
{
    static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
    static constexpr auto unknown_version() noexcept { return "Unknown version."; }
}; // EaxEffectErrorMessages

class EaxEffect {
public:
    EaxEffect(ALenum type) noexcept : al_effect_type_{type} { }
    virtual ~EaxEffect() = default;

    const ALenum al_effect_type_;
    EffectProps al_effect_props_{};

    virtual void dispatch(const EaxCall& call) = 0;

    // Returns "true" if any immediated property was changed.
    /*[[nodiscard]]*/ virtual bool commit() = 0;
}; // EaxEffect

// Base class for EAX4+ effects.
template<typename TException, typename TProps>
class EaxEffect4 : public EaxEffect
{
public:
    EaxEffect4(ALenum type, const EaxCall& call)
        : EaxEffect{type}, version_{clamp(call.get_version(), 4, 5)}
    {}

    void initialize()
    {
        set_defaults();
        set_efx_defaults();
    }

    void dispatch(const EaxCall& call) override
    {
        call.is_get() ? get(call) : set(call);
        version_ = call.get_version();
    }

    bool commit() final
    {
        switch (version_)
        {
            case 4: return commit_state(state4_);
            case 5: return commit_state(state5_);
            default: fail_unknown_version();
        }
    }

protected:
    using Exception = TException;
    using Props = TProps;

    struct State {
        Props i; // Immediate.
        Props d; // Deferred.
    }; // State

    int version_;
    Props props_;
    State state4_;
    State state5_;

    template<typename TValidator, typename TProperty>
    static void defer(const EaxCall& call, TProperty& property)
    {
        const auto& value = call.get_value<Exception, const TProperty>();
        TValidator{}(value);
        property = value;
    }

    virtual void set_defaults(Props& props) = 0;
    virtual void set_efx_defaults() = 0;

    virtual void get(const EaxCall& call, const Props& props) = 0;
    virtual void set(const EaxCall& call, Props& props) = 0;

    virtual bool commit_props(const Props& props) = 0;

    [[noreturn]] static void fail(const char* message)
    {
        throw Exception{message};
    }

    [[noreturn]] static void fail_unknown_property_id()
    {
        fail(EaxEffectErrorMessages::unknown_property_id());
    }

    [[noreturn]] static void fail_unknown_version()
    {
        fail(EaxEffectErrorMessages::unknown_version());
    }

private:
    void set_defaults()
    {
        set_defaults(props_);
        state4_.i = props_;
        state4_.d = props_;
        state5_.i = props_;
        state5_.d = props_;
    }

    void get(const EaxCall& call)
    {
        switch (call.get_version())
        {
            case 4: get(call, state4_.i); break;
            case 5: get(call, state5_.i); break;
            default: fail_unknown_version();
        }
    }

    void set(const EaxCall& call)
    {
        switch (call.get_version())
        {
            case 4: set(call, state4_.d); break;
            case 5: set(call, state5_.d); break;
            default: fail_unknown_version();
        }
    }

    bool commit_state(State& state)
    {
        const auto props = props_;
        state.i = state.d;
        props_ = state.d;
        return commit_props(props);
    }
}; // EaxEffect4

using EaxEffectUPtr = std::unique_ptr<EaxEffect>;

// Creates EAX4+ effect.
template<typename TEffect>
EaxEffectUPtr eax_create_eax4_effect(const EaxCall& call)
{
    auto effect = std::make_unique<TEffect>(call);
    effect->initialize();
    return effect;
}

EaxEffectUPtr eax_create_eax_null_effect();
EaxEffectUPtr eax_create_eax_chorus_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_distortion_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_echo_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_flanger_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_frequency_shifter_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_vocal_morpher_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_pitch_shifter_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_ring_modulator_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_auto_wah_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_compressor_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_equalizer_effect(const EaxCall& call);
EaxEffectUPtr eax_create_eax_reverb_effect(const EaxCall& call);

#endif // !EAX_EFFECT_INCLUDED