Line data Source code
1 : #include "audio/effects/reverb.h"
2 : #include "audio/effects/effect_factory.h"
3 :
4 : namespace Amplitron {
5 :
6 2 : static EffectRegistrar<Reverb> reg("Reverb");
7 :
8 : // Comb filter delay lengths (in samples at 44100Hz, will be scaled)
9 : static const int COMB_LENGTHS[] = {1116, 1188, 1277, 1356};
10 : static const int ALLPASS_LENGTHS[] = {556, 441};
11 : // Right-channel lengths add a prime offset for decorrelation (classic Freeverb technique)
12 : static constexpr int STEREO_SPREAD = 23;
13 : static const int COMB_LENGTHS_R[] = {1116 + STEREO_SPREAD, 1188 + STEREO_SPREAD,
14 : 1277 + STEREO_SPREAD, 1356 + STEREO_SPREAD};
15 : static const int ALLPASS_LENGTHS_R[] = {556 + STEREO_SPREAD, 441 + STEREO_SPREAD};
16 :
17 196 : Reverb::Reverb() {
18 441 : params_ = {
19 98 : {"Decay", 0.6f, 0.1f, 0.99f, 0.6f, "", "Length of the reverb tail. Higher values simulate larger acoustic spaces like halls or caves."},
20 49 : {"Damp", 0.4f, 0.0f, 1.0f, 0.4f, "", "High-frequency damping. Higher values absorb treble faster, simulating softer room materials."},
21 49 : {"Level", 0.3f, 0.0f, 1.0f, 0.3f, "", "Mix volume of the reverb effect. Controls how wet or distant the overall sound feels."},
22 441 : };
23 147 : init_filters();
24 343 : }
25 :
26 165 : void Reverb::set_sample_rate(int sample_rate) {
27 165 : Effect::set_sample_rate(sample_rate);
28 165 : init_filters();
29 165 : }
30 :
31 312 : void Reverb::init_filters() {
32 312 : const float scale = static_cast<float>(sample_rate_) / 44100.0f;
33 :
34 1560 : for (int i = 0; i < NUM_COMBS; ++i) {
35 1248 : int len = static_cast<int>(COMB_LENGTHS[i] * scale);
36 1248 : combs_[i].buffer.assign(len, 0.0f);
37 1248 : combs_[i].write_pos = 0;
38 1248 : combs_[i].lp_state = 0.0f;
39 :
40 1248 : int len_r = static_cast<int>(COMB_LENGTHS_R[i] * scale);
41 1248 : combs_r_[i].buffer.assign(len_r, 0.0f);
42 1248 : combs_r_[i].write_pos = 0;
43 1248 : combs_r_[i].lp_state = 0.0f;
44 416 : }
45 :
46 936 : for (int i = 0; i < NUM_ALLPASS; ++i) {
47 624 : int len = static_cast<int>(ALLPASS_LENGTHS[i] * scale);
48 624 : allpasses_[i].buffer.assign(len, 0.0f);
49 624 : allpasses_[i].write_pos = 0;
50 624 : allpasses_[i].feedback = 0.5f;
51 :
52 624 : int len_r = static_cast<int>(ALLPASS_LENGTHS_R[i] * scale);
53 624 : allpasses_r_[i].buffer.assign(len_r, 0.0f);
54 624 : allpasses_r_[i].write_pos = 0;
55 624 : allpasses_r_[i].feedback = 0.5f;
56 208 : }
57 312 : }
58 :
59 99 : void Reverb::process(float* buffer, int num_samples) {
60 99 : if (!enabled_) return;
61 :
62 96 : float decay = params_[0].value;
63 96 : float damp = params_[1].value;
64 96 : float level = params_[2].value;
65 :
66 189408 : for (int i = 0; i < num_samples; ++i) {
67 189312 : float dry = buffer[i];
68 189312 : float input = buffer[i] * 0.2f;
69 189312 : float out = 0.0f;
70 :
71 : // Parallel comb filters
72 946560 : for (int c = 0; c < NUM_COMBS; ++c) {
73 757248 : auto& comb = combs_[c];
74 757248 : int buf_len = static_cast<int>(comb.buffer.size());
75 757248 : int read_pos = comb.write_pos;
76 :
77 757248 : float delayed = comb.buffer[read_pos];
78 :
79 : // Damping LP filter
80 757248 : comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
81 757248 : float fb_sample = comb.lp_state * decay;
82 :
83 757248 : comb.buffer[comb.write_pos] = input + fb_sample;
84 757248 : comb.write_pos = (comb.write_pos + 1) % buf_len;
85 :
86 757248 : out += delayed;
87 252416 : }
88 :
89 : // Series allpass filters
90 567936 : for (int a = 0; a < NUM_ALLPASS; ++a) {
91 378624 : auto& ap = allpasses_[a];
92 378624 : int buf_len = static_cast<int>(ap.buffer.size());
93 :
94 378624 : float delayed = ap.buffer[ap.write_pos];
95 378624 : float temp = out + delayed * ap.feedback;
96 378624 : ap.buffer[ap.write_pos] = temp;
97 378624 : out = delayed - out * ap.feedback;
98 :
99 378624 : ap.write_pos = (ap.write_pos + 1) % buf_len;
100 126208 : }
101 :
102 189312 : buffer[i] = dry * (1.0f - level) + out * level;
103 63104 : }
104 33 : }
105 :
106 3 : void Reverb::process_stereo(float* left, float* right, int num_samples) {
107 3 : if (!enabled_) {
108 0 : return;
109 : }
110 :
111 3 : const float decay = params_[0].value;
112 3 : const float damp = params_[1].value;
113 3 : const float level = params_[2].value;
114 :
115 6147 : for (int i = 0; i < num_samples; ++i) {
116 6144 : const float input_l = left[i] * 0.2f;
117 6144 : const float input_r = right[i] * 0.2f;
118 6144 : float out_l = 0.0f;
119 6144 : float out_r = 0.0f;
120 :
121 : // Parallel comb filters — left
122 30720 : for (int c = 0; c < NUM_COMBS; ++c) {
123 24576 : auto& comb = combs_[c];
124 24576 : const int buf_len = static_cast<int>(comb.buffer.size());
125 24576 : float delayed = comb.buffer[comb.write_pos];
126 24576 : comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
127 24576 : comb.buffer[comb.write_pos] = input_l + comb.lp_state * decay;
128 24576 : comb.write_pos = (comb.write_pos + 1) % buf_len;
129 24576 : out_l += delayed;
130 8192 : }
131 : // Parallel comb filters — right
132 30720 : for (int c = 0; c < NUM_COMBS; ++c) {
133 24576 : auto& comb = combs_r_[c];
134 24576 : const int buf_len = static_cast<int>(comb.buffer.size());
135 24576 : float delayed = comb.buffer[comb.write_pos];
136 24576 : comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
137 24576 : comb.buffer[comb.write_pos] = input_r + comb.lp_state * decay;
138 24576 : comb.write_pos = (comb.write_pos + 1) % buf_len;
139 24576 : out_r += delayed;
140 8192 : }
141 :
142 : // Series allpass filters — left
143 18432 : for (int a = 0; a < NUM_ALLPASS; ++a) {
144 12288 : auto& ap = allpasses_[a];
145 12288 : const int buf_len = static_cast<int>(ap.buffer.size());
146 12288 : float delayed = ap.buffer[ap.write_pos];
147 12288 : float temp = out_l + delayed * ap.feedback;
148 12288 : ap.buffer[ap.write_pos] = temp;
149 12288 : out_l = delayed - out_l * ap.feedback;
150 12288 : ap.write_pos = (ap.write_pos + 1) % buf_len;
151 4096 : }
152 : // Series allpass filters — right
153 18432 : for (int a = 0; a < NUM_ALLPASS; ++a) {
154 12288 : auto& ap = allpasses_r_[a];
155 12288 : const int buf_len = static_cast<int>(ap.buffer.size());
156 12288 : float delayed = ap.buffer[ap.write_pos];
157 12288 : float temp = out_r + delayed * ap.feedback;
158 12288 : ap.buffer[ap.write_pos] = temp;
159 12288 : out_r = delayed - out_r * ap.feedback;
160 12288 : ap.write_pos = (ap.write_pos + 1) % buf_len;
161 4096 : }
162 :
163 6144 : left[i] = left[i] * (1.0f - level) + out_l * level;
164 6144 : right[i] = right[i] * (1.0f - level) + out_r * level;
165 2048 : }
166 1 : }
167 :
168 168 : void Reverb::reset() {
169 840 : for (auto& c : combs_) {
170 672 : std::fill(c.buffer.begin(), c.buffer.end(), 0.0f);
171 672 : c.write_pos = 0;
172 672 : c.lp_state = 0.0f;
173 : }
174 840 : for (auto& c : combs_r_) {
175 672 : std::fill(c.buffer.begin(), c.buffer.end(), 0.0f);
176 672 : c.write_pos = 0;
177 672 : c.lp_state = 0.0f;
178 : }
179 504 : for (auto& a : allpasses_) {
180 336 : std::fill(a.buffer.begin(), a.buffer.end(), 0.0f);
181 336 : a.write_pos = 0;
182 : }
183 504 : for (auto& a : allpasses_r_) {
184 336 : std::fill(a.buffer.begin(), a.buffer.end(), 0.0f);
185 336 : a.write_pos = 0;
186 : }
187 168 : }
188 :
189 : } // namespace Amplitron
|