Generating Impulse Responses for Foobar
Apr 25, 2006 at 11:09 PM Thread Starter Post #1 of 3

Born2bwire

25+ Member ;-)
Joined
Nov 11, 2001
Posts
3,700
Likes
14
I thought it would be interesting if I put this in a new thread. I have been making a few impulse responses in Matlab to play around with in the convolver feature for Foobar. Matlab presents an ideal environment for people to make their filters because it allows you to build filters and easily generate the impulse responses. Even more importantly, you can go beyond simple equalizers.

Just a quick run through, an impulse response is the time domain output from a given filter that is fed an impulse signal. That is, if we send into our digital filter the input, h[0] = 1, then the output is our impulse response. This uniquely defines the filter and by convolving any digital stream with the impulse response we get the same output as if we ran it through the filter. It should be noted that convolution is not the always the best way to create effects. For example, an echo is marvelously inefficient using convolution. If we want to create an impulse signal in Matlab,

h=zeros(1024,1);
h(1)=.99;
wavwrite(h,44100,16,'Impulse');

This created an impulse signal of length 1024 samples. Technically, you only need one sample but this is here incase someone wants to pass the impulse through a filter of their own and get the impulse response for their own convolution filter. And if you run the impulse through the convolver, surprise, you get your original input back. Also, make sure you uncheck "Auto level adjust" to prevent clipping and to get the true output from your filter.

Let's say we want to create an echo that is 3dB down and has a .5 second delay. In Matlab we would simply do,

h = zeros(22050,1);
h(1)=.99; h(22051)=.5;
wavwrite(h,44100,16,'Echo');

We now have a superposition of the original sample and a sample that has been delayed 22050 samples, or .5 seconds. The wavwrite takes in amplitudes from [1 -1] so the amplitude of .5 for the delayed sample means that the sample is half-strengthed, or -3dB. Now if we want to create a continuing echo, for example, let’s have four echos that attenuate by -3dB every .25 seconds.

h=zeros(55125,1);
h(1)=.99; h(11026)=.5; h(22051)=.25; h(33076)=.125; h(44101)=.0625; h(55126)=.03125;
wavwrite(h,44100,16,'Echoing');

This is very inefficient since we have a convolusion of 55,125 samples for each output sample. The better way to do this would be to store registers in memory of previous samples and then just do a multiply and add to get the final output.

We can also easily create lowpass, highpass, or bandpass filters. For example, to create a 10th order Chebyshev lowpass filter with cutoff frequency of 1KHz and ripple magnitude of .5 dB,

[b,a] = cheby1(10,.5,1000/22050);
[h,t] = impz(b,a,1024,44100);
wavwrite(h,44100,16,'ChebyFilterLP');

The impz command will take in the polynomial coefficients of the numerator b and denominator a of the transfer function (H(z)) and create an impulse response of 1024 samples at a sampling rate of 44100 (in this case). So we can use the impz command to create any FIR filter that we desire.

Now let’s do something really interesting. Let’s make our own upsampler. To upsample by an integer amount, like 22.05KHz to 44.1KHz (X2), all you do is pad the file by zeros. So to upsample by two, we just add a zero inbetween each of our original samples. Now we need to run the data through an interpolation filter. If we actually look at our frequency domain after zero-padding, we actually have copies of the original sound in the upper frequency. For example, our original source occupied frequencies from DC to 11.025KHz. After zero-padding, we have a copy of the original sound that has been modulated up to occupy the 11.025KHz-22.05KHz region. So we use a filter to remove this and we can do this in our convolver. So let’s take a three second wave file, Clip22KHz.wav, upsample it to 44.1KHz, and save the unfiltered zero-padded signal. The clip is originally from a CD, which is bandlimited from 20Hz-20KHz, they leave 2.05KHz of unused bandwidth so that we can have a relaxed filter. Let us assumed that we retain an empty bandwidth of 1.025KHz after we downsampled (when the file is downsampled, the original empty bandwidth is removed, but if you were to upsample from 44.1Khz then you would be able to take advantage of it). The downsampled 22.05KHz sampled signal has an empty bandwidth of 1.025 KHz. Thus, we need to pass everything from 20Hz-10KHz and filter everything out above 12.05KHz. We can then make a filter whose transition band is between 10KHz-12KHz. So through a quick bit of trial and error, a type 2 Chebyshev filter with cut-off frequency of 12KHz, -90dB ripple, and 15 taps will allow us to have |H(z)|=1 up to 10KHz and will reach around -90dB by 12KHz.

Sample = wavread('Clip22KHz.wav');
Fs = 22050;
bits = 16;
Upsample = zeros(2*66152,2);
for n=1:66152
Upsample(2*n,: ) = Sample(n,: );
end
wavwrite(Upsample,2*Fs,16,'ClipUpsample');
[b,a] = cheby2(15,90,12000/22050);
[h,t] = impz(b,a,1024,44100);
wavwrite(2*h,44100,16,'UpsamplerLP');

To view the filter response, do,
freqz(b,a,44100,44100);

When you play back the unfiltered upsampled file, you should notice that the sound has been attenuated (by -3dB, or ½) and that it sounds distorted. If you watch the visualization in Foobar, you can see the modulated sound in the upper half of the frequency band. Now if you apply the filter, the upper frequency band is now gone and the file should play back without distortion and at the correct volume.

One other project that I have not tried out would be to create a noise reduction filter. My idea for this would be to record a second or two of tape hiss from the blank portion of a cassette tape or record. Then superimpose that on a good soundfile. Using the original soundfile as our desired output, we could use the noisy soundfile to train an adaptive filter. The converged adaptive filter could then be used to create an impulse response that can be used as a noise reducer for LP or cassette transfers. Don't know how successful this would be but I would imagine that we could effectively model hiss and rumble as white noise and thus be able to effectively filter it out.

We could also implement frequency shifting. The amount that a frequency is shifted is approximately the derivative of the phase response of the filter. So ideally, we create filters that have a linear phase shift (and thus the frequency shift is slight and constant across the band). So if you created a filter with |H(z)|=1 and a non-linear phase response, then you could create frequency shifting. I think I may have a filter response somewhere that does this. It was meant to take in a sharp impulse and return a chirp from smearing the frequencies.

You can get all of the sound samples that I used and created here:

https://netfiles.uiuc.edu/patkins/www/Impulse.wav
https://netfiles.uiuc.edu/patkins/www/Echo.wav
https://netfiles.uiuc.edu/patkins/www/Echoing.wav
https://netfiles.uiuc.edu/patkins/www/ChebyFilterLP.wav
https://netfiles.uiuc.edu/patkins/www/Clip22KHz.wav
https://netfiles.uiuc.edu/patkins/www/ClipUpsample.wav
https://netfiles.uiuc.edu/patkins/www/UpsamplerLP.wav
 
May 6, 2006 at 5:31 PM Post #3 of 3
Quote:

Originally Posted by wakked1
That's great information, than you very much for posting this! I'm guessing for a simple EQ I could just truncate the WAV to 1 sample and apply the EQ?


Not exactly. If you wanted to reproduce the original .wav using convolution, you would only need a 1 sample long impulse response. This is the equivalent of applying convolution with a delta function (in other words, integrating with a delta function) and hence you reproduce the original input. This is not very useful, which is why I have a 1024 sample impulse response. The first sample is 1, while the rest are just zeros. If you want to create an EQ, what you would do is first set your equalization to the levels that you want. Then input my impulse file and record the output. This works because the impulse response is the output of your system when fed a delta function input. So if you record the output of your EQ when fed my impulse response, you will have a 1024 tap reproduction of your EQ's impulse response. Unless your EQ causes time shifting, the output will only be as long as your input. So a single sample impulse response will not retain any information at the output since you will only get one sample at the output. But if you have 1024 samples, you should be able to get a decent result to use for convolution.

One other thing I should note, all of my filters and examples here create mono impulse responses. So the filter acts on both channels of a stereo input. But you can create a stereo impulse response and thus have different filters for your left or right channels. This is done by first noting that the track information of your wav is stored in separate columns (I would guess that the first column is left track and the second is right). Thus, make your impulse response have two columns as well. So let's have our lowpass filter affect our right channel and have an echo on our left channel.

h = zeros(22051,2);
h(1,1)=.99; h(22051,1)=.5;
[b,a] = cheby1(10,.5,1000/22050);
[h:),2),t] = impz(b,a,22051,44100);
wavwrite(h,44100,16,'StereoEchoLP');

I'm running a simulation on my computer right now so it will be a little bit before I can run this and check to see if I have the right association for the channels.

EDIT: Yep, works fine.

https://netfiles.uiuc.edu/patkins/www/StereoEchoLP.wav
 

Users who are viewing this thread

Back
Top