Room Impulse Response
Short answer. A room impulse response (RIR) is the recording of what a room does to a click: a direct arrival, then reflections that decay over time. Convolve clean speech with an RIR and you get that speech as it would sound in that room, which is how reverberant training data is made from clean data. The number that summarizes an RIR is RT60, the time for the reverberant energy to fall 60 dB. The number that predicts ASR difficulty is the direct-to-reverberant ratio, because a long RT60 with a strong direct path is easier than a short one without.
How to do it
What is actually in the file
One channel, or several for an array, sampled at the rate you will convolve at. The samples are ordered by arrival time: a propagation delay equal to the source-to-microphone distance divided by the speed of sound (3 ms per meter), then the direct sound, then early reflections in roughly the first 50 ms, then the late tail.
Length is a practical decision: it should cover RT60 plus a margin so the convolution does not truncate the decay, which for a normal room is 0.3 to 1.5 s. An RIR shorter than its own RT60 has been silently high-passed in the time domain.
Where RIRs come from, and what each source costs you
Measured: play a sine sweep or an MLS sequence through a loudspeaker in the room, record it with the microphone, and deconvolve. This captures the real room including furniture and diffraction, but it fixes one source and one receiver position per measurement, so covering a room means dozens of sweeps.
Simulated: the image-source method places mirrored copies of the source behind each wall for the early reflections, and a statistical or feedback-delay-network model generates the late tail. Any geometry and any source position, at the cost that walls are flat planes and furniture is absent.
Synthetic: exponentially decaying noise, which is what the code below does. It reproduces the energy decay exactly and has no directional structure at all. Good enough for testing a dereverberation algorithm; not for training a beamformer, which needs an arrival direction.
RT60, and the estimate you can compute from the file
RT60 is the time for the reverberant energy to decay by 60 dB, and the physical estimate is Sabine: RT60 = 0.161 V / A, with V in cubic meters and A the total absorption in sabins. A 100 cubic meter room with 15 sabins of absorption gives 0.161 x 100 / 15 = 1.07 s. A carpeted living room is 0.3 to 0.5 s; a gymnasium is 2 s or more.
From a recorded RIR, use the Schroeder backward integral: square the samples, integrate backwards from the end of the file to get the energy decay curve, convert to dB, fit a line between -5 dB and -35 dB, and extrapolate the slope to -60 dB. Fitting there rather than from 0 to -60 is deliberate: the first few dB are contaminated by the direct sound and the last 25 dB by the measurement noise floor.
Convolution, and the two details that break labels
Use FFT-based convolution. Direct convolution of a 10-second file with a 1-second RIR is 2.5 billion multiply-adds per channel at 16 kHz; the FFT version costs a few thousandths of that. In scipy it is one call: fftconvolve.
Two details. First, the output is longer than the input by the length of the RIR minus one, so trim it back if you want frame-level labels to stay aligned. Second, the RIR propagation delay shifts the audio in time: 3 ms is invisible to a human and enough to move the first phoneme boundary of every segment you cut. Trim the leading delay from the RIR, or offset the labels.
Why the direct-to-reverberant ratio matters more than RT60 for ASR
Two rooms with RT60 = 0.8 s are not equally hard. A microphone 30 cm from the speaker's mouth gives a direct-to-reverberant ratio of about +10 dB; the same room with the microphone across a conference table gives -5 dB. The recognizer sees the second as a smeared signal where each phoneme overlaps the next two.
DRR is roughly 10 log10 of the direct-arrival energy divided by the rest, and it is dominated by the source-microphone distance: halving the distance gains about 6 dB. When you specify reverberant data, state both numbers. A dataset described only as "reverberant" could be either of these two cases, and they need different models.
Code
Synthesize a decaying impulse response, estimate its RT60 by the Schroeder backward integral, and convolve it with a synthetic speech signal.
import numpy as np
from scipy.signal import fftconvolve
SR = 16000
def synthetic_rir(rt60=0.6, direct_ms=4.0, dur=1.2, tail_gain=0.5, seed=0):
"""Exponentially decaying noise plus a direct arrival and early reflections."""
r = np.random.default_rng(seed)
t = np.arange(int(SR * dur)) / SR
ir = r.normal(size=t.size) * 10 ** (-3.0 * t / rt60) * tail_gain
ir[:int(SR * 0.005)] = 0.0 # nothing before the direct sound
d = int(SR * direct_ms / 1000.0)
ir[d] += 1.0 # direct path
for delay_ms, gain in ((9, 0.5), (14, 0.35), (23, 0.25), (31, 0.18)):
ir[d + int(SR * delay_ms / 1000.0)] += gain
return (ir / np.max(np.abs(ir))).astype(np.float32)
def rt60_schroeder(ir, sr=SR):
"""Backward-integrated energy decay, slope fitted between -5 and -35 dB."""
decay = np.cumsum(ir[::-1] ** 2)[::-1]
db = 10.0 * np.log10(decay / decay[0] + 1e-12)
i5 = int(np.argmax(db <= -5.0))
i35 = int(np.argmax(db <= -35.0))
slope = (db[i35] - db[i5]) / ((i35 - i5) / sr) # dB per second
return -60.0 / slope
def speech_like(dur=2.0, seed=1):
r = np.random.default_rng(seed)
t = np.arange(int(SR * dur)) / SR
f0 = 130 + 10 * np.sin(2 * np.pi * 0.4 * 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 * 4.0 * t)) ** 2
return (x / np.max(np.abs(x))).astype(np.float32)
for target in (0.3, 0.6, 1.2):
ir = synthetic_rir(rt60=target)
print('target RT60 %.2f s -> estimated %.2f s (IR length %.2f s)'
% (target, rt60_schroeder(ir), len(ir) / SR))
ir = synthetic_rir(rt60=0.6)
x = speech_like()
y = fftconvolve(x, ir)[:len(x)]
early = float((ir[:int(SR * 0.04)] ** 2).sum() / (ir ** 2).sum())
print('clean RMS %.4f reverberant RMS %.4f' % (
float(np.sqrt((x ** 2).mean())), float(np.sqrt((y ** 2).mean()))))
print('first 40 ms hold %.1f%% of the impulse response energy (DRR proxy)'
% (100.0 * early))
print('peak of the RIR at %.2f ms, which is the shift applied to every label'
% (1000.0 * float(np.argmax(ir)) / SR)) - pip install numpy scipy. No audio files and no librosa needed.
- Expected output: the estimated RT60 should track the target within about 0.05 s at all three values. If it comes out at half or double, the decay exponent is being applied to amplitude and interpreted as power, or the other way around.
- The DRR proxy prints as a percentage of energy rather than in dB. A tail_gain of 0.5 gives a small early share, which is a fairly reverberant case; raise tail_gain to 0.2 and the same RT60 becomes noticeably easier.
- The last line is the practical warning from the steps: the RIR peaks a few milliseconds in, and that is exactly how far your labels will be off if you convolve without trimming the propagation delay.
Where this goes wrong
Convolving without trimming the propagation delay
The direct path arrives after the source-to-microphone travel time, which is about 3 ms per meter. Convolution shifts the whole signal by that amount, so every segment boundary and every forced-alignment timestamp is off by 3 to 15 ms. It does not sound wrong and it does not look wrong in a waveform plot, but it corrupts frame-level labels and it moves the first phoneme of each segment out of its window. Trim the leading silence from the RIR, or add the offset to the labels.
Mixing RIRs from a different sample rate than the audio
A 48 kHz RIR convolved with 16 kHz audio produces a reverb whose decay is three times too short in real time, and nothing in the output tells you. Resample the RIR to the audio rate, or resample the audio up, but never convolve across rates. This one is invisible in a listening test and obvious in the RT60 estimate.
Treating RT60 as the only descriptor
Two RIRs with the same RT60 can differ by 20 dB in direct-to-reverberant ratio, which is the difference between an easy ASR condition and a hard one. They can also differ in early-reflection structure, which is what a human hears as room size. A dataset labeled only "RT60 = 0.5 s" does not tell you what the model will have to handle; ask for the source-microphone distance or the DRR as well.
Using one RIR per room and calling the corpus diverse
A room produces a different impulse response for every source position, every microphone position, and every orientation, and the variation within one room is often larger than the variation between two rooms. A corpus built from one measurement per room has the room count as its diversity and the position count as one, which is why models trained on it overfit to the specific geometries in the file. Either measure several positions per room or simulate the geometry.
Convolving the noise along with the speech
The order matters. Convolve the clean speech with the RIR and add the noise afterward, or the noise is also reverberated and the SNR you computed before convolution no longer describes the file. Real environments reverberate the noise too, so this is a modeling choice rather than a universal rule — but it is a choice, and it has to be the same one at training and at evaluation, or the SNR labels are meaningless.
When to buy the data instead of building the pipeline
Buy when you need spatial diversity that measurement cannot cover in the time you have: hundreds of rooms with several source and microphone positions each, at consistent sample rates, with the geometry recorded. Buy when you need array RIRs, where the response must be consistent across channels and the propagation delays must be right to within a sample, because a synthetic tail has no arrival direction and cannot train a beamformer. Buy when the room types in your deployment are ones you cannot access — a car cabin, a call center floor, a specific open-plan office. Simulating is the right call for testing a dereverberation algorithm or for a first robustness experiment, and it costs nothing; it stops being the right call when the geometry itself is what the model has to learn.
More technical reference
-
Dereverberation
dereverberation
-
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
Need data for Room Impulse Response?
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.