Skip to content

JDSherbert/Audio-DSP-Procedural-Reverb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image

Audio: Procedural Reverb

Stars Badge Forks Badge Watchers Badge Issues Badge


Audio Processing License




Overview

A simple procedural reverb implemented in plain C++ using a comb filter. Intended as a learning resource for understanding how digital reverb works from first principles — no libraries, no black boxes, just the DSP.

Reverb simulates the way sound bounces around a physical space. Rather than recording a real room's impulse response (convolution reverb), procedural reverb approximates this behaviour algorithmically, making it lightweight and suitable for real-time audio processing.


How It Works

This implementation uses a single feedback comb filter, which is the fundamental building block of most reverb algorithms.

input ──►──────────────────────────────────────► output
          │                                  ▲
          ▼                                  │
       [ delay buffer (N samples) ] ──► × decayFactor ──►

On every sample:

  1. The oldest sample in the delay buffer (N samples ago) is read
  2. It is multiplied by decayFactor to attenuate it, simulating energy loss
  3. This attenuated value is added to the incoming input (feedback)
  4. The combined value is written back into the buffer
  5. The combined value is returned as output

Repeating this on every sample produces a series of exponentially decaying echoes — which our ears interpret as reverberation.

The buffer itself is a circular buffer: a fixed-size array with a write index that wraps around. This means no memory is ever allocated or freed during audio processing, which is critical for real-time performance.


Parameters

Parameter Type Range Description
decayFactor float (0.0, 1.0) How much each reflection attenuates. Near 1.0 = long tail, near 0.0 = very dry.
delayLength size_t > 0 Number of samples to delay. Controls perceived room size.

Choosing a delay length:

At a 44100Hz sample rate, a useful rule of thumb is:

Delay Length Approximate Time Perceived Size
2205 ~50ms Small room
4410 ~100ms Medium room
11025 ~250ms Large hall
22050 ~500ms Cathedral

Files

File Description
ProceduralReverb.h Class declaration and documentation
ProceduralReverb.cpp Comb filter implementation

Usage

Basic

// ~100ms delay at 44100Hz, moderate decay
Sherbert::ProceduralReverb reverb(0.75f, 4410);
 
float output = reverb.ProcessSample(input);

Wet/Dry Mix

ProcessSample returns the fully wet signal. Blend it with the dry input yourself to control the mix:

const float wetAmount = 0.4f; // 0.0 = fully dry, 1.0 = fully wet
 
float wet    = reverb.ProcessSample(input);
float output = (wetAmount * wet) + ((1.0f - wetAmount) * input);

Resetting State

Call reset() when playback stops or the effect is bypassed to prevent stale samples bleeding into the next session:

reverb.reset();

Changing Parameters at Runtime

Both parameters can be changed at runtime. Note that either setter internally calls reset() to avoid artefacts from the buffer containing samples recorded at the old settings:

reverb.setDecayFactor(0.9f);  // Longer tail
reverb.setDelayLength(11025); // Larger room

API Reference

Method Description
ProceduralReverb(decayFactor, delayLength) Construct with initial parameters.
ProcessSample(input) Process one sample. Call once per sample in your audio loop.
reset() Clear the delay buffer and reset the write head.
setDecayFactor(value) Update decay factor at runtime. Calls reset().
setDelayLength(value) Update delay length at runtime. Calls reset().
getDecayFactor() Returns current decay factor.
getDelayLength() Returns current delay length in samples.

Limitations & Next Steps

This is an intentionally minimal implementation for learning purposes. A single comb filter is a good starting point but has limitations worth knowing about:

Metallic colouration: a single comb filter produces a characteristic "metallic" or "pitched" resonance because the echoes are evenly spaced. Real reverb algorithms (like the Schroeder reverberator) use multiple comb filters at prime-number delay lengths to break up this regularity.

No early reflections: real rooms produce a burst of distinct early reflections before the diffuse reverb tail begins. This implementation goes straight to the tail.

No diffusion: allpass filters are typically chained after comb filters to scatter the echo density and produce a smoother, more natural tail.

If you want to explore further, the natural next steps from here are:

  • Multiple parallel comb filters (Schroeder reverberator)
  • Allpass filter diffusion stages
  • Separate early reflection delay lines
  • High-frequency damping (a low-pass filter in the feedback path)

About

Simple C++ implementation of a basic Procedural Reverb algorithm. Includes an example usage case with a simulated sinusoidal signal.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages