Contents

Exercises in MATLAB for convolution and Fourier transform

% These are a set of demonstrations to get you comfortable with real-world applications of the
mathematical tools we discussed in class. Although you can copy and paste the MATLAB code directly, 
you might want to type it manually.

% We will apply convolution and fourier transforms to two sets of data. The first is a sound 
file, on which we will perform frequency space manipulations.

% The builtin MATLAB function, wavread(), allows us to read in sound files in the .wav format.

% Copy the file, ?Something.wav? onto your computer and use the ?cd? command to move to the 
directory that has the file. The following command creates a new variable, mySound, whose 
contents are the sound file. The length is 1 Mb.

% We will be manipulating this file in frequency space. The builtin function, fft(), takes 
the fast fourier transform of the input. There are a few things to note right away: first, 
the fft outputs are complex but we can only play real sounds. Second, the plot command may 
not do what you might expect if you graph complex numbers. Specifically, it plots the real part 
on the x axis and the imaginary parts on the y axis. To manage this, we will use the MATLAB real() 
and abs() functions to look at the real part or the magnitudes, sqrt(x2+y2), of the complex numbers. The 
imag() function returns the imaginary part, if needed. Finally, the order in which the data are 
returned from the fft are a little strange. The first half of the output contains the positive 
frequencies from zero to the maximum frequency in the data. The second half of the data are the 
negative frequencies (as though we were going backwards in time) arranged from the most negative 
to the least. I have suppressed all of this in the exercises.

mySound = wavread('Something.wav');

% Create a new variable, FmyS, containing the fft of the sound file:
FmyS = fft(mySound);

% Create a set of filters. The idea is that we will multiply the fft (or 'spectrum') of the 
sound files by a weighting function, or filter. The first filter simply decreases the amplitude 
of the high frequencies linearly:
filt1 = abs(-524288:524287)'./524688;

% Another variant decreases the amplitude more rapidly, proportional to s3 (s is the frequency).
filt1sq = filt1.^3;
plot(filt1,'linewidth',2);
hold all
plot(filt1sq ,'linewidth',2);

% The ?holdall? command makes sure that the prior graph is still showing when you add a second. 
Make note of the colors of each curve for later. This is how to get MATLAB to form a log plot 
along the Y axis:
set(gca, 'YScale', 'log');

Our second filter weights the signal proportional to 1/s:

s = 1:524288;
filt2 = s .^ -1; % This raises each value of s to the -1 power

% Note that because of the curious order of the fft output, we must make a second half for 
the negative frequencies. The command, fliplr(), flips right and left in a row vector upside. 
The single quote at the end of the command line converts a row vector to a column vector and 
vice versa.
filt2 = [filt2, fliplr(filt2)]';
plot(filt2 ,'linewidth',2)

Our final filter is a digital version of what you will be making out of small electronic parts. It has the characteristic that the amplitude is proportional to 1/(1+tau/s):

s = 1:524288;
tau = .0001;
filt3 = 1./(1+s*tau);
filt3 = [filt3, fliplr(filt3)]'; % fliplr is like flipud for row vectors
plot(filt3 ,'linewidth',2);

The filters go from 0 to max positive, then from max negative to zero (because of the output order in the fft). It is just as well to look at only
the first half of the filter. Having done so, we can look at these filters with a logarithmic x axis, which is a little easier to see. The third command
just limits the range on the Y axis:

set(gca,'XLim',[1,524288]);
set(gca, 'XScale', 'log');
set(gca,'YLim',[1e-6 1]);

% Here we apply each of our digital filters by multiplying the Fourier transform of the sound 
by the filter weights:
filt1FS   = filt1 .* FmyS;
filt1sqFS = filt1sq .* FmyS;

filt2FS = filt2 .* FmyS;

filt3FS = filt3 .* FmyS;

% To get back from the frequency domain to the time domain, we apply the inverse Fourier transform.
filt1S   = real(ifft(filt1FS));
filt1S   = filt1S / max(filt1S);

filt1sqS = real(ifft(filt1sqFS));
filt1sqS = filt1sqS / max(filt1sqS);

filt2S   = real(ifft(filt2FS));
filt2S   = filt2S / max(filt2S);

filt3S   = real(ifft(filt3FS));
filt3S   = filt3S / max(filt3S);

Compare the results. Can you hear the difference?:

disp('Playing unfiltered sound')
sound(mySound,  44100, 16);
pause(1);

disp('Playing linear filter, filt1')
sound(filt1S,   44100, 16);
pause(1);

disp('Playing squared filter, filt1_sq')
sound(filt1sqS, 44100, 16);
pause(1);

disp('Playing 1/s filter, filt2')
sound(filt2S,   44100, 16);
pause(1);

disp('Playing standard lowpass, filt3')
sound(filt3S,   44100, 16);

% When we started in class we looked at a moving average filter. What does this do to the frequency 
characteristics of our signal? The answer may be surprising (unless you followed the in class lecture?)

% An easy way to see this is to use random noise ? containing equal amplitude at all frequencies ? as 
an input, then observing the filtered output. We generate noise with the randn() function, then use 
the conv() (convolution) function to apply a filter kernel, consisting of the weighted average of 7 
consecutive points. Note that:
% 1.	Arguments to the conv() function can be in either order (convolution is commutative)
% 2.	The length of the output from the conv() function is the sum of the lengths of the kernel and input functions.
Playing unfiltered sound
Playing linear filter, filt1
Playing squared filter, filt1_sq
Playing 1/s filter, filt2
Playing standard lowpass, filt3

Filtering by moving average

clf; % create a new figure
N = randn(1, 32768);
kern1 = [1 1 1 1 1 1 1] / 7;
k1StarN = conv(kern1, N);
k1StarN = k1StarN(4:32768+4); % make the output the same length as the input

% The ones() function creates a matrix of ones.
% We could have created kern1 as: kern1 = ones(1,7);
kern2 = ones(1,128) / 128;

k2StarN = conv(kern2, N);
k2StarN = k2StarN(64:(32768+64));
sound(N/max(N),32768);
sound(k1StarN/max(k1StarN),32768);
sound(k2StarN/max(k2StarN),32768);

fN = fft(N);
fk1N = fft(k1StarN);
fk2N = fft(k2StarN);
clf
plot(abs(fN(1:16384))/128);
hold all
plot(-3+abs(fk1N(1:16384))/128);
plot(-6+abs(fk2N(1:16384))/128);

% The plot shows the frequency content after the convolution filter. Notice that some frequencies are suppressed 
to zero, whereas others are minimally affected. Longer filters have more prominent weirdness.

Here are some other exercises to think about in order to cement this knowledge: 1. Apply a moving average filter to the Fourier transform of pure noise,
and observe what happens to its inverse transform (to time domain). What do you expect?

kfN = conv(kern2,fN);
kN = ifft(kfN);
clf
plot(abs(kN(1:16384)));

% 2.	Apply a moving average filter to the frequency domain representations of the sound file and play it back.
kFmyS = conv(kern2,FmyS);
kmyS = real(ifft(kFmyS)); % Turn up the volume to hear this.
sound(kmyS, 44100, 16);

% 3.	Try convolving by sinc, sin(x)/x, functions.  Do you get what you expect?
% 4.	Run the music through a low pass filter
% 5.	Adjust the value of tau for filt3. What happens?