Deluge Firmware 1.3.0
Build date: 2026.10.14
Loading...
Searching...
No Matches
mutable.hpp
1// Copyright 2023 Katherine Whitlock
2// Copyright 2014 Emilie Gillet
3// Reverb.
4
5#pragma once
6#include "definitions_cxx.hpp"
7#include "dsp/reverb/base.hpp"
8#include "dsp/util.hpp"
9#include "fx_engine.hpp"
10#include <array>
11#include <limits>
12
13namespace deluge::dsp::reverb {
14
15class Mutable : public Base {
16 constexpr static size_t kBufferSize = 32768;
17
18public:
19 Mutable() = default;
20
21 ~Mutable() override = default;
22
23 void process(std::span<int32_t> in, std::span<StereoSample> output) override {
24 // This is the Griesinger topology described in the Dattorro paper
25 // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay).
26 // Modulation is applied to the two long delays for a slow shimmer/chorus effect.
27 // Upstream also smears the first diffuser AP; that is deliberately not ported.
28 typename FxEngine::AllPass ap1(150);
29 typename FxEngine::AllPass ap2(214);
30 typename FxEngine::AllPass ap3(319);
31 typename FxEngine::AllPass ap4(527);
32
33 typename FxEngine::AllPass dap1a(2182);
34 typename FxEngine::AllPass dap1b(2690);
35 typename FxEngine::AllPass del1(4501);
36
37 typename FxEngine::AllPass dap2a(2525);
38 typename FxEngine::AllPass dap2b(2197);
39 typename FxEngine::AllPass del2(6312);
40
41 typename FxEngine::Context c;
42 FxEngine::ConstructTopology(engine_, //<
43 {
44 &ap1, &ap2, &ap3, &ap4, //<
45 &dap1a, &dap1b, &del1, //<
46 &dap2a, &dap2b, &del2, //<
47 });
48
49 const float kap = diffusion_;
50 const float klp = lp_;
51 const float krt = reverb_time_;
52
53 float lp_1 = lp_decay_1_;
54 float lp_2 = lp_decay_2_;
55
56 for (size_t frame = 0; frame < in.size(); frame++) {
57 StereoSample& s = output[frame];
58 float wet = 0;
59 float apout = 0.0f;
60 engine_.Advance();
61
62 // Smear AP1 inside the loop.
63 // c.Interpolate(ap1, 10.0f, LFO_1, 80.0f, 1.0f);
64 // c.Write(ap1, 100, 0.0f);
65
66 const float input_sample = in[frame] / static_cast<float>(std::numeric_limits<int32_t>::max());
67
68 c.Set(input_sample);
69
70 // Diffuse through 4 allpasses.
71 ap1.Process(c, kap);
72 ap2.Process(c, kap);
73 ap3.Process(c, kap);
74 ap4.Process(c, kap);
75 apout = c.Get();
76
77 // Main reverb loop.
78 c.Set(apout);
79 del2.Interpolate(c, 6261.0f, LFO_2, 50.0f, krt);
80 c.Lp(lp_1, klp);
81 dap1a.Process(c, -kap);
82 dap1b.Process(c, kap);
83 del1.Write(c, 2.0f);
84 wet = c.Get();
85 wet = wet - dsp::OnePole(hp_r_, wet, hp_cutoff_);
86 wet = dsp::OnePole(lp_r_, wet, lp_cutoff_);
87
88 auto output_right = dsp::toQ31Saturating(wet * kOutputGain);
89
90 c.Set(apout);
91 del1.Interpolate(c, 4460.0f, LFO_1, 40.0f, krt);
92 c.Lp(lp_2, klp);
93 dap2a.Process(c, -kap);
94 dap2b.Process(c, kap);
95 del2.Write(c, 2.0f);
96 wet = c.Get();
97 wet = wet - dsp::OnePole(hp_l_, wet, hp_cutoff_);
98 wet = dsp::OnePole(lp_l_, wet, lp_cutoff_);
99
100 auto output_left = dsp::toQ31Saturating(wet * kOutputGain);
101
102 // Mix
103 s.l += multiply_32x32_rshift32_rounded(output_left, getPanLeft());
104 s.r += multiply_32x32_rshift32_rounded(output_right, getPanRight());
105 }
106
107 lp_decay_1_ = lp_1;
108 lp_decay_2_ = lp_2;
109 }
110
111 inline void Clear() { engine_.Clear(); }
112
115 static constexpr float kOutputGain = 15.f * 4294967296.f;
116
117 static constexpr float kReverbTimeMin = 0.01f;
118 static constexpr float kReverbTimeMax = 0.98f;
119 static constexpr float kWidthMin = 0.1f;
120 static constexpr float kWidthMax = 0.9f;
123 static constexpr float kLpMin = 0.01f;
124 static constexpr float kLpMax = 1.f;
125
126 // Reverb Base Overrides
127 void setRoomSize(float value) override {
128 reverb_time_ = util::map(value, 0.f, 1.f, kReverbTimeMin, kReverbTimeMax);
129 }
130 [[nodiscard]] float getRoomSize() const override {
131 return util::map(reverb_time_, kReverbTimeMin, kReverbTimeMax, 0.f, 1.f);
132 };
133
136 void setDamping(float value) override {
137 lp_val_ = value;
138 const float damping = std::clamp(std::log2((value * 50.f) + 1.f) / 5.7f, 0.f, 1.f);
139 lp_ = util::map(1.f - damping, 0.f, 1.f, kLpMin, kLpMax);
140 }
141 [[nodiscard]] float getDamping() const override { return lp_val_; }
142
143 void setWidth(float value) override { diffusion_ = util::map(value, 0.f, 1.f, kWidthMin, kWidthMax); }
144 [[nodiscard]] float getWidth() const override { return util::map(diffusion_, kWidthMin, kWidthMax, 0.f, 1.f); };
145
146 void setHPF(float f) override {
147 hp_cutoff_val_ = f;
148 hp_cutoff_ = calcFilterCutoff<FilterType::HighPass>(f);
149 }
150
151 [[nodiscard]] float getHPF() const override { return hp_cutoff_val_; }
152
153 void setLPF(float f) override {
154 lp_cutoff_val_ = f;
155 lp_cutoff_ = calcFilterCutoff<FilterType::LowPass>(f);
156 }
157
158 [[nodiscard]] float getLPF() const override { return lp_cutoff_val_; }
159
160protected:
161 static constexpr float sample_rate = kSampleRate;
162
163 std::array<float, kBufferSize> buffer_{};
164 FxEngine engine_{buffer_, {0.5f / sample_rate, 0.3f / sample_rate}};
165
166 // size
167 float reverb_time_ = 0.665f;
168
169 // width
170 float diffusion_ = 0.625f;
171
172 // damping
173 float lp_{0.7f};
174 float lp_val_{0.7f};
175
176 // These are the state variables for the low-pass filters
177 float lp_decay_1_{0};
178 float lp_decay_2_{0};
179
180 // High-pass
181 float hp_cutoff_val_{0.f};
182 // corresponds to 20Hz
183 float hp_cutoff_{calcFilterCutoff<FilterType::HighPass>(0)};
184 float hp_l_{0.0}; // HP state variable
185 float hp_r_{0.0}; // HP state variable
186
187 // Low-pass
188 float lp_cutoff_val_{0.f};
189 // corresponds to 0Hz
190 float lp_cutoff_{calcFilterCutoff<FilterType::LowPass>(0)};
191 float lp_l_{0.0}; // LP state variable
192 float lp_r_{0.0}; // LP state variable
193};
194
195} // namespace deluge::dsp::reverb
Definition fx_engine.hpp:98
static constexpr float kOutputGain
Definition mutable.hpp:115
static constexpr float kLpMin
Definition mutable.hpp:123
void setDamping(float value) override
Definition mutable.hpp:136
Definition stereo_sample.h:25
Definition fx_engine.hpp:168
float Interpolate(Context &c, float offset, float scale)
Can be used in place of any AllPass::Read calls.
Definition fx_engine.hpp:200