{"id":152,"date":"2012-12-15T17:08:26","date_gmt":"2012-12-16T01:08:26","guid":{"rendered":"http:\/\/www.earlevel.com\/main\/?p=152"},"modified":"2013-06-01T16:01:28","modified_gmt":"2013-06-02T00:01:28","slug":"a-one-pole-filter","status":"publish","type":"post","link":"https:\/\/www.earlevel.com\/main\/2012\/12\/15\/a-one-pole-filter\/","title":{"rendered":"A one-pole filter"},"content":{"rendered":"<p>Here&#8217;s a very simple workhorse of DSP applications\u2014the 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):<\/p>\n<p><a href=\"\/main\/wp-content\/uploads\/2012\/12\/DF1-1pole.gif\"><img loading=\"lazy\" decoding=\"async\" src=\"\/main\/wp-content\/uploads\/2012\/12\/DF1-1pole.gif\" alt=\"\" title=\"DF1-&gt;1pole\" width=\"241\" height=\"105\" class=\"alignnone size-full wp-image-153\" \/><\/a><\/p>\n<p>We keep the input gain control, <em>a0<\/em>, in order to compensate for the gain of the feedback path due to <em>b1<\/em>, which sets the corner frequency of the filter. Specifically, <em>b1<\/em> = <em>-e<\/em><sup>-2&pi;Fc<\/sup> and <em>a0<\/em> = 1 &#8211; |<em>b1<\/em>|. (In the implementation, we&#8217;ll roll the minus sign in the summation into <em>b1<\/em> and make it positive, for convenience.)<\/p>\n<h3>Filter types<\/h3>\n<p>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.<\/p>\n<p>However, a one-pole makes a poor highpass filter for cases in which we might be likely to use it\u2014in particular, a DC blocker. That&#8217;s because it makes a highpass by pushing response up at high frequencies\u2014we really need a zero to pull the response down at very low frequencies. So, we&#8217;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.<\/p>\n<h3>Typical uses<\/h3>\n<p>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\u2014some gentle smoothing of a calculated signal in an automatic gain control, for instance.<\/p>\n<p>The 6 dB per octave slope of the one-pole filter\u2014a halving of amplitude for every doubling of frequency\u2014is gentle and natural. It&#8217;s extremely cheap, computationally, so it&#8217;s the perfect choice when you need just a bit of smoothing. And it doesn&#8217;t &#8220;ring&#8221; (overshoot), so it&#8217;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.<\/p>\n<p>Note that if you feed the one-pole lowpass an impulse, it will yield a perfect exponential decay\u2014the 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.<\/p>\n<h3>The source code<\/h3>\n<p>The one-pole filter is so simple that we&#8217;ll make it inline, entirely in the header file:<\/p>\n<pre>\r\n\/\/\r\n\/\/  OnePole.h\r\n\/\/\r\n\r\n#ifndef OnePole_h\r\n#define OnePole_h\r\n\r\n#include &lt;math.h&gt;\r\n\r\nclass OnePole {\r\npublic:\r\n    OnePole() {a0 = 1.0; b1 = 0.0; z1 = 0.0;};\r\n    OnePole(double Fc) {z1 = 0.0; setFc(Fc);};\r\n    ~OnePole();\r\n    void setFc(double Fc);\r\n    float process(float in);\r\n    \r\nprotected:    \r\n    double a0, b1, z1;\r\n};\r\n\r\ninline void OnePole::setFc(double Fc) {\r\n    b1 = exp(-2.0 * M_PI * Fc);\r\n    a0 = 1.0 - b1;\r\n}\r\n\r\ninline float OnePole::process(float in) {\r\n    return z1 = in * a0 + z1 * b1;\r\n}\r\n\r\n#endif\r\n<\/pre>\n<h3>Examples<\/h3>\n<p>Here&#8217;s a DC blocker:<\/p>\n<pre>\r\nOnePole *dcBlockerLp = new OnePole(10.0 \/ sampleRate);\r\n...\r\n\/\/ for each sample:\r\nsample -= dcBlockerLp->process(sample);\r\n<\/pre>\n<h3>Need highpass?<\/h3>\n<p>It&#8217;s trivial to add one-pole highpass response, if you&#8217;d like\u2014just negate the lowpass calculation; this inverts the spectrum, so you&#8217;ll need to flip the frequency range by subtracting Fc from 0.5:<\/p>\n<pre>\r\nb1 = -exp(-2.0 * M_PI * (0.5 - Fc));\r\na0 = 1.0 + b1;\r\n<\/pre>\n<h3>Pole position<\/h3>\n<p>You can experiment with how the location of the pole affects frequency response by using the <a href=\"\/main\/2003\/02\/27\/pole-zero-placement\/\">pole-zero placement app<\/a>. 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&#8217;ll see highpass response when the pole is to the left of the origin, and lowpass to the right.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a very simple workhorse of DSP applications\u2014the 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 &hellip; <a href=\"https:\/\/www.earlevel.com\/main\/2012\/12\/15\/a-one-pole-filter\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,4,9,26],"tags":[],"_links":{"self":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/152"}],"collection":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/comments?post=152"}],"version-history":[{"count":1,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/152\/revisions"}],"predecessor-version":[{"id":156,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/posts\/152\/revisions\/156"}],"wp:attachment":[{"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/media?parent=152"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/categories?post=152"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.earlevel.com\/main\/wp-json\/wp\/v2\/tags?post=152"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}