Measure a room, then remove its tail.
On this page
Speech recorded across a room sounds distant because the walls send back delayed copies of it. The room is one filter, its impulse response h: this page describes h, measures how long it rings, and removes its late tail from a single recording.
Clap once in a lecture hall and the microphone records the clap, then the hall answering it. That answer is the room’s impulse response h. A room is a linear time-invariant (LTI) filter: speak a word instead, and every sample of the word gets the same answer, so the room’s tail runs on after each sound. The room material on this page follows the thesis by Anushiravani (2016).
The ear hears copies of a sound as separate echoes when they arrive about 50 to 100 ms apart; closer copies fuse into reverberation. A film editor matching a dubbed line to a scene’s echo can read those copies off a recording: find the peaks of the recording’s log power over time, keep each peak’s delay and height relative to the first sound, and rebuild the echo as delayed, smaller clicks. The simplest room response is exactly that, a few clicks far apart, here at a 16 kHz sampling rate.
A real room sends back thousands of copies. The first to arrive is the direct sound, then a few distinct early reflections off the nearest walls, then so many reflections that they merge into a dense tail that dies away smoothly.
Early reflections colour the sound but help a listener understand it. The late tail smears each sound over the next, and that is the part section 9 removes.
The standard measure of a room is how long its tail takes to fall by 60 dB, called T60. Offices sit around 0.2 to 0.6 s and large churches near 3 s (Pacheco and Seara 2008). A bigger room rings longer, because sound travels further between walls; softer walls ring shorter, because each bounce loses more. The room of Figure 5 is a 5 m by 4 m by 3 m box.
| average absorption a | T60 |
|---|---|
| 0.1, bare walls | 1.028 s |
| 0.2 | 0.514 s |
| 0.4, soft walls | 0.257 s |
The direct sound weakens with distance, 6 dB per doubling, while the room’s tail fills the space at about the same level everywhere. Somewhere the two are equal: that distance is the critical distance dc.
The dense tail looks like noise, and it falls by the same number of decibels every second. Polack’s model writes it as exactly that: white noise, whose power is spread evenly over frequency, under a falling exponential. Take T60 = 0.5 s at fs = 16 kHz: the envelope falls 30 dB in 0.25 s and 60 dB in 0.5 s.
| sample n | time | envelope |
|---|---|---|
| 0 | 0 s | 1, 0 dB |
| 4,000 | 0.25 s | 0.032, −30 dB |
| 8,000 | 0.5 s | 0.001, −60 dB |
Real tails die faster at high frequencies, where air and soft surfaces absorb more, so T60 is usually given per frequency band. Simulators that need the early reflections too place mirror images of the talker behind each wall, the image method of Allen and Berkley (1979).
A measured tail is too noisy to read a slope off directly. Schroeder’s trick is to add up, at each sample, all the energy still to come. Take an illustrative envelope sampled every 50 ms, 1, 0.5, 0.25 and 0.125: the energies are their squares, and the sums from each time to the end fall close to a straight line in dB (the last step bends because the sum stops).
Background noise under a tail adds energy that never fades, so the EDC flattens onto a noise floor. Where that floor sits above −60 dB, the curve never reaches −60 dB, which is why the fit stops at −35 dB.
Figure 9. 1The fit band, doubled, reads T60 until a near noise floor bends the fit over 5% long (simulated tail).
To measure h, play a click and record. A popped balloon is the classic click, but the thesis finds its “dynamic range and the frequency range … are not broad enough”, and repeated pops “vary too much”. Acousticians play a sine sweep instead, a tone that glides from low to high frequency, loud and the same every time.
Because convolution order does not matter, convolving the recording with the sweep’s inverse pairs the sweep with its inverse, which leaves a click, and a click convolved with h is h itself (Figure 10). The loudspeaker and microphone add their own responses, so flat devices are used or their responses are removed by deconvolution. Measured responses are shared in databases such as the Aachen Impulse Response (AIR) database (Jeub et al. 2009), so a dry sound can be placed in a real room by convolution.
The goal is the dry x from one wet recording y. Inverting h directly fails for three reasons. A room response is usually not minimum phase, meaning a response whose inverse is both stable and causal, so its exact inverse needs future samples or grows without bound. Frequencies where the room nearly cancels itself blow up the noise when divided. And Wiener deconvolution needs h, which one recording does not give.
The first fix is position: a microphone inside dc (section 4) records more talker than room. When that is not possible, the methods of Figure 11 remain.
| Method | What is reported |
|---|---|
| Wiener deconvolution, divides out a known h | needs h; useful only on the room’s minimum-phase part (The Wiener Filter, section 6) |
| Beamforming, several microphones aimed at the talker | used in hearing aids and headsets |
| Per-band noise gate, mutes a band once it falls quiet | works on drums, fails on speech, can pump (audible level surges) |
| Complex cepstrum, splits room from voice in the log spectrum | “little use in practical cases” |
| Linear prediction residual, what a speech predictor misses | same problem as the cepstrum |
| Non-negative matrix factorization (NMF) with a room model | learns the room per band (NMF, section 7.4) |
| Late-tail spectral subtraction | one microphone, needs T60; tested at 0.34 to 1.7 s (section 9) |
Lebart, Boucher and Denbigh (2001) treat the late tail as noise that can be predicted. In each frequency bin of the short-time Fourier transform (STFT), the tail now is whatever was in that bin a moment ago, faded by the room’s decay. They tested one microphone on rooms with T60 from 0.34 to 1.7 s and reported large gains for a speech recognizer.
Take T60 = 0.5 s and a split Td = 40 ms between early and late. In 40 ms the power falls to 0.331 of itself, −4.80 dB. An illustrative bin that held power 2.0 then is predicted to hold 0.662 of late tail now.
Figure 12. 1Bin 3 was loudest Td ago, so no bin loses more; bin 2 keeps the most (illustrative powers).
| Knob | Raise it | Lower it |
|---|---|---|
| T60, known or estimated (section 6) | removes more tail | leaves more tail |
| Td, commonly 40 ms | leaves more early reflections | starts removing reflections that help a listener |
| β, no single default | more tail, less musical noise | less tail, more musical noise |
Late-tail removal runs frame by frame and causally, in front of a speech recognizer or as offline cleanup. The paper used 128-point Hamming frames with 75% overlap, zero-padded to 256. On top of the STFT it costs one exponential factor per recording and one division per bin per frame.
A simulated room from section 5, a stand-in dry sound through it, and T60 read back with the T30 fit.
import numpy as np, scipy.signal as sps
fs, t60 = 16000, 0.5
n = np.arange(int(1.2 * t60 * fs))
zeta = 3 * np.log(10) / (t60 * fs)
h = np.random.default_rng(0).standard_normal(n.size) * np.exp(-zeta * n)
dry = np.random.default_rng(1).standard_normal(fs)
wet = sps.fftconvolve(dry, h)
energy = np.cumsum(h[::-1] ** 2)[::-1]
edc_db = 10 * np.log10(energy / energy[0])
i5, i35 = np.argmax(edc_db <= -5), np.argmax(edc_db <= -35)
slope = np.polyfit(n[i5:i35] / fs, edc_db[i5:i35], 1)[0]
t30 = -60 / slope # 0.496
Late-tail removal on that wet signal, with the settings of the walkthrough. The frames are this page’s choice, not the paper’s: 512 samples with a hop of 128 samples, 8 ms, so Td is exactly 5 frames.
f, t, Y = sps.stft(wet, fs, nperseg=512, noverlap=384)
Py = np.abs(Y) ** 2
td, hop = 0.04, 512 - 384
d = round(td * fs / hop)
delta = 3 * np.log(10) / t60
p_late = np.zeros_like(Py)
p_late[:, d:] = np.exp(-2 * delta * td) * Py[:, :-d]
beta = 0.1
G = np.maximum(1 - p_late / np.maximum(Py, 1e-12), beta)
_, clean = sps.istft(np.sqrt(G) * Y, fs, nperseg=512, noverlap=384)
pyroomacoustics (Scheibler et al. 2018) builds rooms with the image method. Its inverse_sabine function turns a target T60 into wall absorption, and a room’s measure_rt60 reads it back from the simulated response. Its default sampling rate is 8 kHz, so set the room to 16 kHz for this page’s numbers.
| What you see | Cause | Fix |
|---|---|---|
| The EDC flattens before −60 dB | Background noise under the tail | Fit −5 to −35 dB and double (Figure 9) |
| Convolved audio sounds pitched or wrong | Kernel and recording at different sampling rates | Resample first (Aliasing, section 5) |
| One T60 sounds wrong across the band | Highs die faster than lows | Use T60 per band (section 5) |
| Twinkling tones after late-tail removal | Musical noise from bins pushed to the floor | Raise β (Spectral Subtraction for Denoising, section 6) |
Learning the room filter while a call runs is a different job, done by an echo canceller.