Introduction

We saw, in the last article, how to create simple waveforms. But this sounds really lacks modulation, like filter or ADSR envelops. Today, we'll see how to implement low-pass and high-pass filters.

Here is the result!

Time domain

When you are listening to music, your speakers moves accordingly to signal's amplitude. This is what we call the time domain: we describe how our signal's amplitude vary through the time.

Frequency domain

The difference between a noise and a note is periodicity of the signal. This is therefore natural to cut one period of our note to analyze it. As I explained in my previous article, if you already played with a synthesizer, you certainly know that the sinus is the base "brick" of signal. In this article we analyzed frequency decomposition of square, triangle and sawtooth waves. Expressing our signal in a sum of couple amplitude / frequency is expressing our signal in the frequency domain.

Fourier

The Fourier's Transform is the mathematical operation which analyzes a time domain representation and outputs it's frequency representation.

There is a lot of Fourier's Transforms: some can be used with a streamed signal, some outputs/inputs complex numbers, some use just cos() to forget the imaginary part.

In a general manner, the Fourier transform for a discrete signal is

\[X(f) = \sum_{n = 0}^{N-1}x(n)e^{-2i \pi f \frac{n}{N}}\]

Understand its output as an array of complex numbers. Re(z) is the cosine phase, Im(z), the sine phase, |z| the amplitude and Ang(z) the phase.

This article explains it better than I could, go read it :)

The Inverse Fourier's Transform goes from frequency to time representation.

Filters

A filter is described by its response frequency: how much it attenuates or amplifies a frequency going through it. Intuitively, we think that it should be simple to implement a filter as an array of coefficients for each frequency, build the frequency domain representation of our signal, multiply it with our filter, then doing the inverse Fourier's Transform and send it to our speakers.

Well, this is really inefficient. You'll have to use a good FFT implementation if you hope to do some real-time application.

If you understood well those concept, you might have found a better way to do: set you filter's coefficients and take it's time representation with IFT. Doing such a transformation allows us to get the impulse response of our filter: the way an impulse evolves temporally through the filter.

So far so good, we avoided a FT on our signal every frame, and we do only one IFT on our filter. In the frequency domain, we did a simple multiplication

\[y(w) = f(w)x(w)\]

(Where x is the input signal, f the filter and w the pulse) How to keep the same transformation in the time domain ? The Fourier's Transform give us another identity

\[FT^{-1}[F(w)X(w)] = f(n) * x(n)\\ FT[f(n)x(n)] = F(w) * X(w)\]

where * is the convolution operator defined as follow

\[x(n) * f(n) = \sum_{i = 0}^{N} x(n)f(n - i)\]

We could the take our filters coefficients, IFT to have the time representation, and use it as a convolution product on our signal. That's really better, but, as you can see, for a sample rate of 44100 Hz, the number of samples in our impulse response becomes high and convolution could be expensive.

FIR Filters

This process is the base of a Finite Impulse Response (FIR) Filter. This kind of filters, as the name suggests, can only act on a duration depending of the number of samples of the impulse response. It's the simple convolution seen before.

\[x(n) * f(n) = \sum_{i = 0}^{N} x(n)f(n - i)\]

A N-order Filter is a filter wich uses at most the sample x(n - N). The more you want a good quality FIR filter, the more the order grows. It can be computationally inefficient for a high quality filter. However, they are numerically stable, and easy to design (ie: it's simple to find impulse response coefficients). FIR filters are a pure digital product.

IIR Filters

Definition

We can simulate closely RLC circuits and introduce also some feedback, so that our filters use their own response.

\[y(n) = \sum_{i = 0}^{N} a_{i}x(n - i) - \sum_{i = 0}^{N} b_{i}x(n - i)\]

A N-order IIR filter is equivalent to an analog N-order filter, and we can imitate them. the a are called forward coefficients whereas b are feedback coefficients. For an IIR, in audio applications, an IIR filter of order 2 is good, and we can define one of IIR filter categories: Biquad filters.

\[y(n) = a_0 x(n) + a_1 x(n - 1) + a_2 x(n -2) - b_1 y(n - 1) - b_2 y(n - 2)\]

This representation is called Direct Form I, and uses two buffers, for forward samples and feedback samples. A most convenient representation is Direct Form II

\[w(n) = x(n) - b_1 w(n - 1) - b_2 w(n - 2)\\ y(n) = a_0 w(n) + a_1 w(n -1) + a_2 w(n - 2)\]

This form is better for floating point operations too. IIR filters are computationally cheap, but now we need a way to design them.

Design

To design IIR filters, there is two possibilities:

  1. You are a god, you do not fear maths, and you design your own. In this case, I can't help you since I belong to the 2nd category.
  2. You aren't, and you'll base your work on previous researches.

There is a lot of "plug and play" filters, Chebyshev's or Butterworth's for instance. Butterworth's filters maybe interesting to know, but you certainly won't use it in your synthesizer (it may be perfectly well-suited for an equalizer). It's main interest is that it's response frequency is as flat as possible in the passband (see article on Wikipedia for more).

Low pass filter

Coeff Lowpass Highpass
\lambda \[\frac{1}{\tan(\frac{\pi f_c}{44100})}\] \[tan(\frac{\pi * f}{44100})\]
\phi - -
a_0 \[\frac{1}{1 + 2 \lambda + \lambda^2}\] \[\frac{1}{1 + 2 \lambda + \lambda^2}\]
a_1 \[2 a_0\] \[2 a_0\]
a_2 \[a_0\] \[a_0\]
b_1 \[2 a_0 (1 - \lambda^2)\] \[2 a_0 (\lambda^2 - 1)\]
b_2 \[a_0 (1 - 2 \lambda + \lambda^2)\] \[a_0 (1 - 2 \lambda + \lambda^2)\]

On a synthesizer, you can see another parameter, Q factor (or resonance), a little amplification just before the frequency response. In order to design such filters, I found this great blog about audio programming

http://www.earlevel.com

You can then design a filter using their interactive designer which will give you the coefficients you need.

If you cannot hardcode your coefficients, here are formulas

There are a huge quantity of filter's categories, if you want to study them, musicdsp is a great website.

Conclusion

We wrote our filters, they sound good, and you already can start to code your own synth, just like I did.

Since I'm a beginner in audio programming, any comment about mistakes, improvements, or even resources about audio DSP in general would be really really really appreciated.

And you can see the implementation of my synthesizer on my Bitbucket profile