# Convolving Room Impulse Response with a Wav File (python)

I have written the following code which is supposed to put echo over an available sound file. Unfortunately the output is a very noisy result which I don't exactly understand. Can anybody help me with regard to this? Is there any skipped step?

#convolving a room impulse response function with a sound sample both of stereo type
from scipy.io import wavfile
if inp[0] != IR[0]:
print "Size mismatch"
sys.exit(-1)
else:
rate = inp[0]
print sound_file_name
out_0 = fftconvolve(inp[1][:, 1], IR[1][:, 1])
out_1 = fftconvolve(inp[1][:, 1], IR[1][:, 1])
in_counter += 1
out = np.vstack((out_0, out_1)).T
wavfile.write(sound_path + sound_file_name + '_echoed.wav', rate, out)

• "a very noisy result" Plot part of it and look at the graph and see what the problem is. Is it clipped? Wrapping around? Skipping samples? – endolith Sep 25 '14 at 13:56
• actually it was clipped to full amplitude and I believe the reason of conversion between float and integer which I was not doing. – Cupitor Sep 25 '14 at 17:48

1. The way you read the files is a little odd. The method used here is clearer with respect to the sampling rate and the signal.
2. You will note that the above example calls pcm2float after reading. This may be the cause of your problem. The wave reader returns an array of ints. Processing those (quite large) values may well cause problems. Try converting your wave data to floats before doing the convolution.
• Thank you. I actually forgot to update here although I am still not completely satisified with final result. – Cupitor Sep 25 '14 at 17:43

It is a little late, but i'm also working on convolution reverb at the moment. If it is still of interest, you can use my code. Simply call the function convolution_reverb and pass the paths to the two audio files (audio and impulse response, both need to be .wav files), as well as the name for the result file to be created.

import numpy as np
from wave import open
import soundfile

class Wave:
def __init__(self, data, frame_rate):
self.data = normalize(data)
self.frame_rate = frame_rate

def make_spectrum(self):
amplitudes = np.fft.rfft(self.data)
frequencies = np.fft.rfftfreq(len(self.data), 1 / self.frame_rate)

return Spectrum(amplitudes, frequencies, self.frame_rate)

zeros = np.zeros(n)
zeros[:len(self.data)] = self.data

self.data = zeros

def write(self, file):

frames = self.quantize().tostring()

def quantize(self):
if max(self.data) > 1 or min(self.data) < -1:
self.data = normalize(self.data)

return (self.data * 32767).astype(np.int16)

class Spectrum:
def __init__(self, amplitudes, frequencies, frame_rate):
self.amplitudes = np.asanyarray(amplitudes)
self.frequencies = np.asanyarray(frequencies)
self.frame_rate = frame_rate

def __mul__(self, other):
return Spectrum(self.amplitudes * other.amplitudes, self.frequencies, self.frame_rate)

def make_wave(self):
return Wave(np.fft.irfft(self.amplitudes), self.frame_rate)

def convert_wav(file):
soundfile.write(file, data, samprate, subtype='PCM_16')

_, sampwidth, framerate, nframes, _, _ = reader.getparams()

dtypes = {1: np.int8, 2: np.int16, 4: np.int32}

if sampwidth not in dtypes:
raise ValueError('unsupported sample width')

data = np.frombuffer(frames, dtype=dtypes[sampwidth])

if num_channels == 2:
data = data[::2]

return Wave(data, framerate)

def normalize(data):
high, low = abs(max(data)), abs(min(data))
return data / max(high, low)

def convolution_reverb(audio_file, ir_file, output_file):
convert_wav(audio_file)
convert_wav(ir_file)

if len(audio.data) > len(ir.data):

else:

ir_spectrum = ir.make_spectrum()
audio_spectrum = audio.make_spectrum()

convolution = audio_spectrum * ir_spectrum
wave = convolution.make_wave()
wave.write(output_file)

convolution_reverb('audio.wav', 'ir.wav', 'result.wav')

• Thanks pal! I have moved on a long time ago! :) Also welcome to dsp! – Cupitor May 31 at 6:52

I have finally made some manipulations that was mainly based on what @JRE suggested and I have independently found out too. I have posted this answer on SO earlier. Manipulations(like convolution) on signal need to be done after converting the wave file to float type and to be finally reconverted to original 16 bit Wav. There also need to be an additional step of adding the original signal with the convolved signal which I added to the below code.

from utility import pcm2float,float2pcm
input_sig=pcm2float(input_sig,'float32')
IR_sig=pcm2float(IR_sig,'float32')

if input_rate!=IR_rate:
print "Size mismatch"
sys.exit(-1)
else:
rate=input_rate
print sound_file_name
out_0=fftconvolve(input_sig[:,0],IR_sig[:,0])
out_0=out_0/np.max(np.abs(out_0))
out_1=fftconvolve(input_sig[:,1],IR_sig[:,1])
out_1=out_1/np.max(np.abs(out_1))
out=np.vstack((out_0,out_1)).T
out=float2pcm(out,'int16')
out[:input_sig[:,0].shape[0]]=out[:input_sig[:,0].shape[0]]+input_sig
wavfile.write(sound_path+'echoed outputs/'+sound_file_name+'_'+IR_file_name+'_echoed.wav',rate,out)


I am still not completely happy with the results. The results using openair website is far better than mine and I still don't know the reason for that: http://www.openairlib.net/auralizationdb/content/elveden-hall-suffolk-england

• You don't "need" to convert to float, but it's certainly easier that way. – endolith Sep 25 '14 at 18:01
• Without conversion I always had the problem of a huge noise over the output. Then I would be happy if you could explain what might be the source of that noisy output (basically not noise but overflown amplitude)... – Cupitor Sep 25 '14 at 18:31
• Well keeping it in integer form is like using a fixed-point DSP rather than a floating-point DSP. You can still do processing that way, by scaling things appropriately at each step, but there's no reason to do it on a computer. Modern computers are faster at floating point math than integer/fixed-point math, so that's the easier and faster way. Fixed-point is used on microcontrollers or specialized processors. – endolith Sep 25 '14 at 19:31
• For the downvoter, I would happily edit my code to make it more suited for the question that I asked(!!!!), if you specify your reason for downvoting – Cupitor Sep 25 '14 at 21:35