From textbooks and classroom I have learned that convolution in time
domain is equivalent to
multiplication in frequency domain and vice versa. When using long
impulse
responses (filter kernels), multiplication in frequency domain can be
the most efficient of the two methods. There is however this effect of
convolution, that the output length equals the input signal length plus
the filter kernel length, minus one sample. Therefore, a faithful
translation of convolution to the frequency domain is not so
straightforward. A plain multiplication of the signal's and filter's
spectra will result in circular convolution. As a matrix operation,
circular convolution can be sketched like this:
![]() |
Compare this to linear convolution, like it is sketched below. If
the linear convolution's tail is cut off and fold over the start, you
get the circular convolution's pattern.
![]() |
The only case where this would cause no problem, is with the filter
being an identity matrix. It may also be a time-shifted identity. To
verify this, I multiplied a signal's spectrum (of two cosines summed)
with the spectrum of a time-shifted Kronecker delta. The output after
inverse FFT shows the signal time-shifted indeed.
![]() |
![]() |
This is my first fast convolution: a wasteful method to delay a
signal... But it is nice to have a pure example of delay implemented in
frequency domain. Checking the output vertical scale, the amplitude
appears reduced by a factor N, where N is the FFT framesize. The
'Kronecker filter' has it's amplitude spread over N frequency bins,
since it was
normalised with the same factor as the signal: 1/N in the forward
transform. Though it is not a realistic case of fast convolution, it
shows normalisation as a topic of consideration once more.
![]() |
Now I want to fast-convolve two signal inputs of equal length, to
check if it is true that the output signal will have the summed length
of signal 1 and signal 2 (minus one sample). For simplicity, I choose
two equal rectangular signals, each with half the FFT framesize.
Because I have my FFT frame virtually centered round time zero, I need
to center these input signals as well. The picture on the right below
shows the spectrum of the a rectangular waveshape.
![]() |
![]() |
The spectra of two such rectangles are multiplied, with complex
multiplication of course. That is actually a matter of computing the
complex square of the spectrum. Then I feed the result in an inverse
FFT. The output signal stretches over the frame indeed, and it has the
shape that one could expect from two rectangles convolved. Still I find
it kind of miraculous.
![]() |
Next I did fast convolution of a similar rectangular signal with a
chirp signal. Again, the resultant signal length is the sum of the
input lengths.
![]() |
![]() |
The signal/filter inputs can have any length ratio, as long as their
sum length does not exceed the FFT framesize plus one sample. Below is
an example with a rectangular signal of 3/4 framesize and a chirp of
1/4 framesize.
![]() |
![]() |
The input/output length difference in convolution complicates
matters even more when a long signal must be processed piecewise using
fast convolution. If the input signal is cut in arrays of equal length,
the bigger output arrays should overlap, and be summed to form the
final output. Here is an impression of that:
![]() |
I have also tried to sketch the parallel with a time domain matrix.
It is a case of framesize N=8. Four samples of the input are processed
per N=8 frame, and the filter kernel has 5 samples. This yields an
output y[n] of 8 samples everytime. When working in frequency domain,
all vectors shall be transformed with a N=8 (I)FFT: input, filter
kernel, output.
![]() |
Below you can see how the output vectors of subsequent frames
overlap. The first four samples in this example overlap with the
preceding output, and the last
four with the next output. Overlapping output means that values are to
be summed. For this reason, the method is generally called
'overlap-add'. Notice that the overlap region is the length of the
filter kernel minus one sample. So it need not be half the output
vector length, but in my example it happens to be the case.
![]() |
Instead of having the output arrays overlap, it is also possible to take overlapping segments of the input stream. You must then discard a portion of the output arrays, to get the correct result. Below I indicated the area of the matrix being skipped with this method. The number of output samples discarded equals the number of samples in the filter kernel minus one.
![]() |
Below, you can see what is actually processed in subsequent frames.
The output portions are not summed, just cut clean and glued
together. Which output samples shall be scrapped, is dependent on the
position of the signal and the filter kernel within the FFT frame. In
my case where everything is centered, it is the middle part that must
be kept. With non-centered FFT it is different: normally the first part
is scrapped, but by shifting the filter kernel the output position can
be manipulated.
![]() |
This method is sometimes and with good reason called
'overlap-discard'
or 'overlap-scrap'. More often it is called 'overlap-save', because you
need to save part of the input vector for the next frame. Using this
method, the final output vector will be smaller than the total input
vector. Theoretically, that is the wrong way round. The leading and
trailing parts of the convolution are missing. If needed, the input
vector can be extended on beforehand, at both ends, to make room for
these transients.
My interest in fast convolution is specific: I want to try chirp
z-transform, a technique which employs fast convolution to escape from
the strict resolution grid of Fourier Transform. In that case, I might
do some zero-phase filtering in frequency domain and deconvolve before
IFFT. There would not be an overlap issue, but the rule of signal
length + filter length + 1 not exceeding the framesize would still hold.