# frequency filtering related to [digital signal processing](../sound.html) digital filters are tools used to attenuate or remove frequencies or change phase relationships between frequencies. it is an effect on the amplitude of frequency partials. # links * [algorithm for a state-variable filter on cytomic.com](https://cytomic.com/index.php?q=technical-papers) * [elementary filters on dsprelated.com](https://www.dsprelated.com/freebooks/filters/Elementary_Filter_Sections.html) * [first section on digital filters on dspguide.com](http://www.dspguide.com/ch14.htm) * [guides on earlevel.com](http://www.earlevel.com/main/?s=filter) * [tomroelandts.com](https://tomroelandts.com/articles/how-to-create-a-simple-low-pass-filter) how to create a simple windowed-sinc low-pass filter and other guides * wikipedia: [fir filter](https://en.wikipedia.org/wiki/Finite_impulse_response), [iir filter](https://en.wikipedia.org/wiki/Infinite_impulse_response), [low-pass filter](https://en.wikipedia.org/wiki/Low-pass_filter), [q-factor](https://en.wikipedia.org/wiki/Q_factor), [sinc filter](https://en.wikipedia.org/wiki/Sinc_filter), [state-variable filter](https://en.wikipedia.org/wiki/State_variable_filter) # filter variants * low-pass: low frequencies remain * high-pass: high frequencies remain * band-pass: a range of frequencies remains * band-reject: a range of frequencies is attenuated * all-pass: change phases * filter bank: multiple band-bass filters and multiple outputs. low-pass and high-pass can be used at the edges * equaliser: filter bank with output amplitudes modifiable and outputs recombined. other implementations possible # common filter types * windowed sinc * pro: extremely good frequency separation, custom frequency responses possible, inherent unity gain if impulse response is set up correctly * con: parameter changes require recreation of impulse response array * based on convolving the input samples with a centered sinc function array of limited length where the edges slowy fade to zero * the filters impulse response is a sinc function in the time domain and its frequency response is a rectangular function * the longer the impulse response kernel, the smaller the transition bandwidth * addition of kernels: adding another filter into the stop-band of another * convolution of kernels: adding another filter into the pass-band of another * take values of the sinc function, window it with a blackman window which performs well in this context, and use this array for convolution with an input signal * a high-pass filter can be created by spectrally inverting or spectrally reversing the impulse response * a band-pass filter can be created by convolution of low- and high-pass filter impulse responses * a band-reject filter can be created by summing low-pass and high-pass filter impulse responses * state-variable filter * pro: low/high/band/notch/peak/all at once via multiple outputs * con: ensuring unity gain and using multiple passes adds processing costs * related to the biquad filter * one-pole, one-zero, multi-pole * con: stop-band rises the higher the cutoff * biquad * moving average * con: almost useless for removing frequencies because it filters with large ripples in the frequency spectrum * pro: good for smoothing and anywhere where time domain shape matters more than frequencies. a centered moving average creates no shift between input and output signal * moving median and similar also possible # terms * stopband: band where frequencies are attenuated most * passband: band where frequencies are kept * transition band: slope between stop and passband * cutoff: start of the transition band at the passband. band-pass or band-reject filter have two cutoff frequencies * q-factor: characterises a resonators bandwidth relative to its centre frequency * passes: filter output can be re-fed into the filter to possible increase the frequency separation * poles: number of coefficients to shape the filter response * difference equation: maps input samples to output samples in the case of fir filter implementations for example # tips for implementing digital filters * to make a windowed-sinc filter give seamless output when called frame-wise on continuous data, carryover data from the convolution must be kept between calls. convolution can be made seamless by writing input-length samples into output and the additional impulse-response-length samples that would occur into an additional carryover buffer that is written into the beginning of the output of the next call * to make multipass filters seamless you would need to keep a state value per pass * c code for seamless convolution and windowed-sinc filtering can be found in [sph-sp](https://github.com/sph-mn/sph-sp) * processing costs for windowed-sincs with frequently changing parameters can be reduced by reducing the resolution of changes. for example, only applying parameter changes for every 960 samples * band-pass filters can be optimized to become only low-pass or high-pass filters at the edges # exemplary one-pole filter implementations x: input, y: output, alpha: cutoff as fraction of the sample rate high-pass ~~~ for i from 1 to n y[i] := alpha * (y[i-1] + x[i] - x[i-1]) ~~~ low-pass ~~~ for i from 1 to n y[i] := y[i-1] + alpha * (x[i] - y[i-1]) ~~~ # exemplary state-variable filter implementation in c * defines a routine for a fast filter that supports multiple filter types in one * only one output is taken but comments show how other filter results can be taken instead * state is to make subsequent call results for continuous data seamless * state must hold two elements and is allocated and owned by the caller * cutoff is as a fraction of the sample rate between 0 and 0.5 * based on the state-variable filter described here: * http://www.cytomic.com/technical-papers * http://www.earlevel.com/main/2016/02/21/filters-for-synths-starting-out/ ~~~ #include #include void state_variable_filter(double* out, double* in, double in_count, double cutoff, double q_factor, double* state) { double a1; double a2; double g; double ic1eq; double ic2eq; uint32_t i; double k; double v0; double v1; double v2; ic1eq = state[0]; ic2eq = state[1]; g = tan(M_PI * cutoff); k = 2 - 2 * q_factor; a1 = 1 / (1 + g * (g + k)); a2 = g * a1; for (i = 0; i < in_count; i = (1 + i)) { v0 = in[i]; v1 = a1 * ic1eq + a2 * (v0 - ic2eq); v2 = ic2eq + (g * v1); ic1eq = 2 * v1 - ic1eq; ic2eq = 2 * v2 - ic2eq; // low-pass out[i] = v2; // instead of the low-pass result, other results could be written to out, // or multiple results if multiple output arrays are made available // high-pass //out[i] = v0 - k * v - v2; // band-pass //out[i] = v1; // band-reject //out[i] = v0 - k * v1; // peak //out[i] = 2 * v2 - v0 + k * v1; // all-pass //out[i] = v0 - 2 * k * v1; }; state[0] = ic1eq; state[1] = ic2eq; } ~~~