{"id":1,"date":"2015-09-24T16:41:23","date_gmt":"2015-09-24T16:41:23","guid":{"rendered":"https:\/\/synthnotes.ucsd.edu\/wp4\/?p=1"},"modified":"2015-10-27T20:48:01","modified_gmt":"2015-10-27T20:48:01","slug":"basic-filter-programming","status":"publish","type":"post","link":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/2015\/09\/24\/basic-filter-programming\/","title":{"rendered":"basic filter programming"},"content":{"rendered":"<h4>Short One Pole Low-pass<\/h4>\n<p>I often use this filter\u00a0for parameter smoothing.<\/p>\n<p>You can get an intuitive feeling for the filter coefficient\u00a0<strong>c<\/strong>\u00a0by observing that when it is near 1.0, the <strong>out<\/strong> slowly moves to the <strong>new<\/strong> value (low-pass). That is very little of the\u00a0<strong>new\u00a0<\/strong>input is passed, and mostly the previous state is preserved.\u00a0If <strong>c<\/strong> is near 0.0, the <strong>out<\/strong> follows the <strong>new<\/strong> value closely (pass-thru)<\/p>\n<pre>out = new * (1 - c) + out * c;<\/pre>\n<p><strong>c<\/strong> can be calculated for a given frequency by:<\/p>\n<pre>c = exp(-2 * PI * frequency\/samplingRate);<\/pre>\n<p>You can obtain a high-pass by subtracting the low-pass output from the input.<\/p>\n<pre>hpout = new - out;<\/pre>\n<h4>Coefficient calculation and sound processing<\/h4>\n<p>Most filters have one\u00a0section of code for setting the frequency, resonance and gain (the coefficients calculation) and another section for filtering the audio (using those coefficients and saving the internal state in one or more delay lines). It\u2019s typical for the coefficient calculation to be more CPU\u00a0intensive than the filtering of sound. This is because coefficient calculation involves trigonometric functions.<\/p>\n<h4>Low and high pass filters derived from an all-pass filter<\/h4>\n<p>All filters have both a defined amplitude and phase response over frequency. An allpass filter is a filter that has a flat amplitude response. The particular allpass filter we will be using is one which causes the phase to shift by \u03c0 or 180 degrees at the nyquist frequency, and has 0\u00a0degrees phase shift at 0 Hz. It uses a single coefficient\u00a0<strong>c,\u00a0<\/strong>which will vary the transition from 0 degrees to 180 degrees, either causing it to happen more quickly or more slowly. If it happens more quickly, lower frequencies will have a phase shift of near 180.<\/p>\n<pre>out = c * (in - out) + in1;\r\nin1 = in;<\/pre>\n<p>Note that the previous value of\u00a0<strong>in\u00a0<\/strong>is copied to\u00a0<strong>in1<\/strong> to be used in the next sample calculation, and\u00a0<strong>out<\/strong> is used in the next sample calculation as well. These two variables need to be preserved as state variables.<\/p>\n<p>The coefficient can be calculated\u00a0for a given frequency as follows:<\/p>\n<pre>omega = PI * frequency\/samplerate;\r\ntf = tan(omega);\r\nc = (tf -1.0)\/(tf + 1.0);<\/pre>\n<p>Now as the phase is shifted by 0 degrees at 0 Hz and 180 degrees at the nyquist frequency (and lower frequencies, dependent on\u00a0<strong>c<\/strong>), adding the\u00a0<strong>in<\/strong> signal to the allpass\u00a0<strong>out<\/strong> will cause high frequencies to cancel for a 0 amplitude, and low frequencies to reinforce for an amplitude of 2. With one additional line of code, we have a simple low-pass filter.<\/p>\n<pre>lpout = (in + out) * 0.5;<\/pre>\n<p>And the high-pass is just as simple.<\/p>\n<pre>hpout = (in - out) * 0.5;<\/pre>\n<h4>Notch\u00a0and band-pass filters derived from a second order all-pass filter<\/h4>\n<p>By putting two all-pass filters in series, we can double the phase shift to 2\u03c0 or 360 degrees. With this second order all-pass filter we can create a notch or bandpass filter by adding the input to the output of the all-pass. This is because the 180 degree phase shift will now appear between 0 Hz and Nyquist. Bandwidth of this filter is controlled by how quickly the phase goes from 0 to 360 phase shift.<\/p>\n<p>The all-pass is calculated as follows:<\/p>\n<pre>out = c * (out2 - in) + d * (1.0 - c) * (in1 - out1) + in2;\r\nout2 = out1;\r\nout1 = out;\r\nin2 = in1; \r\nin1 = in;<\/pre>\n<p>It shouldn&#8217;t be any surprise that we now have four state variables (<strong>out1<\/strong>, <strong>out2<\/strong>, <strong>in1<\/strong>, <strong>in2<\/strong>) &#8211; twice as many as in the first order filter. We now have two coefficients <strong>c<\/strong> and <strong>d<\/strong>. These are calculated so that we have independent control of frequency and bandwidth.<\/p>\n<pre>tf = tan(PI * bandwidth\/sampleRate);\r\nc = (tf -1.0)\/(tf + 1.0);\u00a0 \r\nd = -cos(2 * PI * frequency\/sampleRate);<\/pre>\n<p>And like the first-order low-pass and high-pass filters, we can create notch and band-pass filters by adding and subtracting the output of the all-pass to the input.<\/p>\n<pre>notch = (in + out) * 0.5;\r\nbandpass = (in + out) * 0.5;<\/pre>\n<h4>Externals for Pure Data<\/h4>\n<p>Just to show this code working, here are two filters programmed as Pure Data externals (<strong>fof~<\/strong> and sof~). Each can be switched between addition and subtraction for low-pass and high-pass or notch and band-pass.<\/p>\n<p>Source for the <strong>fof~<\/strong> external &#8211; <a href=\"https:\/\/synthnotes.ucsd.edu\/wp4\/wp-content\/uploads\/2015\/10\/fofpdsrc.zip\">download<\/a><\/p>\n<p>The sample processing (<strong>fof_perform<\/strong>)\u00a0function from the <strong>fof~<\/strong> external:<\/p>\n<pre>static t_int *fof_perform(t_int *w)\r\n{\r\n\u00a0 \u00a0 t_float tf;\r\n\u00a0 \u00a0 t_fof *x = (t_fof *)(w[1]);\r\n\u00a0 \u00a0 t_float *in = (t_float *)(w[2]);\r\n\u00a0 \u00a0 t_float *out = (t_float *)(w[3]);\r\n\u00a0 \u00a0 int n = (int)(w[4]);\r\n\r\n\u00a0 \u00a0 \/\/ calculate coefficient only if frequency changes\r\n\u00a0 \u00a0 if(x-&gt;frequency != x-&gt;frequencyold)\r\n\u00a0 \u00a0 {\r\n\u00a0\u00a0 \u00a0 \u00a0 if(x-&gt;frequency &gt; (x-&gt;samplerate * 0.48f))\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency = (x-&gt;samplerate * 0.48f);\r\n\u00a0 \u00a0 \u00a0 \u00a0 else if(x-&gt;frequency &lt; 0.0001f)\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency = 0.0001f;\r\n\u00a0 \u00a0 \u00a0 \u00a0 else\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency;\r\n\u00a0 \u00a0 \u00a0 \u00a0 tf = tanf(x-&gt;PI * x-&gt;frequency\/x-&gt;samplerate);\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;c = (tf -1.0f)\/(tf + 1.0f);\r\n\u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 while (n--)\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0     float input = *(in++);\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;out = x-&gt;c * (input - x-&gt;out) + x-&gt;in1;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;in1 = input;\r\n\r\n\u00a0 \u00a0 \u00a0 \u00a0 switch(x-&gt;type)\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 0:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = (input + x-&gt;out) * 0.5f; \/\/ lp\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 1:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = (input - x-&gt;out) * 0.5f; \/\/ hp\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 2:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = x-&gt;out; \/\/ ap\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 3:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 default:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = input; \/\/ bypass\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 return (w+5);\r\n}<\/pre>\n<p>Source for the <strong>sof~<\/strong> external &#8211; <a href=\"https:\/\/synthnotes.ucsd.edu\/wp4\/wp-content\/uploads\/2015\/10\/sofpdsrc.zip\">download<\/a><\/p>\n<p>The sample processing (<strong>sof_perform<\/strong>) and bandwidth setting (<strong>sof_bandwidth<\/strong>) function from the <strong>sof~<\/strong>\u00a0external:<\/p>\n<pre>void sof_bandwidth(t_sof *x, t_floatarg g)\r\n{\r\n\u00a0 \u00a0 t_float tf;\r\n\u00a0 \u00a0 x-&gt;bandwidth = g;\r\n\u00a0 \u00a0 tf = tanf(x-&gt;PI * x-&gt;bandwidth\/x-&gt;samplerate);\r\n\u00a0 \u00a0 x-&gt;c = (tf - 1.0f)\/(tf + 1.0f);\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<pre>static t_int *sof_perform(t_int *w)\r\n{\r\n\u00a0 \u00a0 t_float tf;\r\n\u00a0 \u00a0 t_sof *x = (t_sof *)(w[1]);\r\n\u00a0 \u00a0 t_float *in = (t_float *)(w[2]);\r\n\u00a0 \u00a0 t_float *out = (t_float *)(w[3]);\r\n\u00a0 \u00a0 int n = (int)(w[4]);\r\n\r\n\u00a0 \u00a0 \/\/ calculate coefficient only if frequency changes\r\n\u00a0 \u00a0 if(x-&gt;frequency != x-&gt;frequencyold)\r\n\u00a0 \u00a0 {\r\n\u00a0\u00a0 \u00a0 \u00a0 if(x-&gt;frequency &gt; (x-&gt;samplerate * 0.48f))\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency = (x-&gt;samplerate * 0.48f);\r\n\u00a0 \u00a0 \u00a0 \u00a0 else if(x-&gt;frequency &lt; 0.0001f)\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency = 0.0001f;\r\n\u00a0 \u00a0 \u00a0 \u00a0 else\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 x-&gt;frequencyold = x-&gt;frequency;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;d = -1.0f * cosf(2.0f * x-&gt;PI * x-&gt;frequency\/x-&gt;samplerate);\u00a0 \u00a0 }\r\n\u00a0 \u00a0 while (n--)\r\n\u00a0 \u00a0 {\r\n\u00a0 \u00a0    float input = *(in++);\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;out = x-&gt;c * (x-&gt;out2 - input)\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 + x-&gt;d * (1.0f - x-&gt;c) * (x-&gt;in1 - x-&gt;out1)\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 + x-&gt;in2;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;out2 = x-&gt;out1;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;out1 = x-&gt;out;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;in2 = x-&gt;in1;\r\n\u00a0 \u00a0 \u00a0 \u00a0 x-&gt;in1 = input;\r\n\u00a0 \u00a0 \u00a0 \u00a0 switch(x-&gt;type)\r\n\u00a0 \u00a0 \u00a0 \u00a0 {\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 0:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = (input + x-&gt;out) * 0.5f;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 1:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = (input - x-&gt;out) * 0.5f;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 2:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = x-&gt;out;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 case 3:\r\n            default:\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 *out++ = input;\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 break;\r\n\u00a0 \u00a0 \u00a0 \u00a0 }\r\n\u00a0 \u00a0 }\r\n\u00a0 \u00a0 return (w+5);\r\n}<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Short One Pole Low-pass I often use this filter\u00a0for parameter smoothing. You can get an intuitive feeling for the filter coefficient\u00a0c\u00a0by observing that when it is near 1.0, the out slowly moves to the new value (low-pass). That is very little of the\u00a0new\u00a0input is passed, and mostly the previous state is preserved.\u00a0If c is near [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,3],"tags":[],"class_list":["post-1","post","type-post","status-publish","format-standard","hentry","category-filters","category-programming"],"_links":{"self":[{"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/posts\/1","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"count":11,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"predecessor-version":[{"id":18,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/posts\/1\/revisions\/18"}],"wp:attachment":[{"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/synthnotes.ucsd.edu\/wp4\/index.php\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}