Vital
Loading...
Searching...
No Matches
reverb.cpp
Go to the documentation of this file.
1#include "reverb.h"
2
3#include "futils.h"
4#include "memory.h"
5#include "synth_constants.h"
6
7namespace vital {
8
9 //============================================================================
10 // Local constants for reverb-specific processing
11 //============================================================================
12 static constexpr float kMaxChorusDrift = 2500.0f;
13 static constexpr float kMinDecayTime = 0.1f;
14 static constexpr float kMaxDecayTime = 100.0f;
15 static constexpr float kMaxChorusFrequency = 16.0f;
16 static constexpr float kChorusShiftAmount = 0.9f;
17 static constexpr float kSampleDelayMultiplier = 0.05f;
18 static constexpr float kSampleIncrementMultiplier = 0.05f;
19
20 // Fixed per-container all-pass delays
21 const poly_int Reverb::kAllpassDelays[kNetworkContainers] = {
22 { 1001, 799, 933, 876 },
23 { 895, 807, 907, 853 },
24 { 957, 1019, 711, 567 },
25 { 833, 779, 663, 997 }
26 };
27
28 // Fixed per-container feedback delays (in floating-point samples)
29 const poly_float Reverb::kFeedbackDelays[kNetworkContainers] = {
30 { 6753.2f, 9278.4f, 7704.5f, 11328.5f },
31 { 9701.12f, 5512.5f, 8480.45f, 5638.65f },
32 { 3120.73f, 3429.5f, 3626.37f, 7713.52f },
33 { 4521.54f, 6518.97f, 5265.56f, 5630.25f }
34 };
35
42 : Processor(kNumInputs, 1),
43 chorus_phase_(0.0f),
44 chorus_amount_(0.0f),
45 feedback_(0.0f),
46 damping_(0.0f),
47 dry_(0.0f),
48 wet_(0.0f),
49 write_index_(0),
50 max_allpass_size_(0),
51 max_feedback_size_(0),
52 feedback_mask_(0),
53 allpass_mask_(0),
54 poly_allpass_mask_(0) {
55 // Initialize buffer sizes for default sample rate
57
58 // StereoMemory for final summation
59 memory_ = std::make_unique<StereoMemory>(kMaxSampleRate);
60
61 // Initialize decays and filter coefficients
62 for (int i = 0; i < kNetworkContainers; ++i)
63 decays_[i] = 0.0f;
64
65 low_pre_coefficient_ = 0.1f;
66 high_pre_coefficient_ = 0.1f;
67 low_coefficient_ = 0.1f;
68 high_coefficient_ = 0.1f;
69 low_amplitude_ = 0.0f;
70 high_amplitude_ = 0.0f;
71 sample_delay_ = kMinDelay;
72 }
73
79 void Reverb::setupBuffersForSampleRate(int sample_rate) {
80 int buffer_scale = getBufferScale(sample_rate);
81
82 // Compute maximum feedback buffer size
83 int max_feedback_size = buffer_scale * (1 << (kBaseFeedbackBits + kMaxSizePower));
84 if (max_feedback_size_ == max_feedback_size)
85 return; // No change needed
86
87 max_feedback_size_ = max_feedback_size;
88 feedback_mask_ = max_feedback_size_ - 1;
89
90 // Reallocate feedback memories and pointers
91 for (int i = 0; i < kNetworkSize; ++i) {
92 feedback_memories_[i] = std::make_unique<mono_float[]>(max_feedback_size_ + kExtraLookupSample);
93 feedback_lookups_[i] = feedback_memories_[i].get() + 1; // offset for wrapping
94 }
95
96 // All-pass buffer sizes
97 max_allpass_size_ = buffer_scale * (1 << kBaseAllpassBits);
98 poly_allpass_mask_ = max_allpass_size_ - 1;
99 allpass_mask_ = max_allpass_size_ * poly_float::kSize - 1;
100
101 for (int i = 0; i < kNetworkContainers; ++i)
102 allpass_lookups_[i] = std::make_unique<poly_float[]>(max_allpass_size_);
103
104 // Ensure write index is in range
105 write_index_ &= feedback_mask_;
106 }
107
113 void Reverb::process(int num_samples) {
115 processWithInput(input(kAudio)->source->buffer, num_samples);
116 }
117
128 void Reverb::processWithInput(const poly_float* audio_in, int num_samples) {
129 // Wrap feedback buffers for safe interpolation
130 for (int i = 0; i < kNetworkSize; ++i)
131 wrapFeedbackBuffer(feedback_memories_[i].get());
132
133 poly_float* audio_out = output()->buffer;
134 mono_float tick_increment = 1.0f / num_samples;
135
136 // Cache current parameter values for smooth interpolation
137 poly_float current_dry = dry_;
138 poly_float current_wet = wet_;
139 poly_float current_low_pre_coefficient = low_pre_coefficient_;
140 poly_float current_high_pre_coefficient = high_pre_coefficient_;
141 poly_float current_low_coefficient = low_coefficient_;
142 poly_float current_low_amplitude = low_amplitude_;
143 poly_float current_high_coefficient = high_coefficient_;
144 poly_float current_high_amplitude = high_amplitude_;
145
146 // Update wet/dry with equal-power crossfade
147 poly_float wet_in = utils::clamp(input(kWet)->at(0), 0.0f, 1.0f);
148 wet_ = futils::equalPowerFade(wet_in);
149 dry_ = futils::equalPowerFadeInverse(wet_in);
150 poly_float delta_wet = (wet_ - current_wet) * tick_increment;
151 poly_float delta_dry = (dry_ - current_dry) * tick_increment;
152
153 // Calculate oversampling scale
154 int sample_rate = getSampleRate();
155 int buffer_scale = getBufferScale(sample_rate);
156 float sample_rate_ratio = getSampleRateRatio(sample_rate);
157
158 // Pre-filter cutoff frequencies
159 poly_float low_pre_cutoff_midi = utils::clamp(input(kPreLowCutoff)->at(0), 0.0f, 130.0f);
160 poly_float low_pre_cutoff_frequency = utils::midiNoteToFrequency(low_pre_cutoff_midi);
161 low_pre_coefficient_ = OnePoleFilter<>::computeCoefficient(low_pre_cutoff_frequency, sample_rate);
162
163 poly_float high_pre_cutoff_midi = utils::clamp(input(kPreHighCutoff)->at(0), 0.0f, 130.0f);
164 poly_float high_pre_cutoff_frequency = utils::midiNoteToFrequency(high_pre_cutoff_midi);
165 high_pre_coefficient_ = OnePoleFilter<>::computeCoefficient(high_pre_cutoff_frequency, sample_rate);
166
167 poly_float delta_low_pre_coefficient = (low_pre_coefficient_ - current_low_pre_coefficient) * tick_increment;
168 poly_float delta_high_pre_coefficient = (high_pre_coefficient_ - current_high_pre_coefficient) * tick_increment;
169
170 // Internal feedback filter parameters
171 poly_float low_cutoff_midi = utils::clamp(input(kLowCutoff)->at(0), 0.0f, 130.0f);
172 poly_float low_cutoff_frequency = utils::midiNoteToFrequency(low_cutoff_midi);
173 low_coefficient_ = OnePoleFilter<>::computeCoefficient(low_cutoff_frequency, sample_rate);
174
175 poly_float high_cutoff_midi = utils::clamp(input(kHighCutoff)->at(0), 0.0f, 130.0f);
176 poly_float high_cutoff_frequency = utils::midiNoteToFrequency(high_cutoff_midi);
177 high_coefficient_ = OnePoleFilter<>::computeCoefficient(high_cutoff_frequency, sample_rate);
178
179 poly_float delta_low_coefficient = (low_coefficient_ - current_low_coefficient) * tick_increment;
180 poly_float delta_high_coefficient = (high_coefficient_ - current_high_coefficient) * tick_increment;
181
182 // Low/high gains become attenuation factors
183 poly_float low_gain = utils::clamp(input(kLowGain)->at(0), -24.0f, 0.0f);
184 low_amplitude_ = poly_float(1.0f) - utils::dbToMagnitude(low_gain);
185
186 poly_float high_gain = utils::clamp(input(kHighGain)->at(0), -24.0f, 0.0f);
187 high_amplitude_ = utils::dbToMagnitude(high_gain);
188
189 poly_float delta_low_amplitude = (low_amplitude_ - current_low_amplitude) * tick_increment;
190 poly_float delta_high_amplitude = (high_amplitude_ - current_high_amplitude) * tick_increment;
191
192 // Retrieve pointers to all-pass memory blocks
193 const mono_float* allpass_lookup1 = (mono_float*)allpass_lookups_[0].get();
194 const mono_float* allpass_lookup2 = (mono_float*)allpass_lookups_[1].get();
195 const mono_float* allpass_lookup3 = (mono_float*)allpass_lookups_[2].get();
196 const mono_float* allpass_lookup4 = (mono_float*)allpass_lookups_[3].get();
197
198 // Group feedback memory in sets of four for each container
199 mono_float* feedback_lookups1[] = { feedback_lookups_[0], feedback_lookups_[1],
200 feedback_lookups_[2], feedback_lookups_[3] };
201 mono_float* feedback_lookups2[] = { feedback_lookups_[4], feedback_lookups_[5],
202 feedback_lookups_[6], feedback_lookups_[7] };
203 mono_float* feedback_lookups3[] = { feedback_lookups_[8], feedback_lookups_[9],
204 feedback_lookups_[10], feedback_lookups_[11] };
205 mono_float* feedback_lookups4[] = { feedback_lookups_[12], feedback_lookups_[13],
206 feedback_lookups_[14], feedback_lookups_[15] };
207
208 // Size parameter modifies overall delay lengths
209 poly_float size = utils::clamp(input(kSize)->at(0), 0.0f, 1.0f);
210 poly_float size_mult = futils::pow(2.0f, size * kSizePowerRange + kMinSizePower);
211
212 // Compute decay from size and decay time
213 poly_float decay_samples = utils::clamp(input(kDecayTime)->at(0), kMinDecayTime, kMaxDecayTime) * kBaseSampleRate;
214 poly_float decay_period = size_mult / decay_samples;
215
216 // Load old decay values for interpolation
217 poly_float current_decay1 = decays_[0];
218 poly_float current_decay2 = decays_[1];
219 poly_float current_decay3 = decays_[2];
220 poly_float current_decay4 = decays_[3];
221
222 // Calculate new decays
223 decays_[0] = utils::pow(kT60Amplitude, kFeedbackDelays[0] * decay_period);
224 decays_[1] = utils::pow(kT60Amplitude, kFeedbackDelays[1] * decay_period);
225 decays_[2] = utils::pow(kT60Amplitude, kFeedbackDelays[2] * decay_period);
226 decays_[3] = utils::pow(kT60Amplitude, kFeedbackDelays[3] * decay_period);
227
228 // Interpolate decays over this block
229 poly_float delta_decay1 = (decays_[0] - current_decay1) * tick_increment;
230 poly_float delta_decay2 = (decays_[1] - current_decay2) * tick_increment;
231 poly_float delta_decay3 = (decays_[2] - current_decay3) * tick_increment;
232 poly_float delta_decay4 = (decays_[3] - current_decay4) * tick_increment;
233
234 // Offsets for all-pass buffers, scaled by buffer_scale
235 poly_int delay_offset(0, -1, -2, -3); // for safe indexing in stereo
236 if (buffer_scale)
237 delay_offset += poly_float::kSize;
238
239 poly_int allpass_offset1 = utils::swapStereo(kAllpassDelays[0] * buffer_scale * poly_float::kSize + delay_offset);
240 poly_int allpass_offset2 = utils::swapStereo(kAllpassDelays[1] * buffer_scale * poly_float::kSize + delay_offset);
241 poly_int allpass_offset3 = utils::swapStereo(kAllpassDelays[2] * buffer_scale * poly_float::kSize + delay_offset);
242 poly_int allpass_offset4 = utils::swapStereo(kAllpassDelays[3] * buffer_scale * poly_float::kSize + delay_offset);
243
244 // Chorus LFO calculation
245 mono_float chorus_frequency = utils::clamp(input(kChorusFrequency)->at(0)[0], 0.0f, kMaxChorusFrequency);
246 mono_float chorus_phase_increment = chorus_frequency / sample_rate;
247
248 // Spread the phase per container
249 mono_float network_offset = 2.0f * kPi / kNetworkSize;
250 poly_float phase_offset = poly_float(0.0f, 1.0f, 2.0f, 3.0f) * network_offset;
251 poly_float container_phase = phase_offset + chorus_phase_ * 2.0f * kPi;
252 chorus_phase_ += num_samples * chorus_phase_increment;
253 chorus_phase_ -= std::floor(chorus_phase_);
254
255 // Real and imaginary increments for a small rotation in the complex plane
256 poly_float chorus_increment_real = utils::cos(chorus_phase_increment * (2.0f * kPi));
257 poly_float chorus_increment_imaginary = utils::sin(chorus_phase_increment * (2.0f * kPi));
258 poly_float current_chorus_real = utils::cos(container_phase);
259 poly_float current_chorus_imaginary = utils::sin(container_phase);
260
261 // Baseline feedback delay amounts
262 poly_float delay1 = size_mult * kFeedbackDelays[0] * sample_rate_ratio;
263 poly_float delay2 = size_mult * kFeedbackDelays[1] * sample_rate_ratio;
264 poly_float delay3 = size_mult * kFeedbackDelays[2] * sample_rate_ratio;
265 poly_float delay4 = size_mult * kFeedbackDelays[3] * sample_rate_ratio;
266
267 // Compute chorus amount (in samples)
268 poly_float current_chorus_amount = chorus_amount_;
269 chorus_amount_ = utils::clamp(input(kChorusAmount)->at(0)[0], 0.0f, 1.0f) * kMaxChorusDrift * sample_rate_ratio;
270
271 // Ensure chorus doesn’t exceed safe range relative to delays
272 chorus_amount_ = utils::min(chorus_amount_, delay1 - 8 * poly_float::kSize);
273 chorus_amount_ = utils::min(chorus_amount_, delay2 - 8 * poly_float::kSize);
274 chorus_amount_ = utils::min(chorus_amount_, delay3 - 8 * poly_float::kSize);
275 chorus_amount_ = utils::min(chorus_amount_, delay4 - 8 * poly_float::kSize);
276 poly_float delta_chorus_amount = (chorus_amount_ - current_chorus_amount) * tick_increment;
277 current_chorus_amount = current_chorus_amount * size_mult;
278
279 // Handle user-defined additional pre-delay
280 poly_float current_sample_delay = sample_delay_;
281 poly_float current_delay_increment = sample_delay_increment_;
282 poly_float end_target = current_sample_delay + current_delay_increment * num_samples;
284 target_delay = utils::interpolate(sample_delay_, target_delay, kSampleDelayMultiplier);
285 poly_float makeup_delay = target_delay - end_target;
286 poly_float delta_delay_increment = makeup_delay / (0.5f * num_samples * num_samples) * kSampleIncrementMultiplier;
287
288 for (int i = 0; i < num_samples; ++i) {
289 // Increment chorus phase and amplitude
290 current_chorus_amount += delta_chorus_amount;
291 // Basic complex-plane rotation for chorus real/imag parts
292 auto old_real = current_chorus_real;
293 current_chorus_real = current_chorus_real * chorus_increment_real -
294 current_chorus_imaginary * chorus_increment_imaginary;
295 current_chorus_imaginary = current_chorus_imaginary * chorus_increment_real +
296 old_real * chorus_increment_imaginary;
297
298 // Add or subtract the chorus to each line’s offset
299 poly_float feedback_offset1 = delay1 + current_chorus_real * current_chorus_amount;
300 poly_float feedback_offset2 = delay2 - current_chorus_real * current_chorus_amount;
301 poly_float feedback_offset3 = delay3 + current_chorus_imaginary * current_chorus_amount;
302 poly_float feedback_offset4 = delay4 - current_chorus_imaginary * current_chorus_amount;
303
304 // Interpolate feedback from each container
305 poly_float feedback_read1 = readFeedback(feedback_lookups1, feedback_offset1);
306 poly_float feedback_read2 = readFeedback(feedback_lookups2, feedback_offset2);
307 poly_float feedback_read3 = readFeedback(feedback_lookups3, feedback_offset3);
308 poly_float feedback_read4 = readFeedback(feedback_lookups4, feedback_offset4);
309
310 // Merge the input to a mono signal (for a typical reverb design)
311 poly_float input = audio_in[i] & constants::kFirstMask;
313
314 // Pre-filters
315 poly_float filtered_input = high_pre_filter_.tickBasic(input, current_high_pre_coefficient);
316 filtered_input = low_pre_filter_.tickBasic(input, current_low_pre_coefficient) - filtered_input;
317 poly_float scaled_input = filtered_input * 0.25f;
318
319 // All-pass retrieval
320 poly_float allpass_read1 = readAllpass(allpass_lookup1, allpass_offset1);
321 poly_float allpass_read2 = readAllpass(allpass_lookup2, allpass_offset2);
322 poly_float allpass_read3 = readAllpass(allpass_lookup3, allpass_offset3);
323 poly_float allpass_read4 = readAllpass(allpass_lookup4, allpass_offset4);
324
325 poly_float allpass_delay_input1 = feedback_read1 - allpass_read1 * kAllpassFeedback;
326 poly_float allpass_delay_input2 = feedback_read2 - allpass_read2 * kAllpassFeedback;
327 poly_float allpass_delay_input3 = feedback_read3 - allpass_read3 * kAllpassFeedback;
328 poly_float allpass_delay_input4 = feedback_read4 - allpass_read4 * kAllpassFeedback;
329
330 // Write to all-pass memory
331 int allpass_write_index = write_index_ & poly_allpass_mask_;
332 allpass_lookups_[0][allpass_write_index] = scaled_input + allpass_delay_input1;
333 allpass_lookups_[1][allpass_write_index] = scaled_input + allpass_delay_input2;
334 allpass_lookups_[2][allpass_write_index] = scaled_input + allpass_delay_input3;
335 allpass_lookups_[3][allpass_write_index] = scaled_input + allpass_delay_input4;
336
337 // Compute final all-pass outputs
338 poly_float allpass_output1 = allpass_read1 + allpass_delay_input1 * kAllpassFeedback;
339 poly_float allpass_output2 = allpass_read2 + allpass_delay_input2 * kAllpassFeedback;
340 poly_float allpass_output3 = allpass_read3 + allpass_delay_input3 * kAllpassFeedback;
341 poly_float allpass_output4 = allpass_read4 + allpass_delay_input4 * kAllpassFeedback;
342
343 // Combine lines
344 poly_float total_rows = allpass_output1 + allpass_output2 + allpass_output3 + allpass_output4;
345 poly_float other_feedback = poly_float::mulAdd(total_rows.sum() * 0.25f, total_rows, -0.5f);
346
347 // Mix partial feedback signals
348 poly_float write1 = other_feedback + allpass_output1;
349 poly_float write2 = other_feedback + allpass_output2;
350 poly_float write3 = other_feedback + allpass_output3;
351 poly_float write4 = other_feedback + allpass_output4;
352
353 // Cross-line coupling
354 poly_float::transpose(allpass_output1.value, allpass_output2.value,
355 allpass_output3.value, allpass_output4.value);
356 poly_float adjacent_feedback = (allpass_output1 + allpass_output2 + allpass_output3 + allpass_output4) * -0.5f;
357
358 write1 += adjacent_feedback[0];
359 write2 += adjacent_feedback[1];
360 write3 += adjacent_feedback[2];
361 write4 += adjacent_feedback[3];
362
363 // Apply high-shelf filtering
364 poly_float high_filtered1 = high_shelf_filters_[0].tickBasic(write1, current_high_coefficient);
365 poly_float high_filtered2 = high_shelf_filters_[1].tickBasic(write2, current_high_coefficient);
366 poly_float high_filtered3 = high_shelf_filters_[2].tickBasic(write3, current_high_coefficient);
367 poly_float high_filtered4 = high_shelf_filters_[3].tickBasic(write4, current_high_coefficient);
368 write1 = high_filtered1 + current_high_amplitude * (write1 - high_filtered1);
369 write2 = high_filtered2 + current_high_amplitude * (write2 - high_filtered2);
370 write3 = high_filtered3 + current_high_amplitude * (write3 - high_filtered3);
371 write4 = high_filtered4 + current_high_amplitude * (write4 - high_filtered4);
372
373 // Apply low-shelf filtering
374 poly_float low_filtered1 = low_shelf_filters_[0].tickBasic(write1, current_low_coefficient);
375 poly_float low_filtered2 = low_shelf_filters_[1].tickBasic(write2, current_low_coefficient);
376 poly_float low_filtered3 = low_shelf_filters_[2].tickBasic(write3, current_low_coefficient);
377 poly_float low_filtered4 = low_shelf_filters_[3].tickBasic(write4, current_low_coefficient);
378 write1 -= low_filtered1 * current_low_amplitude;
379 write2 -= low_filtered2 * current_low_amplitude;
380 write3 -= low_filtered3 * current_low_amplitude;
381 write4 -= low_filtered4 * current_low_amplitude;
382
383 // Interpolate decays
384 current_decay1 += delta_decay1;
385 current_decay2 += delta_decay2;
386 current_decay3 += delta_decay3;
387 current_decay4 += delta_decay4;
388
389 // Multiply by decays for feedback storage
390 poly_float store1 = current_decay1 * write1;
391 poly_float store2 = current_decay2 * write2;
392 poly_float store3 = current_decay3 * write3;
393 poly_float store4 = current_decay4 * write4;
394
395 // Store in feedback buffers
396 feedback_lookups1[0][write_index_] = store1[0];
397 feedback_lookups1[1][write_index_] = store1[1];
398 feedback_lookups1[2][write_index_] = store1[2];
399 feedback_lookups1[3][write_index_] = store1[3];
400 feedback_lookups2[0][write_index_] = store2[0];
401 feedback_lookups2[1][write_index_] = store2[1];
402 feedback_lookups2[2][write_index_] = store2[2];
403 feedback_lookups2[3][write_index_] = store2[3];
404 feedback_lookups3[0][write_index_] = store3[0];
405 feedback_lookups3[1][write_index_] = store3[1];
406 feedback_lookups3[2][write_index_] = store3[2];
407 feedback_lookups3[3][write_index_] = store3[3];
408 feedback_lookups4[0][write_index_] = store4[0];
409 feedback_lookups4[1][write_index_] = store4[1];
410 feedback_lookups4[2][write_index_] = store4[2];
411 feedback_lookups4[3][write_index_] = store4[3];
412
413 write_index_ = (write_index_ + 1) & feedback_mask_;
414
415 // Cross-line sum to feed forward
416 poly_float total_allpass = store1 + store2 + store3 + store4;
417 poly_float other_feedback_allpass = poly_float::mulAdd(total_allpass.sum() * 0.25f, total_allpass, -0.5f);
418
419 poly_float feed_forward1 = other_feedback_allpass + store1;
420 poly_float feed_forward2 = other_feedback_allpass + store2;
421 poly_float feed_forward3 = other_feedback_allpass + store3;
422 poly_float feed_forward4 = other_feedback_allpass + store4;
423
424 poly_float::transpose(store1.value, store2.value, store3.value, store4.value);
425 poly_float adjacent_feedback_allpass = (store1 + store2 + store3 + store4) * -0.5f;
426
427 feed_forward1 += adjacent_feedback_allpass[0];
428 feed_forward2 += adjacent_feedback_allpass[1];
429 feed_forward3 += adjacent_feedback_allpass[2];
430 feed_forward4 += adjacent_feedback_allpass[3];
431
432 // Sum final signals and push to StereoMemory for short-latency read
433 poly_float total = write1 + write2 + write3 + write4;
434 total += (feed_forward1 * current_decay1 + feed_forward2 * current_decay2 +
435 feed_forward3 * current_decay3 + feed_forward4 * current_decay4) * 0.125f;
436
437 memory_->push(total + utils::swapVoices(total));
438 audio_out[i] = current_wet * memory_->get(current_sample_delay) + current_dry * input;
439
440 // Update pre-delay increments
441 current_delay_increment += delta_delay_increment;
442 current_sample_delay += current_delay_increment;
443 current_sample_delay = utils::clamp(current_sample_delay, kMinDelay, kMaxSampleRate);
444
445 // Update interpolated parameters
446 current_dry += delta_dry;
447 current_wet += delta_wet;
448 current_high_coefficient += delta_high_coefficient;
449 current_high_amplitude += delta_high_amplitude;
450 }
451
452 // Save state for next block
453 sample_delay_increment_ = current_delay_increment;
454 sample_delay_ = current_sample_delay;
455 }
456
462 void Reverb::setSampleRate(int sample_rate) {
463 Processor::setSampleRate(sample_rate);
465 }
466
472 void Reverb::setOversampleAmount(int oversample_amount) {
473 Processor::setOversampleAmount(oversample_amount);
475 }
476
484 wet_ = 0.0f;
485 dry_ = 0.0f;
486 low_pre_filter_.reset(constants::kFullMask);
487 high_pre_filter_.reset(constants::kFullMask);
488 chorus_amount_ = utils::clamp(input(kChorusAmount)->at(0)[0], 0.0f, 1.0f) * kMaxChorusDrift;
489
490 // Reset shelves and decays
491 for (int i = 0; i < kNetworkContainers; ++i) {
492 low_shelf_filters_[i].reset(constants::kFullMask);
493 high_shelf_filters_[i].reset(constants::kFullMask);
494 decays_[i] = 0.0f;
495 }
496
497 // Clear all-pass memory
498 for (int n = 0; n < kNetworkContainers; ++n) {
499 for (int i = 0; i < max_allpass_size_; ++i)
500 allpass_lookups_[n][i] = 0.0f;
501 }
502
503 // Clear feedback memory
504 for (int n = 0; n < kNetworkSize; ++n) {
505 for (int i = 0; i < max_feedback_size_ + kExtraLookupSample; ++i)
506 feedback_memories_[n][i] = 0.0f;
507 }
508 }
509} // namespace vital
force_inline void reset(poly_mask reset_mask)
Resets the filter state for the voices indicated by a mask.
Definition one_pole_filter.h:36
static force_inline poly_float computeCoefficient(poly_float cutoff_frequency, int sample_rate)
Computes the filter coefficient for a given cutoff frequency and sample rate.
Definition one_pole_filter.h:131
force_inline poly_float tickBasic(poly_float audio_in, poly_float coefficient)
Processes a single sample in a basic (non-saturating) manner.
Definition one_pole_filter.h:56
Base class for all signal-processing units in Vital.
Definition processor.h:212
virtual void setOversampleAmount(int oversample)
Sets the oversampling amount and updates the effective sample rate.
Definition processor.h:293
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
bool inputMatchesBufferSize(int input=0)
Checks whether the buffer size of a particular input matches the size needed by this Processor.
Definition processor.cpp:42
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
virtual void setSampleRate(int sample_rate)
Updates the sample rate of this Processor (scaled by oversampling).
Definition processor.h:285
void hardReset() override
Resets the reverb, clearing buffer contents and resetting filters.
Definition reverb.cpp:483
static constexpr float kMinDelay
Minimum delay (in samples) used for certain time-domain operations.
Definition reverb.h:31
void setSampleRate(int sample_rate) override
Overrides base class to update reverb internal buffers at a new sample rate.
Definition reverb.cpp:462
force_inline poly_float readAllpass(const mono_float *lookup, poly_int offset)
Reads from the all-pass filters using integer offsets.
Definition reverb.h:215
static constexpr mono_float kT60Amplitude
Amplitude at which we consider the reverb tail to effectively end (T60).
Definition reverb.h:21
static constexpr int kDefaultSampleRate
Default sample rate used for internal buffer initialization.
Definition reverb.h:41
force_inline int getBufferScale(int sample_rate)
Computes a buffer scaling factor based on current sample rate.
Definition reverb.h:156
static constexpr int kMinSizePower
Minimum size exponent for reverb buffer scale.
Definition reverb.h:71
static constexpr int kExtraLookupSample
Extra samples in the buffer to allow for interpolation overflow.
Definition reverb.h:56
force_inline poly_float readFeedback(const mono_float *const *lookups, poly_float offset)
Reads from the feedback delay line with polynomial interpolation.
Definition reverb.h:197
force_inline float getSampleRateRatio(int sample_rate)
Returns the ratio of the current sample rate to kBaseSampleRate.
Definition reverb.h:146
void setOversampleAmount(int oversample_amount) override
Overrides base class to handle changes in oversampling factor.
Definition reverb.cpp:472
static constexpr int kNetworkContainers
Number of poly_float-sized containers covering the entire network.
Definition reverb.h:66
static const poly_float kFeedbackDelays[kNetworkContainers]
Fixed feedback delays (in samples) for each container.
Definition reverb.h:91
void setupBuffersForSampleRate(int sample_rate)
Adjusts internal buffer sizes and states for the given sample rate.
Definition reverb.cpp:79
@ kChorusFrequency
Frequency of the chorus LFO (Hz)
Definition reverb.h:107
@ kPreHighCutoff
Pre-filter high cutoff (MIDI note)
Definition reverb.h:101
@ kSize
Overall size (scales buffer size exponent)
Definition reverb.h:109
@ kChorusAmount
Amount of chorusing applied to feedback lines.
Definition reverb.h:106
@ kAudio
Audio input buffer.
Definition reverb.h:98
@ kHighGain
High-frequency attenuation (dB)
Definition reverb.h:105
@ kDelay
Additional pre-delay in samples.
Definition reverb.h:110
@ kPreLowCutoff
Pre-filter low cutoff (MIDI note)
Definition reverb.h:100
@ kHighCutoff
Internal feedback high cutoff (MIDI note)
Definition reverb.h:104
@ kLowCutoff
Internal feedback low cutoff (MIDI note)
Definition reverb.h:102
@ kDecayTime
Reverb decay time in seconds.
Definition reverb.h:99
@ kLowGain
Low-frequency attenuation (dB)
Definition reverb.h:103
@ kWet
Dry/wet mix.
Definition reverb.h:111
static constexpr mono_float kAllpassFeedback
Feedback coefficient for all-pass filter sections.
Definition reverb.h:26
static constexpr int kBaseSampleRate
Reference sample rate for the base reverb time calculations.
Definition reverb.h:36
static const poly_int kAllpassDelays[kNetworkContainers]
Fixed all-pass filter delays for each container.
Definition reverb.h:86
static constexpr int kBaseAllpassBits
Base bits used for the all-pass filters’ buffer size.
Definition reverb.h:61
static constexpr int kBaseFeedbackBits
Base bits used for feedback buffer size calculations.
Definition reverb.h:51
Reverb()
Constructs a Reverb processor with default configuration.
Definition reverb.cpp:41
static constexpr int kMaxSizePower
Maximum size exponent for reverb buffer scale.
Definition reverb.h:76
void process(int num_samples) override
Processes audio by pulling from the kAudio input buffer.
Definition reverb.cpp:113
static constexpr float kSizePowerRange
The exponent range (max minus min).
Definition reverb.h:81
void processWithInput(const poly_float *audio_in, int num_samples) override
Processes a block of audio using a provided input buffer.
Definition reverb.cpp:128
static constexpr int kNetworkSize
Number of feedback delay lines in the network.
Definition reverb.h:46
force_inline void wrapFeedbackBuffer(mono_float *buffer)
Wraps the feedback buffer to preserve continuity for polynomial interpolation.
Definition reverb.h:227
#define VITAL_ASSERT(x)
Definition common.h:11
Contains faster but less accurate versions of utility math functions, such as exponential,...
Declares classes for time-domain memory storage and retrieval with cubic interpolation.
const poly_mask kFullMask
A mask covering all lanes of a poly_float vector.
Definition synth_constants.h:257
const poly_mask kFirstMask
A mask identifying the first voice slots in a polyphonic vector.
Definition synth_constants.h:266
force_inline poly_float equalPowerFadeInverse(poly_float t)
The inverse equal-power fade from t to t+1.0.
Definition futils.h:448
force_inline poly_float equalPowerFade(poly_float t)
Produces an equal-power crossfade (sin-based) between 0.0 and 1.0.
Definition futils.h:436
force_inline mono_float pow(mono_float base, mono_float exponent)
Definition futils.h:141
force_inline poly_float cos(poly_float value)
Computes the cosine of each element (in radians).
Definition poly_utils.h:164
force_inline poly_float clamp(poly_float value, mono_float min, mono_float max)
Clamps each lane of a vector to [min, max].
Definition poly_utils.h:306
force_inline poly_float min(poly_float left, poly_float right)
Returns the minimum of two poly_floats lane-by-lane.
Definition poly_utils.h:334
force_inline poly_float pow(poly_float base, poly_float exponent)
Raises each lane in base to the power of the corresponding lane in exponent.
Definition poly_utils.h:388
force_inline poly_float dbToMagnitude(poly_float value)
Converts a dB value to linear magnitude (vectorized).
Definition poly_utils.h:149
force_inline poly_float swapVoices(poly_float value)
Swaps the first half of the lanes with the second half.
Definition poly_utils.h:437
force_inline poly_float midiNoteToFrequency(poly_float value)
Converts a MIDI note to a frequency (vectorized).
Definition poly_utils.h:123
force_inline poly_float sin(poly_float value)
Computes the sine of each element (in radians).
Definition poly_utils.h:159
force_inline poly_float interpolate(poly_float from, poly_float to, mono_float t)
Performs a linear interpolation between two poly_floats using a scalar t in [0..1].
Definition poly_utils.h:182
force_inline poly_float swapStereo(poly_float value)
Swaps the left and right channels of a stereo poly_float.
Definition poly_utils.h:411
Contains classes and functions used within the Vital synthesizer framework.
constexpr mono_float kPi
Pi constant.
Definition common.h:36
constexpr int kMaxSampleRate
Maximum expected sample rate in Hz.
Definition common.h:43
float mono_float
Definition common.h:33
poly_float * buffer
Pointer to the output buffer.
Definition processor.h:110
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
static force_inline void vector_call transpose(simd_type &row0, simd_type &row1, simd_type &row2, simd_type &row3)
Performs an in-place 4x4 transpose of four SSE/NEON registers containing float data.
Definition poly_values.h:1048
simd_type value
The underlying SIMD register for float.
Definition poly_values.h:1112
static force_inline float vector_call sum(simd_type value)
Computes the sum of all elements in a SIMD float register.
Definition poly_values.h:1020
static force_inline simd_type vector_call mulAdd(simd_type one, simd_type two, simd_type three)
Fused multiply-add operation: one = one + (two * three).
Definition poly_values.h:779
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56