|
| 1 | +/* |
| 2 | + * ResonantFilter.h |
| 3 | + * |
| 4 | + * Copyright 2012 Tim Barrass |
| 5 | + * |
| 6 | + * This file is part of Mozzi. |
| 7 | + * |
| 8 | + * Mozzi is licensed under a Creative Commons |
| 9 | + * Attribution-NonCommercial-ShareAlike 4.0 International License. |
| 10 | + * |
| 11 | + */ |
| 12 | + |
| 13 | +#ifndef RESONANTFILTER_H_ |
| 14 | +#define RESONANTFILTER_H_ |
| 15 | + |
| 16 | +#include "IntegerType.h" |
| 17 | +#include "AudioOutput.h" |
| 18 | +#include "meta.h" |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +/* |
| 23 | +simple resonant filter posted to musicdsp.org by Paul Kellett |
| 24 | +http://www.musicdsp.org/archive.php?classid=3#259, applying the |
| 25 | +modification from Peter Schoffhauzer to make it able to output |
| 26 | +all filter types (LowPass, HighPass, Notch and BandPass). |
| 27 | +
|
| 28 | +The generic filter is ResonantFilter<unsigned_t type, FILTER_TYPE>. |
| 29 | + - type specifies the type expected for the cutoff and resonance. Only uint8_t and uint16_t have been tested. These are denoted 8bits and 16bits versions of the filter in the following. |
| 30 | + - FILTER_TYPE specifies the filter type. LOWPASS, BANDPASS, HIGHPASS and NOTCH are available types. |
| 31 | +
|
| 32 | +Two versions are available: the 8bits and 16bits versions (see above). |
| 33 | +The 8bits version is an optimized version that uses 8bits values to set |
| 34 | +the resonance and the cutoff_freq. It can works on 8bits samples only |
| 35 | +on 8bits platforms. |
| 36 | +The 16bits version consumes more CPU ressources but uses 16bits values |
| 37 | +for resonance and cutoff_freq and can work on samples up to 16bits on |
| 38 | +8bits platforms and up to 32 on 32bits platforms. |
| 39 | +
|
| 40 | +The filter can be instanciated using the template version ResonantFilter<unsigned_t type, FILTER_TYPE>. For ease of use, the following types are also accepted: |
| 41 | +
|
| 42 | +8bits versions: LowPassFilter, HighPassFilter, BandPassFilter, NotchFilter |
| 43 | +16bits versions: LowPassFilter16, HighPassFilter16, BandPassFilter16, NotchFilter16 |
| 44 | +
|
| 45 | +
|
| 46 | +
|
| 47 | +//// ALGORITHM //// |
| 48 | +// set feedback amount given f and q between 0 and 1 |
| 49 | +fb = q + q/(1.0 - f); |
| 50 | +In order to avoid a slow division we use the use a Taylor expansion to approximate 1/(1.0 - f): |
| 51 | +Close to f=0: 1/(1.0-f) approx 1.0+f. |
| 52 | +Hence: fb = q + q * (1.0 + f) |
| 53 | +This approximation is less and less valid with an increasing cutoff, leading to a reduction of the resonance of the filter at high cutoff frequencies. |
| 54 | +
|
| 55 | +// for each sample... |
| 56 | +buf0 = buf0 + f * (in - buf0 + fb * (buf0 - buf1)); |
| 57 | +buf1 = buf1 + f * (buf0 - buf1); |
| 58 | +out = buf1; // LowPass |
| 59 | +out = in - buf0; // HighPass |
| 60 | +out = buf0 - buf1; // BandPass |
| 61 | +out = in - buf0 + buf1; // Notch |
| 62 | +
|
| 63 | +fixed point version of the filter |
| 64 | +"dave's blog of art and programming" http://www.pawfal.org/dave/blog/2011/09/ |
| 65 | +*/ |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +enum filter_types { LOWPASS, BANDPASS, HIGHPASS, NOTCH }; |
| 72 | + |
| 73 | +/** A generic resonant filter for audio signals. |
| 74 | + */ |
| 75 | +template<int8_t FILTER_TYPE, typename su=uint8_t> |
| 76 | +class ResonantFilter |
| 77 | +{ |
| 78 | + |
| 79 | +public: |
| 80 | + /** Constructor. |
| 81 | + */ |
| 82 | + ResonantFilter() { ; } |
| 83 | + |
| 84 | + |
| 85 | + /** deprecated. Use setCutoffFreqAndResonance(su cutoff, su |
| 86 | + resonance). |
| 87 | +
|
| 88 | + Set the cut off frequency, |
| 89 | + @param cutoff use the range 0-255 to represent 0-8191 Hz (AUDIO_RATE/2) for ResonantFilter, cutoff use the range 0-65535 to represent 0-AUDIO_RATE/2. |
| 90 | + Be careful of distortion at the lower end, especially with high resonance. |
| 91 | + */ |
| 92 | + void setCutoffFreq(su cutoff) |
| 93 | + { |
| 94 | + f = cutoff; |
| 95 | + fb = q + ucfxmul(q, (typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type) SHIFTED_1 + cutoff); |
| 96 | + } |
| 97 | + |
| 98 | + /** deprecated. Use setCutoffFreqAndResonance(su cutoff, su |
| 99 | + resonance). |
| 100 | +
|
| 101 | + Set the resonance. If you hear unwanted distortion, back off the resonance. |
| 102 | + After setting resonance, you need to call setCuttoffFreq() to hear the change! |
| 103 | + @param resonance in the range 0-255 for ResonantFilter, 0-65535 for ResonantFilter<FILTER_TYPE, uint16_t>, with 255/65535 being most resonant |
| 104 | + @note Remember to call setCuttoffFreq() after resonance is changed! |
| 105 | + */ |
| 106 | + void setResonance(su resonance) { q = resonance; } |
| 107 | + |
| 108 | + /** |
| 109 | + Set the cut off frequency and resonance. Replaces setCutoffFreq() and |
| 110 | + setResonance(). (Because the internal calculations need to be done whenever either parameter changes.) |
| 111 | + @param cutoff range 0-255 represents 0-8191 Hz (AUDIO_RATE/2) for ResonantFilter, range 0-65535 for ResonantFilter16 |
| 112 | + Be careful of distortion at the lower end, especially with high resonance. |
| 113 | + @param resonance range 0-255 for ResonantFilter, 0-65535 for ResonantFilter<FILTER_TYPE, uint16_t>, 255/65535 is most resonant. |
| 114 | + */ |
| 115 | + void setCutoffFreqAndResonance(su cutoff, su resonance) |
| 116 | + { |
| 117 | + f = cutoff; |
| 118 | + q = resonance; // hopefully optimised away when compiled, just here for |
| 119 | + // backwards compatibility |
| 120 | + fb = q + ucfxmul(q,(typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type) SHIFTED_1 + cutoff); |
| 121 | + } |
| 122 | + |
| 123 | + /** Calculate the next sample, given an input signal. |
| 124 | + @param in the signal input. Should not be more than 8bits on 8bits platforms (Arduino) if using the 8bits version and not 16bits version. |
| 125 | + @return the signal output. |
| 126 | + @note Timing: about 11us. |
| 127 | + */ |
| 128 | + // 10.5 to 12.5 us, mostly 10.5 us (was 14us) |
| 129 | + inline AudioOutputStorage_t next(AudioOutputStorage_t in) |
| 130 | + { |
| 131 | + advanceBuffers(in); |
| 132 | + return next(in, Int2Type<FILTER_TYPE>()); |
| 133 | + } |
| 134 | + |
| 135 | +protected: |
| 136 | + su q; |
| 137 | + su f; |
| 138 | + typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type fb; |
| 139 | + AudioOutputStorage_t buf0, buf1; |
| 140 | + const uint8_t FX_SHIFT = sizeof(su) << 3; |
| 141 | + const uint8_t FX_SHIFT_M_1 = FX_SHIFT-1; |
| 142 | + const su SHIFTED_1 = (1<<FX_SHIFT)-1; |
| 143 | + |
| 144 | + // // multiply two fixed point numbers (returns fixed point) |
| 145 | + // inline |
| 146 | + // long fxmul(long a, long b) |
| 147 | + // { |
| 148 | + // return (a*b)>>FX_SHIFT; |
| 149 | + // } |
| 150 | + |
| 151 | + inline void advanceBuffers(AudioOutputStorage_t in) |
| 152 | + { |
| 153 | + buf0 += fxmul(((in - buf0) + fxmul(fb, buf0 - buf1)), f); |
| 154 | + buf1 += ifxmul(buf0 - buf1, f); // could overflow if input changes fast |
| 155 | + } |
| 156 | + |
| 157 | + inline AudioOutputStorage_t next(AudioOutputStorage_t in, Int2Type<LOWPASS>) {return buf1;} |
| 158 | + |
| 159 | + inline AudioOutputStorage_t next(AudioOutputStorage_t in, Int2Type<HIGHPASS>) {return in - buf0;} |
| 160 | + |
| 161 | + inline AudioOutputStorage_t next(AudioOutputStorage_t in, Int2Type<BANDPASS>) {return buf0-buf1;} |
| 162 | + |
| 163 | + inline AudioOutputStorage_t next(AudioOutputStorage_t in, Int2Type<NOTCH>) {return in - buf0 + buf1;} |
| 164 | + |
| 165 | + // multiply two fixed point numbers (returns fixed point) |
| 166 | + inline typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type ucfxmul(su a, typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type b) |
| 167 | + { |
| 168 | + return (((typename IntegerType<sizeof(su)+sizeof(su)>::unsigned_type)a * (b >> 1)) >> (FX_SHIFT_M_1)); |
| 169 | + } |
| 170 | + |
| 171 | + // multiply two fixed point numbers (returns fixed point) |
| 172 | + inline typename IntegerType<sizeof(AudioOutputStorage_t)+sizeof(su)-1>::signed_type ifxmul(typename IntegerType<sizeof(AudioOutputStorage_t )+sizeof(su)-1>::signed_type a, su b) { return ((a * b) >> FX_SHIFT); } |
| 173 | + |
| 174 | + // multiply two fixed point numbers (returns fixed point) |
| 175 | + inline typename IntegerType<sizeof(AudioOutputStorage_t)+sizeof(AudioOutputStorage_t)>::signed_type fxmul(typename IntegerType<sizeof(AudioOutputStorage_t)+sizeof(AudioOutputStorage_t)>::signed_type a, typename IntegerType<sizeof(AudioOutputStorage_t)+sizeof(su)-1>::signed_type b) { return ((a * b) >> FX_SHIFT); } |
| 176 | +}; |
| 177 | + |
| 178 | +/** A generic filter for audio signals that can produce lowpass, highpass, bandpass and notch outputs at runtime. |
| 179 | +Behaves like ResonantFilter for setting the resonance and cutoff frequency. |
| 180 | +Like ResonantFilter, it can be used on different sample sizes: MultiResonantFilter<uint8_t> and MultiResonantFilter<uint16_t> have been tested. |
| 181 | +For the former, both cutoff and resonance are uint8_t, hence between 0-255. |
| 182 | +For the later, both cutoff and resonance are uint16_t, hence between 0-65535. |
| 183 | + */ |
| 184 | +template<typename su=uint8_t> |
| 185 | +class MultiResonantFilter: public ResonantFilter<LOWPASS,su> |
| 186 | +{ |
| 187 | +public: |
| 188 | + /** Compute the filters, given an input signal. |
| 189 | + @param in the signal input. Should not be more than 8bits on 8bits platforms (Arduino) if using the 8bits version and not 16bits version. |
| 190 | + */ |
| 191 | +inline void next (AudioOutputStorage_t in) |
| 192 | + { |
| 193 | + last_in = in; |
| 194 | + ResonantFilter<LOWPASS,su>::advanceBuffers(in); |
| 195 | + } |
| 196 | + /** Return the input filtered with a lowpass filter |
| 197 | + @return the filtered signal output. |
| 198 | + */ |
| 199 | + inline AudioOutputStorage_t low() {return ResonantFilter<LOWPASS,su>::next(last_in,Int2Type<LOWPASS>());} |
| 200 | + /** Return the input filtered with a highpass filter |
| 201 | + @return the filtered signal output. |
| 202 | + */ |
| 203 | + inline AudioOutputStorage_t high() {return ResonantFilter<LOWPASS,su>::next(last_in,Int2Type<HIGHPASS>());} |
| 204 | + /** Return the input filtered with a bandpass filter |
| 205 | + @return the filtered signal output. |
| 206 | + */ |
| 207 | + inline AudioOutputStorage_t band() {return ResonantFilter<LOWPASS,su>::next(last_in,Int2Type<BANDPASS>());} |
| 208 | + /** Return the input filtered with a notch filter |
| 209 | + @return the filtered signal output. |
| 210 | + */ |
| 211 | + inline AudioOutputStorage_t notch() {return ResonantFilter<LOWPASS,su>::next(last_in,Int2Type<NOTCH>());} |
| 212 | + |
| 213 | +private: |
| 214 | + AudioOutputStorage_t last_in; |
| 215 | +}; |
| 216 | + |
| 217 | + |
| 218 | +typedef ResonantFilter<LOWPASS> LowPassFilter; |
| 219 | +typedef ResonantFilter<LOWPASS, uint16_t> LowPassFilter16; |
| 220 | +/* |
| 221 | +typedef ResonantFilter<uint8_t, HIGHPASS> HighPassFilter; |
| 222 | +typedef ResonantFilter<uint16_t, HIGHPASS> HighPassFilter16; |
| 223 | +typedef ResonantFilter<uint8_t, BANDPASS> BandPassFilter; |
| 224 | +typedef ResonantFilter<uint16_t, BANDPASS> BandPassFilter16; |
| 225 | +typedef ResonantFilter<uint8_t, NOTCH> NotchFilter; |
| 226 | +typedef ResonantFilter<uint16_t, NOTCH> NotchFilter16; |
| 227 | +*/ |
| 228 | + |
| 229 | + |
| 230 | +/** |
| 231 | +@example 10.Audio_Filters/ResonantFilter/ResonantFilter.ino |
| 232 | +This example demonstrates the ResonantFilter specification of this class. |
| 233 | +
|
| 234 | +@example 10.Audio_Filters/ResonantFilter16/ResonantFilter16.ino |
| 235 | +This example demonstrates the ResonantFilter16 specification of this class. |
| 236 | +
|
| 237 | +@example 10.Audio_Filters/MultiResonantFilter/MultiResonantFilter.ino |
| 238 | +This example demonstrates the MultiResonantFilter specification of this class. |
| 239 | +*/ |
| 240 | + |
| 241 | +#endif /* RESONANTFILTER_H_ */ |
0 commit comments