Here’s a very simple workhorse of DSP applications—the one-pole filter. By comparison, biquads implement two zeros and two poles. You can see that our one-pole simply discards the zeros (the feed-forward delay paths) and the second pole (feedback path):

We keep the input gain control, *a0*, in order to compensate for the gain of the feedback path due to *b1*, which sets the corner frequency of the filter. Specifically, *b1* = *-e*^{-2πFc} and *a0* = 1 – |*b1*|. (In the implementation, we’ll roll the minus sign in the summation into *b1* and make it positive, for convenience.)

### Filter types

A pole is a single-frequency point of pushing gain up, and a zero is a single-frequency point of pulling the gain down; with a single pole, you are not going to get complex response curves such as bandpass, peak, and shelving filters that you can get with the two poles and zeros of a biquad. That leave us with lowpass and highpass, which have a single point of change.

However, a one-pole makes a poor highpass filter for cases in which we might be likely to use it—in particular, a DC blocker. That’s because it makes a highpass by pushing response up at high frequencies—we really need a zero to pull the response down at very low frequencies. So, we’ll only implement coefficient calculation for lowpass response here. However, we can still make a highpass filter suitable for a DC blocker by subtracting the output of a one-pole lowpass filter, set to a low frequency, from the direct signal.

### Typical uses

What are typical uses of this one-pole lowpass filter? Sometimes, we do need the gentle slope of a first order frequency roll-off. And often, we use this simple filter on parameters instead of one the audio directly—some gentle smoothing of a calculated signal in an automatic gain control, for instance.

The 6 dB per octave slope of the one-pole filter—a halving of amplitude for every doubling of frequency—is gentle and natural. It’s extremely cheap, computationally, so it’s the perfect choice when you need just a bit of smoothing. And it doesn’t “ring” (overshoot), so it’s an excellent choice for filtering knob changes. Run each of your user interface sliders and knobs through a one-pole lowpass set to a low (sub-audio) frequency, and your glitchy, zippered control changes turn into smooth parameter changes.

Note that if you feed the one-pole lowpass an impulse, it will yield a perfect exponential decay—the same decay that we use on typical ADSR envelope generators on synthesizers. To look at it another way, the feedback path is an iterative solution to calculating an exponential curve.

### The source code

The one-pole filter is so simple that we’ll make it inline, entirely in the header file:

// // OnePole.h // #ifndef OnePole_h #define OnePole_h #include <math.h> class OnePole { public: OnePole() {a0 = 1.0; b1 = 0.0; z1 = 0.0;}; OnePole(double Fc) {z1 = 0.0; setFc(Fc);}; ~OnePole(); void setFc(double Fc); float process(float in); protected: double a0, b1, z1; }; inline void OnePole::setFc(double Fc) { b1 = exp(-2.0 * M_PI * Fc); a0 = 1.0 - b1; } inline float OnePole::process(float in) { return z1 = in * a0 + z1 * b1; } #endif

### Examples

Here’s a DC blocker:

OnePole *dcBlockerLp = new OnePole(10.0 / sampleRate); ... // for each sample: sample -= dcBlockerLp->process(sample);

### Need highpass?

It’s trivial to add one-pole highpass response, if you’d like—just negate the lowpass calculation; this inverts the spectrum, so you’ll need to flip the frequency range by subtracting Fc from 0.5:

b1 = -exp(-2.0 * M_PI * (0.5 - Fc)); a0 = 1.0 + b1;

### Pole position

You can experiment with how the location of the pole affects frequency response by using the pole-zero placement app. Click the Pair checkbox near the Poles sliders to disable it. Move both Poles sliders to center to move the poles to the origin. (The zeros are already at the origin by default when you open the page. At the origin, poles and zeros have no effect.) Now, move just one pole slider; you’ll see highpass response when the pole is to the left of the origin, and lowpass to the right.

Hi Nigel,

This is great for me as I am only beginning to understand filter design. Thank you very much for the great resource!

Thank you for your comment, Harold—glad this is a help. I added an additional paragraph about using the pole-zero applet to experiment. While you’re moving the slider and watching the graph, keep in mind that as the pole nears the unit circle, it pushes the frequency response higher and higher. The input gain parameter,

a0, compensates by pulling the overall amplitude down to keep the peak amplitude constant.Nigel

Hey Nigel,

Although I am still trying to learn the intricacies of digital filter designing and I have experimented with this idea myself, but I still wonder about a few things regarding the implementation.

In the blog above, we have a function OnePole::process(…) which processes just 1 sample at a time (opposed to a frame of samples).

Won’t the function calling for every sample make the processing slow ?

Why not rather pass address of an entire frame to OnePole::process(…), let it do the DSP on that piece of memory, wont this be faster ?

Please clarify.

Thanks

Shashank

Hi Shashank,

In the end, yes, you generate frames of output. However, you still start with “atomic” DSP objects. Consider that if you need a OnePole inside a reverb process. The reverb may generate its output a frame at a time, but it may require a OnePole in a feedback within the reverb (a common requirement). If you had designed OnePole to work on frames, it would be useless for the Reverb object. On the other had, it’s trivial to use OnePole to do an entire frame—either stick it in a for loop outright, or create a trivial OnePoleFrame object that uses OnePole (perhaps using template meta programming to effectively create unrolled loops for zero loop overhead).

