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