DEV Community

SUDIP MONDAL
SUDIP MONDAL

Posted on

How I implemented Schroeder Reverb in real-time audio (C++17)

What is Schroeder Reverb?

Schroeder Reverb is a classic real-time reverberation algorithm that simulates room acoustics using parallel comb filters followed by series allpass filters. It's computationally efficient while producing convincing reverb effects, making it ideal for real-time audio applications.

The Algorithm

The Schroeder reverberator consists of:

  1. 4 parallel comb filters - These create the initial reflections
  2. 2 series allpass filters - These add diffusion and smear the sound

Each comb filter uses a feedback topology:

float CombFilter::process(float input) {
    // Read from circular buffer at delay time
    float delayed = buffer[writePos - delayTime] & BUFFER_MASK;
    float output = delayed * (-feedback);

    // Mix input with feedback
    float mixedInput = input + output;

    // Write to buffer
    buffer[writePos] = mixedInput;
    writePos = (writePos + 1) & BUFFER_MASK;

    return delayed;
}
Enter fullscreen mode Exit fullscreen mode

Implementation Details

Comb Filter Delays

Typical delay times (in milliseconds) for 44.1kHz sample rate:

Comb 1: 25.31ms
Comb 2: 26.94ms
Comb 3: 28.63ms
Comb 4: 30.47ms
Enter fullscreen mode Exit fullscreen mode

These are prime number-based delays to avoid comb resonances at audio frequencies.

Allpass Filters

Allpass filters are crucial for diffusion:

float AllpassFilter::process(float input) {
    float delayed = buffer[writePos - delayTime] & BUFFER_MASK;
    float output = delayed + (input * -feedback);

    buffer[writePos] = input + (delayed * feedback);
    writePos = (writePos + 1) & BUFFER_MASK;

    return output;
}
Enter fullscreen mode Exit fullscreen mode

Allpass delays are typically shorter (5-17ms) than comb filters.

Key Implementation Tips

  1. Use circular buffers - Allocate based on max delay time at your sample rate
  2. Feedback coefficient - Keep between 0.5-0.9 to avoid instability
  3. Dry/wet mix - Combine the dry input with the reverb output (typically 30% wet)
  4. Denormal handling - Add small noise floor to prevent denormal CPU stalls
float output = drySignal * (1 - wetMix) + 
               reverbSignal * wetMix;

// Prevent denormals
if (fabs(output) < 1e-6) output = 0;
Enter fullscreen mode Exit fullscreen mode

Performance

Schroeder reverb is extremely efficient:

  • 6 filters total (4 comb + 2 allpass)
  • ~3KB memory for buffers at 44.1kHz
  • ~0.1ms latency with optimized code
  • Minimal CPU usage - runs easily on embedded systems

Tuning the Sound

Tweak these parameters for different room characteristics:

  • Damping: Low-pass filter in feedback path (reduces high frequencies)
  • Feedback amount: Higher values = longer decay
  • Wet/Dry mix: More wet = more obvious reverb
  • Delay times: Pre-calculated for room size

The beauty of Schroeder reverb is its simplicity and efficiency. With just a few parameters, you can create convincing spatial effects perfect for guitar sims, synthesizers, or any real-time audio application.


Want to see it in action? Check out Amplitron's full implementation in the main article!

Top comments (0)