Dereverberation
Short answer. Dereverberation removes the late reverberant tail from a signal. It cannot undo the smearing the early reflections already applied, because those reflections overlap the direct sound and there is no way to separate them after the fact. The workhorse method is spectral subtraction against an estimate of the late-reverberation power spectrum, obtained by exponentially smoothing past frames of the observed spectrum. It runs per channel before the ASR front end, and the only honest metric is a before-and-after comparison on the same signal.
How to do it
Decide which part of the reverberation you are removing
The early reflections — roughly the first 50 ms — fuse perceptually with the direct sound and carry information about room size rather than about the speech. The late tail is what makes speech sound distant, what fills the gaps between words, and what a recognizer actually struggles with. Every practical dereverberation method targets the late tail.
That is a real limitation, not a tuning detail: the early reflections have already convolved with the direct sound, and separating them requires a second microphone or a strong statistical assumption. If your problem is early-reflection smearing, a beamformer or a neural enhancement model is the right tool.
Estimate the late tail from the recent past
The standard assumption, from the statistical reverberation model: the late reverberation power at frame m is a weighted sum of the observed power over the previous frames, with weights that decay exponentially. The per-frame power factor is exp(-3 ln(10) hop / (SR x RT60)), which for a 10 ms hop and a 0.7 s RT60 is about 0.91, so a memory of roughly 30 frames (300 ms) captures three time constants.
Get RT60 wrong and the algorithm fails in a specific way: too small and the estimate is too short, leaving audible reverb; too large and the estimate is too long, subtracting speech energy along with the reverb and hollowing out the signal. If you do not know RT60, estimate it from the recording, or expose the value as a parameter and tune it on the downstream metric.
Subtract with a floor, and smooth the gain
Compute a per-bin gain from the ratio of estimated late power to observed power, subtract, and floor the result. The floor is what keeps the output from developing musical noise — isolated time-frequency spikes that appear when the gain jumps around frame to frame. A floor of 0.1 to 0.3 in amplitude (that is, -20 to -10 dB of maximum attenuation) is the usual range.
Smooth the gain across frames as well, with a first-order filter and a coefficient around 0.5 to 0.9. This is the cheapest fix for musical noise and costs a fraction of a frame of latency. Smoothing across a few frequency bins helps too, because the late-reverberation estimate is noisy per bin.
Measure it honestly, which is harder than it sounds
C50 is the ratio of energy in the first 50 ms to the energy after it, in dB, and it is the standard objective measure — but it is defined on an impulse response, and you usually have speech, not an impulse. Applying it to speech requires a known reference or a filtered proxy, and the proxy numbers are not comparable across implementations.
A crude alternative that works on any signal is a peak-to-floor ratio: the mean short-term energy divided by the median, in dB. Reverberation raises the floor between speech events, so the ratio falls; successful dereverberation raises it back. It is not C50 and should not be quoted as C50, but it needs no reference signal.
Whatever you use, run the same measurement on the input and the output. A dereverberation result reported without the input number is not a result.
Tune on the downstream task, because the metric can improve while the product gets worse
Aggressive suppression improves every energy-ratio metric and removes the low-energy consonants along with the reverb. Word-final stops, fricatives, and unstressed syllables sit near the floor, and they are the parts a recognizer needs. It is normal to see C50 improve by 4 dB and WER get worse by 3 points.
The practical procedure is to sweep the floor parameter and the memory length, evaluate WER at each setting, and pick the setting that wins on WER rather than on the acoustic metric. If you cannot run a recognizer, use the mildest setting that produces a visible improvement.
Code
A late-reverberation spectral subtraction on a synthetic reverberant signal, with a peak-to-floor energy ratio measured before and after.
import numpy as np
from scipy.signal import fftconvolve
SR, NFFT, HOP = 16000, 512, 160
WIN = np.hanning(NFFT)
def speech_like(dur=3.0, seed=1):
r = np.random.default_rng(seed)
t = np.arange(int(SR * dur)) / SR
f0 = 140 + 12 * np.sin(2 * np.pi * 0.5 * t)
x = sum(np.sin(2 * np.pi * k * f0 * t) / k for k in range(1, 25))
x = x * (0.5 + 0.5 * np.sin(2 * np.pi * 3.0 * t)) ** 2
x = x + 0.005 * r.normal(size=t.size)
return (x / np.max(np.abs(x))).astype(np.float32)
def reverb(x, rt60=0.7, seed=2):
r = np.random.default_rng(seed)
t = np.arange(SR) / SR
ir = r.normal(size=SR) * 10 ** (-3.0 * t / rt60) * 0.5
ir[:int(SR * 0.004)] = 0.0
ir[int(SR * 0.004)] += 1.0
return (fftconvolve(x, ir)[:len(x)] / np.max(np.abs(x))).astype(np.float32)
def stft(y):
n = 1 + (len(y) - NFFT) // HOP
return np.stack([np.fft.rfft(y[i * HOP:i * HOP + NFFT] * WIN)
for i in range(n)], axis=1)
def istft(S):
out = np.zeros((S.shape[1] - 1) * HOP + NFFT)
wsum = np.zeros_like(out)
for i in range(S.shape[1]):
out[i * HOP:i * HOP + NFFT] += np.fft.irfft(S[:, i], n=NFFT) * WIN
wsum[i * HOP:i * HOP + NFFT] += WIN ** 2
return out / np.maximum(wsum, 1e-6)
def dereverb(y, rt60=0.7, floor=0.2):
S = stft(y)
P = np.abs(S) ** 2
decay = np.exp(-3.0 * np.log(10) * HOP / (SR * rt60)) # per-frame power decay
L = int(np.ceil(3.0 / (1.0 - decay))) # about 3 time constants
late = np.zeros_like(P)
for i in range(L, P.shape[1]):
late[:, i] = (P[:, i - L:i] * decay ** np.arange(L)[::-1]).sum(axis=1)
gain = np.sqrt(np.maximum(1.0 - late / (P + 1e-12), floor ** 2))
return istft(S * gain)[:len(y)].astype(np.float32)
def peak_to_floor(x):
e = np.abs(x) ** 2
return float(10.0 * np.log10((e.mean() + 1e-12) / (np.median(e) + 1e-12)))
decay = np.exp(-3.0 * np.log(10) * HOP / (SR * 0.7))
print('per-frame power decay %.4f -> memory length %d frames (%.0f ms)'
% (decay, int(np.ceil(3.0 / (1.0 - decay))),
1000.0 * HOP * np.ceil(3.0 / (1.0 - decay)) / SR))
x = speech_like()
y = reverb(x)
z = dereverb(y)
print('peak-to-floor, clean : %5.1f dB' % peak_to_floor(x))
print('peak-to-floor, reverberant: %5.1f dB' % peak_to_floor(y))
print('peak-to-floor, dereverbed : %5.1f dB' % peak_to_floor(z))
print('energy removed by the dereverberation: %.1f%%'
% (100.0 * (1.0 - (z ** 2).sum() / (y ** 2).sum()))) - pip install numpy scipy. numpy and scipy only, no librosa.
- Expected output: the reverberant peak-to-floor is 5 to 12 dB below the clean value, and the dereverbed value moves most of the way back. If the dereverbed value overshoots the clean value, the floor is too low and you are hearing musical noise rather than a cleaner signal.
- The last line prints the energy removed. Anything above about 25% means the floor is too aggressive for this signal, and on real speech that is where consonants start disappearing.
- Raise the floor from 0.2 to 0.5 and rerun: less energy removed, a smaller metric gain, and — on real speech — usually a better WER. That trade is the whole design decision on this page.
Where this goes wrong
Using an RT60 that does not match the recording
The memory length and the decay factor both come from RT60. Guess it 2x too large and the estimate includes speech from earlier words, which gets subtracted as if it were reverb; guess it 2x too small and most of the tail survives. If you do not have the value, estimate it from the recording, and if you cannot, expose it as a parameter and tune it on WER rather than fixing it at a default.
Setting the floor to zero
Without a floor, the gain goes to zero in any bin where the estimate exceeds the observation, which produces isolated surviving bins and a characteristic warbling artifact. The artifact is not only unpleasant — it is spectrally structured noise that a recognizer has never seen, so it degrades WER more than the reverberation did. A floor of 0.1 to 0.3 in amplitude is not a compromise, it is a requirement.
Reporting a metric improvement without the input measurement
A dereverberation result quoted as "C50 of 8 dB after processing" says nothing, because you cannot tell whether the input was at 2 dB or at 7 dB. Always report the pair, on the same file, with the same measurement code. This is also how you catch a bug in the measurement itself: if the input number is implausible for the RT60, the metric is wrong, not the algorithm.
Processing each channel independently and calling it multichannel
Applying a single-channel dereverberation to every channel of an array destroys the phase relationships between channels, which is the information a beamformer or a spatial model needs. If the downstream stage is multichannel, use a multichannel method (WPE operates on the inter-channel covariance) or leave the signal alone. Single-channel dereverberation before beamforming is a common pipeline that quietly removes most of the array benefit.
Assuming the recognizer wants the cleanest possible signal
ASR models are trained on real recordings, and an aggressively processed signal is out of distribution even when it is objectively cleaner. A model trained on noisy data can do worse on denoised test audio. If you process the test set, either process the training set the same way or fine-tune on processed data — a mismatch between processed test audio and unprocessed training audio costs more than the reverberation did.
When to buy the data instead of building the pipeline
Buy when the reverberation in your deployment cannot be simulated with the parameters you have — specifically when you need real early-reflection structure rather than an exponential tail, which is the case for anything involving a microphone array or a speaker-verification system. Buy when you need matched pairs, meaning the same utterance recorded both anechoically and in the target room, because those are what let you measure dereverberation honestly rather than with a proxy. Building it yourself is right for testing a single-channel algorithm on a known RT60: the synthetic RIR in the previous page reproduces the energy decay exactly, which is all a late-tail subtraction needs.
More technical reference
-
ASR Evaluation Metrics
asr evaluation metrics
-
Zero-Crossing Rate
zero crossing rate
-
Speech Enhancement
speech enhancement
-
Audio Sample Rate
audio sample rate
-
WAV vs MP3 for Training Data
wav vs mp3
-
Opus Audio Codec
opus audio codec
Need data for Dereverberation?
Tell us the language, the hours, and what the data needs to look like. You will get a real number and a real timeline — not a range. If we cannot source it well, we will tell you that instead.
- Pilot batch before the full run, so problems surface early.
- Consent documentation delivered with the data.
- No medical or clinical data. No recorded telephone calls.