Note that OnePole::process is inline and computationally cheap—there is no function call overhead at all—no extra cost for it to be atomic.

Nigel

PS—Need to sleep now, but I’ll check out your blog—looks interesting.

Nice article – this filter is indeed the “workhorse” of DSP!

If you don’t mind a nitpicking comment though: you’ve labelled your filter coefficients a and b as is usual, but unusually, the b coefficients are applied to the output signal and the a coefficients are applied to the input. Convention dictates it should be the other way around.

Obviously this isn’t a problem in the context of this blog post, but as it’s a beginner-type article this switch-around may be confusing for those DSP novices who will generally see the opposite with other DSP resources.

In any case, I look forward to continuing to follow your musings. Cheers!

Hi Alan,

Actually, there is no convention (or there are two). A little more than half of the DSP books on my bookshelf favor the orientation I use, where

ais in the numerator of the transfer function. I wish there were a convention—on top of the a/b confusion, some people roll the minus signs in the feedback into the coefficients, other don’t. However, since almost everyone normalizes, you need only look to see whethera0orb0is missing to determine the orientation. It’s not always so obvious whether the minus signs are included.Thanks for reading!

Nigel

Nice description. Just curious- why did you switch the traditional nomenclature for the coeff, A and B. I have always seen A in the feedback, (denominator) and B(numerator) in the forward.

Hi Jerry—there is no standard, no tradition. It was natural for me to have A coefficients on top of the transfer function. Some might start with A on the left of the direct form I block diagram, which yields the opposite relationship. I once did a survey of the DSP books on my bookshelf, and A-on-top was used more often (Zölzer uses that convention, for example).

I only wish more people used b for feed

Back, and a for feedforward. 🙂I’m looking at Zölzer DAFX 2nd Ed right now and he uses b in the numerator, a in the denominator in Eq 2.3. Most of my books use b in the numerator. But your point stands – it doesn’t matter as long as it’s clear and consistent across this article / website.

Yes, I noticed that too—I suspect to be consistent with DAFX publications. His Digital Audio Signal Processing is the other way.

You can eliminate a multiply by using z1 += (in – z1)*a0;

…I guess that’s * a0…

Good spot, and I like the elegance. However, it’s the same number of operations, and I think most processors these days have single-cycle multiply. It does reduce a fetch, which might not be zero cost in the pipeline in all cases. I recall considering using a single variable, but I think I went with two with the idea of also having a highpass version of setFrequency (which is of marginal utility), as noted in the article but not the code, and after finding no difference in execution time. Testing, the code execution time is consistently identical between the two versions with compiler optimizations enabled (Xeon); for some reason, the single-coefficient version is consistently slower by a trivial amount (2%) without optimizations—I can’t think of a reason for that, but it’s unimportant.

Very nice article. I am naïve at DSP and trying to learn. One question that comes to my mind is that in determining the coefficients based on cut-off frequency, there is no dependence on the sample rate. Won’t sample rate have impact on the digital filter performance?

I could have made this more clear, sorry: “Fc” is normalized frequency. That is, it’s frequency divided by sample rate. For instance, look at the example for a DC blocker—Fc is set to “10.0 / sampleRate”, or 10 Hz.

Interestingly, if this is used to try and dc block a signal from a fast waveform digitizer (500MS/s) with a baseline of the max channel values, 16k, there is a significant distortion at the beginning due to z being set as 0 to start. I was able to get reasonable behavior by starting z as the beginning value of the function for the first sample.

Hi, nice article thank you. Can you explain can i can get resonance?

I know that i must to use feedback. I had fails make.

You can’t get resonance with a one-pole filter. You need complex poles.

Hello, Nigel. Thank you so much for your post.

Would you be so kind to provide the exact reference for the formulae you’re using in your implementation?

I’ve been testing different approaches for the coefficient calculation of 1-pole low-pass filters and it’s not so easy to find a good compromise. Your implementation is so far the best I’ve found.

In the Audio Programming book (Boulanger and Lazzarini), they use trigonometric functions for the coefficient calculation. The resulting filter is quite consistent from about .004 Fc (normalised freq.) up, with an attenuation of ~-3dB at cutoff. Below that point, attenuation at cutoff starts decreasing and there is little or no resolution in the coefficients when we reach 4.5e-05 Fc (about 2Hz at 44.1kHz SR).

Cliff Sparks also uses trigonometric functions and his filters behave very similarly to those from the AP book. http://www.arpchord.com/pdf/coeffs_first_order_filters_0p1.pdf

Another implementation is the 1-pole LP filter in Pure Data. Miller Puckette uses the following approximation to calculate the coefficients: b1 = 1 – (Hz_cutoff * 2 * pi / SR). This filter behaves very similarly to yours in the bottom range, with a good resolution for the very low frequencies, although the coefficient would change sign after about .159157 so it is clipped to 0 from that point on. Besides, the ~-3dB attenuation expected starts dropping after about .0024 Fc (normalised freq.).

