From 45e2867877f65ece4b805e2680dd00fea674e6e2 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 3 Jun 2026 19:01:57 -0500 Subject: [PATCH] stereo reverb --- include/audio.h | 16 +++++------ src/audio.cpp | 71 ++++++++++++++++++++++++++++++------------------- src/ui.cpp | 8 +++--- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/include/audio.h b/include/audio.h index 7afa9a8..bc86a48 100644 --- a/include/audio.h +++ b/include/audio.h @@ -8,8 +8,8 @@ namespace audio { constexpr int channels = 2; struct DelayFilter { - float * buffer; - int index; + float * buffers[channels]; + int indexes[channels]; const int maxDelay; int delay; @@ -18,23 +18,23 @@ namespace audio { DelayFilter(int maxDelay, int delay, float gain); void reset(); - virtual float feed(float value) = 0; + virtual float feed(int channel, float value) = 0; }; struct FeedbackCombFilter : DelayFilter { FeedbackCombFilter(int maxDelay, int delay, float gain); - float feed(float value) override; + float feed(int channel, float value) override; }; struct FeedforwardCombFilter : DelayFilter { public: FeedforwardCombFilter(int maxDelay, int delay, float gain); - float feed(float value) override; + float feed(int channel, float value) override; }; struct AllpassFilter : DelayFilter { AllpassFilter(int maxDelay, int delay, float gain); - float feed(float x) override; + float feed(int channel, float x) override; }; using FBCF = FeedbackCombFilter; @@ -52,11 +52,11 @@ namespace audio { Reverb(DelayFilter * cf, DelayFilter * ap); void reset(); - lr feed(float x); + lr feed(int channel, float x); }; extern int reverbIndex; - extern Reverb * reverbs[]; + extern Reverb reverbs[]; extern int const reverbsCount; extern float wetGain; diff --git a/src/audio.cpp b/src/audio.cpp index 2fb1610..5715c94 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -34,16 +34,20 @@ namespace audio { void DelayFilter::reset() { - memset(buffer, 0, maxDelay * (sizeof (float))); + for (int i = 0; i < channels; i++) { + memset(buffers[i], 0, maxDelay * (sizeof (float))); + indexes[i] = 0; + } } DelayFilter::DelayFilter(int maxDelay, int delay, float gain) - : index(0) - , maxDelay(maxDelay) + : maxDelay(maxDelay) , delay(delay) , gain(gain) { - buffer = (float *)malloc(maxDelay * (sizeof (float))); + for (int i = 0; i < channels; i++) { + buffers[i] = (float *)malloc(maxDelay * (sizeof (float))); + } reset(); } @@ -55,8 +59,11 @@ namespace audio { : DelayFilter(maxDelay, delay, gain) {} - float FeedbackCombFilter::feed(float value) + float FeedbackCombFilter::feed(int channel, float value) { + int & index = indexes[channel]; + float * buffer = buffers[channel]; + float y = gain * value + gain * buffer[index]; buffer[index] = y; index = (index + 1) % delay; @@ -71,8 +78,11 @@ namespace audio { : DelayFilter(maxDelay, delay, gain) {} - float FeedforwardCombFilter::feed(float value) + float FeedforwardCombFilter::feed(int channel, float value) { + int & index = indexes[channel]; + float * buffer = buffers[channel]; + float y = gain * value + gain * buffer[index]; buffer[index] = value; index = (index + 1) % delay; @@ -87,8 +97,11 @@ namespace audio { : DelayFilter(maxDelay, delay, gain) {} - float AllpassFilter::feed(float x) + float AllpassFilter::feed(int channel, float x) { + int & index = indexes[channel]; + float * buffer = buffers[channel]; + float v = x + -gain * buffer[index]; float y = buffer[index] + gain * v; buffer[index] = v; @@ -108,16 +121,16 @@ namespace audio { ap[i].reset(); } - lr Reverb::feed(float x) + lr Reverb::feed(int channel, float x) { for (int i = 0; i < apCount; i++) { - x = ap[i].feed(x); + x = ap[i].feed(channel, x); } - float x0 = cf[0].feed(x); - float x1 = cf[1].feed(x); - float x2 = cf[2].feed(x); - float x3 = cf[3].feed(x); + float x0 = cf[0].feed(channel, x); + float x1 = cf[1].feed(channel, x); + float x2 = cf[2].feed(channel, x); + float x3 = cf[3].feed(channel, x); float s0 = x0 + x2; float s1 = x1 + x3; @@ -139,7 +152,6 @@ namespace audio { static AP forwardAP[3]{AP{2500, 2017, 0.7f}, AP{2500, 647, 0.7f}, AP{2500, 137, 0.7f}}; - Reverb forwardReverb(forwardCF, forwardAP); static FBCF backCF[4]{FBCF{10000, 3229, 0.733f}, // 1687 FBCF{10000, 3079, 0.802f}, // 1601 @@ -148,12 +160,11 @@ namespace audio { static AP backAP[3]{AP{1500, 661, 0.7f}, // 347 AP{1500, 257, 0.7f}, // 113 AP{1500, 71, 0.7f}}; - Reverb backReverb(backCF, backAP); int reverbIndex = 0; - Reverb * reverbs[] = { - &backReverb, - &forwardReverb, + Reverb reverbs[] = { + {backCF, backAP}, + {forwardCF, forwardAP}, }; int const reverbsCount = (sizeof (reverbs)) / (sizeof (reverbs[0])); @@ -198,7 +209,7 @@ namespace audio { audio_instances_count = 0; for (int i = 0; i < reverbsCount; i++) - reverbs[i]->reset(); + reverbs[i].reset(); for (int i = 0; i < mixChannelCount; i++) { mixChannelGain[i] = 1.0f; @@ -539,15 +550,21 @@ namespace audio { // audio configuration "B" // mono reverberation for (int i = 0; i < half_period_samples; i++) { - float value = channel_buffer[mix_channel::voice][i * channels + 0]; + float mix[2] = { 0, 0 }; + for (int ch = 0; ch < channels; ch++) { + float value = channel_buffer[mix_channel::voice][i * channels + ch]; - lr wet; - assert(reverbIndex >= 0 && reverbIndex < reverbsCount); - wet = reverbs[reverbIndex]->feed(value); - float left = value * dryGain + wet.l * wetGain; - float right = value * dryGain + wet.r * wetGain; - channel_buffer[mix_channel::voice][i * channels + 0] = left; - channel_buffer[mix_channel::voice][i * channels + 1] = right; + assert(reverbIndex >= 0 && reverbIndex < reverbsCount); + lr wet = reverbs[reverbIndex].feed(ch, value); + + float left = value * dryGain + wet.l * wetGain; + float right = value * dryGain + wet.r * wetGain; + mix[0 ^ ch] += left; + mix[1 ^ ch] += right; + } + + for (int ch = 0; ch < channels; ch++) + channel_buffer[mix_channel::voice][i * channels + ch] = mix[ch]; } for (int i = 0; i < half_period_samples; i++) { diff --git a/src/ui.cpp b/src/ui.cpp index 6b5972b..10c5882 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -74,15 +74,15 @@ namespace ui reverberatorSliders[i].ap[j] = widget::DelayGainSlider(allpassLabels[j], allpassLeft, top + ySpace * j, - 1, audio::reverbs[i]->ap[j].maxDelay, &audio::reverbs[i]->ap[j].delay, - 0.0, 1.0, &audio::reverbs[i]->ap[j].gain); + 1, audio::reverbs[i].ap[j].maxDelay, &audio::reverbs[i].ap[j].delay, + 0.0, 1.0, &audio::reverbs[i].ap[j].gain); } for (int j = 0; j < 4; j++) { reverberatorSliders[i].comb[j] = widget::DelayGainSlider(combLabels[j], combLeft, top + ySpace * j, - 100, audio::reverbs[i]->cf[j].maxDelay, &audio::reverbs[i]->cf[j].delay, - 0.0, 1.0, &audio::reverbs[i]->cf[j].gain); + 100, audio::reverbs[i].cf[j].maxDelay, &audio::reverbs[i].cf[j].delay, + 0.0, 1.0, &audio::reverbs[i].cf[j].gain); } } }