Deluge Firmware 1.3.0
Build date: 2025.04.16
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 in the loop of the first diffuser AP for additional
27 // smearing; and to the two long delays for a slow shimmer/chorus effect.
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 const float gain = input_gain_;
53
54 float lp_1 = lp_decay_1_;
55 float lp_2 = lp_decay_2_;
56
57 for (size_t frame = 0; frame < in.size(); frame++) {
58 StereoSample& s = output[frame];
59 float wet = 0;
60 float apout = 0.0f;
61 engine_.Advance();
62
63 // Smear AP1 inside the loop.
64 // c.Interpolate(ap1, 10.0f, LFO_1, 80.0f, 1.0f);
65 // c.Write(ap1, 100, 0.0f);
66
67 const float input_sample = in[frame] / static_cast<float>(std::numeric_limits<int32_t>::max());
68
69 c.Set(input_sample // * gain
70 );
71
72 // Diffuse through 4 allpasses.
73 ap1.Process(c, kap);
74 ap2.Process(c, kap);
75 ap3.Process(c, kap);
76 ap4.Process(c, kap);
77 apout = c.Get();
78
79 // Main reverb loop.
80 c.Set(apout);
81 del2.Interpolate(c, 6261.0f, LFO_2, 50.0f, krt);
82 c.Lp(lp_1, klp);
83 dap1a.Process(c, -kap);
84 dap1b.Process(c, kap);
85 del1.Write(c, 2.0f);
86 wet = c.Get();
87 wet = wet - dsp::OnePole(hp_r_, wet, hp_cutoff_);
88 ;
89 wet = dsp::OnePole(lp_r_, wet, lp_cutoff_);
90
91 auto output_right =
92 static_cast<int32_t>(wet * static_cast<float>(std::numeric_limits<uint32_t>::max()) * 0xF);
93
94 c.Set(apout);
95 del1.Interpolate(c, 4460.0f, LFO_1, 40.0f, krt);
96 c.Lp(lp_2, klp);
97 dap2a.Process(c, -kap);
98 dap2b.Process(c, kap);
99 del2.Write(c, 2.0f);
100 wet = c.Get();
101 wet = wet - dsp::OnePole(hp_l_, wet, hp_cutoff_);
102 ;
103 wet = dsp::OnePole(lp_l_, wet, lp_cutoff_);
104 ;
105
106 auto output_left =
107 static_cast<int32_t>(wet * static_cast<float>(std::numeric_limits<uint32_t>::max()) * 0xF);
108
109 // Mix
110 s.l += multiply_32x32_rshift32_rounded(output_left, getPanLeft());
111 s.r += multiply_32x32_rshift32_rounded(output_right, getPanRight());
112 }
113
114 lp_decay_1_ = lp_1;
115 lp_decay_2_ = lp_2;
116 }
117
118 inline void Clear() { engine_.Clear(); }
119
120 static constexpr float kReverbTimeMin = 0.01f;
121 static constexpr float kReverbTimeMax = 0.98f;
122 static constexpr float kWidthMin = 0.1f;
123 static constexpr float kWidthMax = 0.9f;
124
125 // Reverb Base Overrides
126 void setRoomSize(float value) override {
127 reverb_time_ = util::map(value, 0.f, 1.f, kReverbTimeMin, kReverbTimeMax);
128 }
129 [[nodiscard]] float getRoomSize() const override {
130 return util::map(reverb_time_, kReverbTimeMin, kReverbTimeMax, 0.f, 1.f);
131 };
132
133 void setDamping(float value) override {
134 lp_val_ = value;
135 lp_ = (value == 0.f) ? 1.f : 1.f - std::clamp((std::log2(((1.f - lp_val_) * 50.f) + 1.f) / 5.7f), 0.f, 1.f);
136 }
137 [[nodiscard]] float getDamping() const override { return lp_val_; }
138
139 void setWidth(float value) override { diffusion_ = util::map(value, 0.f, 1.f, kWidthMin, kWidthMax); }
140 [[nodiscard]] float getWidth() const override { return util::map(diffusion_, kWidthMin, kWidthMax, 0.f, 1.f); };
141
142 void setHPF(float f) override {
143 hp_cutoff_val_ = f;
144 hp_cutoff_ = calcFilterCutoff<FilterType::HighPass>(f);
145 }
146
147 [[nodiscard]] float getHPF() const override { return hp_cutoff_val_; }
148
149 void setLPF(float f) override {
150 lp_cutoff_val_ = f;
151 lp_cutoff_ = calcFilterCutoff<FilterType::LowPass>(f);
152 }
153
154 [[nodiscard]] float getLPF() const override { return lp_cutoff_val_; }
155
156protected:
157 static constexpr float sample_rate = kSampleRate;
158
159 std::array<float, kBufferSize> buffer_{};
160 FxEngine engine_{buffer_, {0.5f / sample_rate, 0.3f / sample_rate}};
161
162 float input_gain_ = 0.2;
163
164 // size
165 float reverb_time_ = 0.665f;
166
167 // width
168 float diffusion_ = 0.625f;
169
170 // damping
171 float lp_{0.7f};
172 float lp_val_{0.7f};
173
174 // These are the state variables for the low-pass filters
175 float lp_decay_1_{0};
176 float lp_decay_2_{0};
177
178 // High-pass
179 float hp_cutoff_val_{0.f};
180 // corresponds to 20Hz
181 float hp_cutoff_{calcFilterCutoff<FilterType::HighPass>(0)};
182 float hp_l_{0.0}; // HP state variable
183 float hp_r_{0.0}; // HP state variable
184
185 // Low-pass
186 float lp_cutoff_val_{0.f};
187 // corresponds to 0Hz
188 float lp_cutoff_{calcFilterCutoff<FilterType::LowPass>(0)};
189 float lp_l_{0.0}; // LP state variable
190 float lp_r_{0.0}; // LP state variable
191};
192
193} // namespace deluge::dsp::reverb
Definition fx_engine.hpp:81
Definition fx_engine.hpp:29
Definition stereo_sample.h:25
Definition fx_engine.hpp:142
float Interpolate(Context &c, float offset, float scale)
Can be used in place of any AllPass::Read calls.
Definition fx_engine.hpp:174