In your implementation, the filter has a rather good response from the very bottom up until ~.07 Fc, which seems to be quite a great result for such a computationally efficient calculation of the coefficients. In fact, this is the one I will use for my time-variant filter.

Thanks!

Dario Sanfilippo

Hi Dario,

At this point I’m going from memory rather than reference, but I can tell you where I first saw this filter, which answers your question better than looking for a reference that fits the bill, or deriving from scratch. In

Musical Applications of Microprocessors, Hal Chamberlin derives the filter. He notes that a one-pole filter is a leaky integrator, which is equivalent to an analog R-C lowpass filter. He also notes that it’s well-known that capacitor voltage discharge follows an inverse exponential curve:E = E, where E is the next voltage after period T, given the initial voltage E_{0}exp(-T/RC)_{0}, R and C are the resistor and capacitor values in ohms and farads. The cutoff frequency is defined byFc = 0.5πRC; substituting we getE = E. The time change,_{0}exp(-2πFcT)T, is the sample rate in the digital system, so converting to sampling frequency,Fs = 1/T, we getE = E. To adjust for a gain of 1 at DC (0 Hz), we set the input multiplier to 1 – that value._{0}exp(-2πFc/Fs)Thank you so much for your answer, Nigel.

I have to step back a little bit here. I have done the tests using the Pure Data real-time audio programming environment. Pure Data essentially works on two domains: one domain is that of signals, where calculations take place at samplerate; the other one is that of “messages”, where the calculations take place “on event”, i.e., clicking on a button or similar.

What runs underneath PD is C, and in order to reduce the computational load the signal cosine function is based on a table. The calculation is thus not as accurate as calling cos() in C, and that’s the reason why I was getting weird coefficients for the implementations using trigonometric functions.

I’ll check the coefficients back using more precise calculations and I’ll let you know what I found out.

Thanks a lot.

Dario

Hey Nigel,

early-ish in the piece above you wrote “However, we can still make a highpass filter suitable for a DC blocker by subtracting the output of a one-pole lowpass filter, set to a low frequency, from the direct signal.”

To me this implies that the phase must remain linear or, I expect, the result would be somewhat tragic – my expectation being that if the output of the one-pole lowpass filter is not in phase with the direct signal then it can hardly cancel well enough to use at all, let alone as a high pass filter – should I have tried this before asking? 😀

Good question! OK, we start with a one-pole lowpass filter. The passband will have unity gain, and zero radians phase until from 0 Hz, dropping as it starts into the rolloff, towards -π/2. Of course, you’ll get perfect cancellation at DC when you subtract it from the original signal. At frequencies higher than the cutoff, the phase won’t be convenient, but the lowpassed signal will be smaller and smaller (cutting in half in amplitude for each doubling of frequency). You can set the lowpass cutoff very low, so by the time you get to the audio range, there’s not enough lowpassed signal to matter.

Try the response grapher. In the right side (b coefficients), put “1,-0.99999”, or however many 9s you want—the closer to -1, the lower the cutoff. In the left side, but “0.00001”, or 1 added to whatever the other number was. View in linear mode so you can see 0 Hz. And remember, it’s not just “100 dB down”, or whatever, at a given point, it’s that far below the input signal.

But your sense that we don’t want to add and subtract filter outputs without regard to phase, normally, is correct (particularly with second order and up). But in this case, there is no perceptible penalty.

Hi Nigel,

I came across this article while endeavouring to learn more about building synths in Native Instrument’s Reaktor. It’s well-written, clear, and just at the right level for someone with moderate physics, maths and coding skills like me – thankyou! The next challenge for me is properly understanding the z-transform, haven’t got my head around that yet. Thanks again…

Pete

Hi,

I tried it and got some weird result, I think I got confused with the ‘minus’ sign mentioned at the beginning of the (great) article.

So, here is what I have done:

1.

Assume: Fc = 0.25 (normalized frequency, equivalant to ~11 KHz at 44100 sample rate)

2.

For 1st order LPF, use the mentioned:

b1 = exp(-2.0 * M_PI * Fc);

a0 = 1.0 – b1;

To receive:

b1 = 0.20787957635076193

a0 = 0.7921204236492381

3.

Copy paste these two params to the “filter frequency response grapher”:

http://www.earlevel.com/main/2016/12/08/filter-frequency-response-grapher/

And one receives a weird result.

4.

Still on the “filter frequency response grapher”, change b1 to -b1

One receives the correct graph

5.

To follow that , the LPF calculation might simply be:

b1 = -exp(-2.0 * M_PI * Fc); // add minus

a0 = 1.0 + b1; //change minus to plus

Is this correct or have I misunderstood the first explanation:

“…Specifically, b1 = -e-2πFc and a0 = 1 – |b1|. (In the implementation, we’ll roll the minus sign in the summation into b1 and make it positive, for convenience.)…”

Hi Malcolm. Yes, as I noted, I rolled the minus sign from the evaluation into the b1 coefficient. There are only plus signs in Process.