Mozzi  version v2.0
sound synthesis library for Arduino
Smooth.h
1 /*
2  * Smooth.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 SMOOTH_H_
13 #define SMOOTH_H_
14 
15 #include "Arduino.h"
16 #include "mozzi_fixmath.h"
17 
18 /** A simple infinite impulse response low pass filter for smoothing control or audio signals.
19 This algorithm comes from http://en.wikipedia.org/wiki/Low-pass_filter:
20 y[i] := y[i-1] + α * (x[i] - y[i-1]),
21 translated as
22 out = last_out + a * (in - last_out).
23 It's not calibrated to any real-world update rate, so if you use it at
24 MOZZI_CONTROL_RATE and you change MOZZI_CONTROL_RATE, you'll need to adjust the smoothness
25 value to suit.
26 @tparam T the type of numbers being smoothed. Watch out for numbers overflowing the
27 internal calculations. Some experimentation is recommended.
28 @note Timing: ~5us for 16 bit types, ~1us for 8 bit types.
29 @todo Check if 8 bit templates can work efficiently with a higher res smoothness -
30  as is they don't have enough resolution to work well at audio rate. See if Line might be
31  more useful in most cases.
32 */
33 
34 template <class T>
35 class Smooth
36 {
37 private:
38  long last_out;
39  Q0n16 a;
40 
41 public:
42  /** Constructor.
43  @param smoothness sets how much smoothing the filter will apply to
44  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
45  very smooth.
46  */
47  Smooth(float smoothness)
48  {
49  setSmoothness(smoothness);
50  }
51 
52  /** Constructor.
53  This constructor which doesn't take a smoothness parameter is useful when you incorporate Smooth into another class definition.
54  You need to call setSmoothness(float) for your object before using Smooth.
55  @note there's probably a better way to do this...
56  */
58  {}
59 
60 
61  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
62  @param in the signal to be smoothed.
63  @return the filtered signal.
64  */
65  inline
66  T next(T in)
67  {
68  long out = ((((((long)in - (last_out>>8)) * a))>>8) + last_out);
69  last_out = out;
70  return (T)(out>>8);
71  }
72 
73 
74  /** Filters the input and returns the filtered value. Same as next(input-value).
75  @param in the signal to be smoothed.
76  @return the filtered signal.
77  */
78  inline
79  T operator()(T n) {
80  return next(n);
81  }
82 
83 
84  /** Sets how much smoothing the filter will apply to its input.
85  @param smoothness sets how much smoothing the filter will apply to
86  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
87  very smooth.
88  */
89  inline
90  void setSmoothness(float smoothness)
91  {
92  a=float_to_Q0n16(1.f-smoothness);
93  }
94 
95 };
96 
97 
98 /** @cond */ // doxygen can ignore the specialisations
99 
100 /** uint8_t specialisation of Smooth template*/
101 template <>
102 class Smooth <uint8_t>
103 {
104 private:
105  unsigned int last_out;
106  Q0n8 a;
107 
108 public:
109  /** Constructor.
110  @param smoothness sets how much smoothing the filter will apply to
111  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
112  very smooth.
113  */
114  Smooth(float smoothness)
115  {
116  setSmoothness(smoothness);
117  }
118 
119  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
120  @param in the signal to be smoothed.
121  @return the filtered signal.
122  */
123  inline
124  uint8_t next(uint8_t in)
125  {
126  unsigned int out = (((((int)in - (last_out>>8)) * a)) + last_out);
127  last_out = out;
128  return (uint8_t)(out>>8);
129  }
130 
131 
132  /** Filters the input and returns the filtered value. Same as next(input-value).
133  @param in the signal to be smoothed.
134  @return the filtered signal.
135  */
136  inline
137  uint8_t operator()(uint8_t n) {
138  return next(n);
139  }
140 
141 
142  /** Sets how much smoothing the filter will apply to its input.
143  @param smoothness sets how much smoothing the filter will apply to
144  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
145  very smooth.
146  */
147  inline
148  void setSmoothness(float smoothness)
149  {
150  a=float_to_Q0n8(1.f-smoothness);
151  }
152 
153 };
154 
155 
156 /** int8_t specialisation of Smooth template*/
157 template <>
158 class Smooth <int8_t>
159 {
160 private:
161  int last_out;
162  Q0n8 a;
163 
164 public:
165  /** Constructor.
166  @param smoothness sets how much smoothing the filter will apply to
167  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
168  very smooth.
169  */
170  Smooth(float smoothness)
171  {
172  setSmoothness(smoothness);
173  }
174 
175 
176  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
177  @param in the signal to be smoothed.
178  @return the filtered signal.
179  */
180  inline
181  int8_t next(int8_t in)
182  {
183  int out = (((((int)in - (last_out>>8)) * a)) + last_out);
184  last_out = out;
185  return (int8_t)(out>>8);
186  }
187 
188 
189  /** Filters the input and returns the filtered value. Same as next(input-value).
190  @param in the signal to be smoothed.
191  @return the filtered signal.
192  */
193  inline
194  int8_t operator()(int8_t n) {
195  return next(n);
196  }
197 
198 
199  /** Sets how much smoothing the filter will apply to its input.
200  @param smoothness sets how much smoothing the filter will apply to
201  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
202  very smooth.
203  */
204  inline
205  void setSmoothness(float smoothness)
206  {
207  a=float_to_Q0n8(1.f-smoothness);
208  }
209 
210 };
211 
212 /** float specialisation of Smooth template*/
213 template <>
214 class Smooth <float>
215 {
216 private:
217  float last_out;
218  float a;
219 
220 public:
221  /** Constructor.
222  @param smoothness sets how much smoothing the filter will apply to
223  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
224  very smooth.
225  */
226  Smooth(float smoothness)
227  {
228  setSmoothness(smoothness);
229  }
230 
231  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
232  @param in the signal to be smoothed.
233  @return the filtered signal.
234  */
235  inline
236  float next(float in)
237  {
238  float out = last_out + a * (in - last_out);
239  //float out = (in - last_out * a) + last_out;
240  last_out = out;
241  return out;
242  }
243 
244 
245  /** Filters the input and returns the filtered value. Same as next(input-value).
246  @param in the signal to be smoothed.
247  @return the filtered signal.
248  */
249  inline
250  float operator()(float n) {
251  return next(n);
252  }
253 
254 
255  /** Sets how much smoothing the filter will apply to its input.
256  @param smoothness sets how much smoothing the filter will apply to
257  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
258  very smooth.
259  */
260  inline
261  void setSmoothness(float smoothness)
262  {
263  a=1.f-smoothness;
264  }
265 
266 };
267 
268 
269 /** @endcond */
270 
271 
272 /* Specialization for UFix */
273 template<int8_t NI, int8_t NF>
274 class Smooth<UFix<NI,NF>>
275 {
276 private:
277  typedef UFix<NI, NF> internal_type;
278  internal_type last_out;
279  UFix<0,16> a;
280 
281 public:
282 
283 
284  /** Constructor.
285  @param smoothness sets how much smoothing the filter will apply to
286  its input. Use a float or a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is
287  very smooth.
288  */
289  template<typename T>
290  Smooth(T smoothness)
291  {
292  setSmoothness(smoothness);
293  }
294 
295  /** Constructor.
296  This constructor which doesn't take a smoothness parameter is useful when you incorporate Smooth into another class definition.
297  You need to call setSmoothness(float) for your object before using Smooth.
298  @note there's probably a better way to do this...
299  */
300  Smooth()
301  {}
302 
303 
304  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
305  @param in the signal to be smoothed.
306  @return the filtered signal.
307  */
308  inline
309  internal_type next(internal_type in)
310  {
311  internal_type out = last_out + a * (in - last_out); // With FixMath, the syntax is actually the same than with floats :)
312  last_out = out;
313  return out;
314  }
315 
316 
317 
318  inline
319  internal_type operator()(internal_type n) {
320  return next(n);
321  }
322 
323  /** Sets how much smoothing the filter will apply to its input.
324  @param smoothness sets how much smoothing the filter will apply to
325  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
326  very smooth.
327  */
328  inline
329  void setSmoothness(float smoothness)
330  {
331  a=internal_type(1.f-smoothness);
332  }
333 
334  /** Sets how much smoothing the filter will apply to its input.
335  @param smoothness sets how much smoothing the filter will apply to
336  its input. Use a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is
337  very smooth.
338  */
339  template<int8_t _NF>
340  void setSmoothness(UFix<0,_NF> smoothness)
341  {
342  a = UFix<1,0>(1) - smoothness;
343  }
344 
345 };
346 
347 
348 
349 
350 /* Specialization for SFix */
351 template<int8_t NI, int8_t NF>
352 class Smooth<SFix<NI,NF>>
353 {
354 private:
355  typedef SFix<NI, NF> internal_type;
356  internal_type last_out;
357  UFix<0,16> a;
358 
359 public:
360 
361 
362  /** Constructor.
363  @param smoothness sets how much smoothing the filter will apply to
364  its input. Use a float or a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is
365  very smooth.
366  */
367  template<typename T>
368  Smooth(T smoothness)
369  {
370  setSmoothness(smoothness);
371  }
372 
373  /** Constructor.
374  This constructor which doesn't take a smoothness parameter is useful when you incorporate Smooth into another class definition.
375  You need to call setSmoothness(float) for your object before using Smooth.
376  @note there's probably a better way to do this...
377  */
378  Smooth()
379  {}
380 
381 
382  /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value).
383  @param in the signal to be smoothed.
384  @return the filtered signal.
385  */
386  inline
387  internal_type next(internal_type in)
388  {
389  internal_type out = last_out + a * (in - last_out);
390  last_out = out;
391  return out;
392  }
393 
394 
395 
396  inline
397  internal_type operator()(internal_type n) {
398  return next(n);
399  }
400 
401  /** Sets how much smoothing the filter will apply to its input.
402  @param smoothness sets how much smoothing the filter will apply to
403  its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is
404  very smooth.
405  */
406  inline
407  void setSmoothness(float smoothness)
408  {
409  a=internal_type(1.f-smoothness);
410  }
411 
412  /** Sets how much smoothing the filter will apply to its input.
413  @param smoothness sets how much smoothing the filter will apply to
414  its input. Use a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is
415  very smooth.
416  */
417  template<int8_t _NF>
418  void setSmoothness(UFix<0,_NF> smoothness)
419  {
420  a = UFix<1,0>(1) - smoothness;
421  }
422 
423 };
424 
425 /**
426 @example 05.Control_Filters/Smooth/Smooth.ino
427 This example demonstrates the Smooth class.
428 */
429 
430 #endif /* SMOOTH_H_ */