1 /*******************************************************************************
2 
3 "A Collection of Useful C++ Classes for Digital Signal Processing"
4  By Vinnie Falco
5 
6 Official project location:
7 https://github.com/vinniefalco/DSPFilters
8 
9 See Documentation.h for contact information, notes, and bibliography.
10 
11 --------------------------------------------------------------------------------
12 
13 License: MIT License (http://www.opensource.org/licenses/mit-license.php)
14 Copyright (c) 2009 by Vinnie Falco
15 
16 Permission is hereby granted, free of charge, to any person obtaining a copy
17 of this software and associated documentation files (the "Software"), to deal
18 in the Software without restriction, including without limitation the rights
19 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 copies of the Software, and to permit persons to whom the Software is
21 furnished to do so, subject to the following conditions:
22 
23 The above copyright notice and this permission notice shall be included in
24 all copies or substantial portions of the Software.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 THE SOFTWARE.
33 
34 ********************************************************************************
35 
36 ="A Collection of Useful C++ Classes for Digital Signal Processing"=
37 _By Vincent Falco_
38 
39 _"Techniques for digital signal processing are well guarded and held
40 close to the chest, as they have valuable applications for multimedia
41 content. The black art of Infinite Impulse Response ("IIR") filtering
42 has remained shrouded in secrecy with little publicly available source
43 code....until now."_
44 
45 ----
46 Building on the work of cherished luminaries such as Sophocles Orfanidis, Andreas Antoniou, Martin Holters, and Udo Zolzer, this library harnesses the power of C++ templates to solve a useful problem in digital signal processing: the realization of multichannel IIR filters of arbitrary order and prescribed specifications with various properties such as Butterworth, Chebyshev, Elliptic, and Optimum-L (Legendre) responses. The library is provided under the MIT license and is therefore fully compatible with proprietary usage.
47 
48 Classes are designed as independent re-usable building blocks. Use some or all of the provided features, or extend the functionality by writing your own objects that plug into the robust framework. Only the code that you need will get linked into your application. Here's a list of features:
49 
50 	* Exclusive focus on IIR filters instead of boring FIR filters
51 	* Complete implementation of all "RBJ Biquad" Cookbook filter formulas
52 	* Butterworth, Chebyshev, Elliptic, Bessel, Legendre designs
53 	* Low Pass, High Pass, Band Pass, Band Stop transformations
54 	* Low, High, and Band Shelf filter implementations for most types
55 	* Smooth interpolation of filter settings, pole/zeros, and biquad coefficients to achieve seamless parameter changes
56 	* Representation of digital filters using poles and zeros
57 	* Realization using Direct Form I, Direct Form II, or user provided class
58 	* Fully factored to minimize template instantiations
59 	* "Design" layer provides runtime introspection into a filter
60 	* Utility template functions for manipulating buffers of sample data
61 	* No calls to malloc or new, great for embedded systems
62 	* No external dependencies, just the standard C++ library!
63 
64 Using these filters is easy:
65 
66 {{{
67     // Create a Chebyshev type I Band Stop filter of order 3
68     // with state for processing 2 channels of audio.
69     Dsp::SimpleFilter <Dsp::ChebyshevI::BandStop <3>, 2> f;
70     f.setup (3,    // order
71              44100,// sample rate
72              4000, // center frequency
73              880,  // band width
74              1);   // ripple dB
75     f.process (numSamples, arrayOfChannels);
76 }}}
77 
78 An accompanying demonstration program that works on most popular platforms by using the separately licensed Juce application framework (included), exercises all the functionality of the library, including these features:
79 
80 	* Dynamic interface creates itself using filter introspection capabilities
81 	* Audio playback with real time application of a selected filter
82 	* Live time stretching and amplitude modulation without clicks or popping
83 	* Charts to show magnitude, phase response and pole/zero placement
84 	* Thread safety "best practices" for audio applications
85 
86 Here's a screenshot of the DspFilters Demo
87 
88 http://dspfilterscpp.googlecode.com/files/dspfiltersdemo.png
89 
90 If you've been searching in futility on the Internet for some source code for implementing high order filters, then look no further because this is it! Whether you are a student of C++ or digital signal processing, a writer of audio plugins, or even a VST synthesizer coder, "A Collection of Useful C++ Classes for Digital Signal Processing" might have something for you!
91 
92 ********************************************************************************
93 
94 Notes:
95 
96   Please direct all comments this DSP and Plug-in Development forum:
97 
98   http://www.kvraudio.com/forum/viewforum.php?f=33
99 
100 Credits
101 
102   All of this code was written by the author Vinnie Falco except where marked.
103 
104   Some filter ideas are based on a java applet (http://www.falstad.com/dfilter/)
105   developed by Paul Falstad.
106 
107 Bibliography
108 
109   "High-Order Digital Parametric Equalizer Design"
110    Sophocles J. Orfanidis
111    (Journal of the Audio Engineering Society, vol 53. pp 1026-1046)
112 
113   http://crca.ucsd.edu/~msp/techniques/v0.08/book-html/node1.html
114 
115   "Spectral Transformations for digital filters"
116    A. G. Constantinides, B.Sc.(Eng.) Ph.D.
117    (Proceedings of the IEEE, vol. 117, pp. 1585-1590, August 1970)
118 
119 ********************************************************************************
120 
121 DOCUMENTATION
122 
123 All symbols are in the Dsp namespace.
124 
125 class Filter
126 
127   This is an abstract polymorphic interface that supports any filter. The
128   parameters to the filter are passed in the Params structure, which is
129   essentially an array of floating point numbers with a hard coded size
130   limit (maxParameters). Each filter makes use of the Params as it sees fit.
131 
132   Filter::getKind ()
133   Filter::getName ()
134   Filter::getNumParams ()
135   Filter::getParamInfo ()
136 
137   Through the use of these functions, the caller can determine the meaning
138   of each indexed filter parameter at run-time. The ParamInfo structure
139   contains methods that describe information about an individual parameter,
140   including convenience functions to map a filter parameter to a "control
141   value" in the range 0...1, suitable for presentation by a GUI element such
142   as a knob or scrollbar.
143 
144   Filter::getDefaultParams ()
145   Filter::getParams ()
146   Filter::getParam ()
147   Filter::setParam ()
148   Filter::findParamId ()
149   Filter::setParamById ()
150   Filter::setParams ()
151   Filter::copyParamsFrom ()
152 
153   These methods allow the caller to inspect the values of the parameters,
154   and set the filter parameters in various ways. When parameters are changed
155   they take effect on the filter immediately.
156 
157   Filter::getPoleZeros ()
158   Filter::response ()
159 
160   For analysis, these routines provide insight into the pole/zero arrangement
161   in the z-plane, and the complex valued response at a given normalized
162   frequency in the range (0..nyquist = 0.5]. From the complex number the
163   magnitude and phase can be calculated.
164 
165   Filter::getNumChannels()
166   Filter::reset()
167   Filter::process()
168 
169   These functions are for applying the filter to channels of data. If the
170   filter was not created with channel state (i.e. Channels==0 in the derived
171   class template) then they will throw an exception.
172 
173   To create a Filter object, use operator new on a subclass template with
174   appropriate parameters based on the type of filter you want. Here are the
175   subclasses.
176 
177 
178 
179 template <class DesignClass, int Channels = 0, class StateType = DirectFormII>
180 class FilterDesign : public Filter
181 
182   This subclass of Filter takes a DesignClass (explained below) representing
183   a filter, an optional parameter for the number of channels of data to
184   process, and an optional customizable choice of which state realization
185   to use for processing samples. Channels may be zero, in which case the
186   object can only be used for analysis.
187 
188   Because the DesignClass is a member and not inherited, it is in general
189   not possible to call members of the DesignClass directly. You must go
190   through the Filter interface.
191 
192 
193 
194 template <class DesignClass, int Channels, class StateType = DirectFormII>
195 class SmoothedFilterDesign : public Filter
196 
197   This subclass of FilterDesign implements a filter of the given DesignClass,
198   and also performs smoothing of parameters over time. Specifically, when
199   one or more filter parameters (such as cutoff frequency) are changed, the
200   class creates a transition over a given number of samples from the original
201   values to the new values. This process is invisible and seamless to the
202   caller, except that the constructor takes an additional parameter that
203   indicates the duration of transitions when parameters change.
204 
205 
206 
207 template <class FilterClass, int Channels = 0, class StateType = DirectFormII>
208 class SimpleFilter : public FilterClass
209 
210   This is a simple wrapper around a given raw FilterClass (explained below).
211   It uses inheritance so all of the members of the FilterClass are available
212   to instances of this object. The simple wrapper provides state information
213   for processing channels in the given form.
214 
215   The wrapper does not support introspection, parameter smoothing, or the
216   Params style of applying filter settings. Instead, it uses the interface
217   of the given FilterClass, which is typically a function called setup()
218   that takes a list of arguments representing the parameters.
219 
220   The use of this class bypasses the virtual function overhead of going
221   through a Filter object. It is not practical to change filter parameters
222   of a SimpleFilter, unless you are re-using the filter for a brand new
223   stream of data in which case reset() should be called immediately before
224   or after changing parameters, to clear the state and prevent audible
225   artifacts.
226 
227 
228 
229 Filter family namespaces
230 
231   Each family of filters is given its own namespace. Currently these namespaces
232   include:
233 
234   RBJ:          Filters from the RBJ Cookbook
235   Butterworth:  Filters with Butterworth response
236   ChebyshevI:   Filters using Chebyshev polynomials (ripple in the passband)
237   ChebyshevII:  "Inverse Chebyshev" filters (ripple in the stopband)
238   Elliptic:     Filters with ripple in both the passband and stopband
239   Bessel:       Uses Bessel polynomials, theoretically with linear phase
240   Legendre:     "Optimum-L" filters with steepest transition and monotonic passband.
241   Custom:       Simple filters that allow poles and zeros to be specified directly
242 
243 <class FilterClass>
244 
245   Within each namespace we have a set of "raw filters" (each one is an example
246   of a FilterClass). For example, the raw filters in the Butterworth namespace are:
247 
248   Butterworth::LowPass
249   Butterworth::HighPass
250   Butterworth::BandPass
251   Butterworth::BandStop
252   Butterworth::LowShelf
253   Butterworth::HighShelf
254   Butterworth::BandShelf
255 
256   When a class template (such as SimpleFilter) requires a FilterClass, it is
257   expecting an identifier of a raw filter. For example, Legendre::LowPass. The
258   raw filters do not have any support for introspection or the Params style of
259   changing filter settings. All they offer is a setup() function for updating
260   the IIR coefficients to a given set of parameters.
261 
262 <class DesignClass>
263 
264   Each filter family namespace also has the nested namespace "Design". Inside
265   this namespace we have all of the raw filter names repeated, except that
266   these classes additional provide the Design interface, which adds
267   introspection, polymorphism, the Params style of changing filter settings,
268   and in general all of the features necessary to interoperate with the Filter
269   virtual base class and its derived classes. For example, the design filters
270   from the Butterworth namespace are:
271 
272   Butterworth::Design::LowPass
273   Butterworth::Design::HighPass
274   Butterworth::Design::BandPass
275   Butterworth::Design::BandStop
276   Butterworth::Design::LowShelf
277   Butterworth::Design::HighShelf
278   Butterworth::Design::BandShelf
279 
280   For any class template that expects a DesignClass, you must pass a suitable
281   object from the Design namespace of the desired filter family. For example,
282   ChebyshevI::Design::BandPass.
283 
284 *******************************************************************************/
285 
286 //
287 // Usage Examples
288 //
289 // This shows you how to operate the filters
290 //
291 
292 // This is the only include you need
293 #include "DspFilters/Dsp.h"
294 
295 #include <sstream>
296 #include <iostream>
297 #include <iomanip>
298 
299 namespace {
300 
UsageExamples()301 void UsageExamples ()
302 {
303   // create a two channel audio buffer
304   int numSamples = 2000;
305   float* audioData[2];
306   audioData[0] = new float[numSamples];
307   audioData[1] = new float[numSamples];
308 
309   // create a 2-channel RBJ Low Pass with parameter smoothing
310   // and apply it to the audio data
311   {
312     // "1024" is the number of samples over which to fade parameter changes
313     Dsp::Filter* f = new Dsp::SmoothedFilterDesign
314       <Dsp::RBJ::Design::LowPass, 2> (1024);
315     Dsp::Params params;
316     params[0] = 44100; // sample rate
317     params[1] = 4000; // cutoff frequency
318     params[2] = 1.25; // Q
319     f->setParams (params);
320     f->process (numSamples, audioData);
321   }
322 
323   // set up a 2-channel RBJ High Pass with parameter smoothing,
324   // but bypass virtual function overhead
325   {
326     // the difference here is that we don't go through a pointer.
327     Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 2> f (1024);
328     Dsp::Params params;
329     params[0] = 44100; // sample rate
330     params[1] = 4000; // cutoff frequency
331     params[2] = 1.25; // Q
332     f.setParams (params);
333     f.process (numSamples, audioData);
334   }
335 
336   // create a 2-channel Butterworth Band Pass of order 4,
337   // with parameter smoothing and apply it to the audio data.
338   // Output samples are generated using Direct Form II realization.
339   {
340     Dsp::Filter* f = new Dsp::SmoothedFilterDesign
341       <Dsp::Butterworth::Design::BandPass <4>, 2, Dsp::DirectFormII> (1024);
342     Dsp::Params params;
343     params[0] = 44100; // sample rate
344     params[1] = 4; // order
345     params[2] = 4000; // center frequency
346     params[3] = 880; // band width
347     f->setParams (params);
348     f->process (numSamples, audioData);
349   }
350 
351   // create a 2-channel Inverse Chebyshev Low Shelf of order 5
352   // and passband ripple 0.1dB, without parameter smoothing and apply it.
353   {
354     Dsp::Filter* f = new Dsp::FilterDesign
355       <Dsp::ChebyshevII::Design::LowShelf <5>, 2>;
356     Dsp::Params params;
357     params[0] = 44100; // sample rate
358     params[1] = 5; // order
359     params[2] = 4000; // corner frequency
360     params[3] = 6; // shelf gain
361     params[4] = 0.1; // passband ripple
362     f->setParams (params);
363     f->process (numSamples, audioData);
364   }
365 
366   // create an abstract Butterworth High Pass of order 4.
367   // This one can't process channels, it can only be used for analysis
368   // (i.e. extract poles and zeros).
369   {
370     Dsp::Filter* f = new Dsp::FilterDesign
371       <Dsp::Butterworth::Design::HighPass <4> >;
372     Dsp::Params params;
373     params[0] = 44100; // sample rate
374     params[1] = 4; // order
375     params[2] = 4000; // cutoff frequency
376     f->setParams (params);
377     // this will cause a runtime assertion
378     f->process (numSamples, audioData);
379   }
380 
381   // Use the simple filter API to create a Chebyshev Band Stop of order 3
382   // and 1dB ripple in the passband. The simle API has a smaller
383   // footprint, but no introspection or smoothing.
384   {
385     // Note we use the raw filter instead of the one
386     // from the Design namespace.
387     Dsp::SimpleFilter <Dsp::ChebyshevI::BandStop <3>, 2> f;
388     f.setup (3,    // order
389              44100,// sample rate
390              4000, // center frequency
391              880,  // band width
392              1);   // ripple dB
393     f.process (numSamples, audioData);
394   }
395 
396   // Set up a filter, extract the coefficients and print them to standard
397   // output. Note that this filter is not capable of processing samples,
398   // as it has no state. It only has coefficients.
399   {
400     Dsp::SimpleFilter <Dsp::RBJ::LowPass> f;
401     f.setup (44100, // sample rate Hz
402              440, // cutoff frequency Hz
403              1); // "Q" (resonance)
404 
405     std::ostringstream os;
406 
407     os << "a0 = " << f.getA0 () << "\n"
408        << "a1 = " << f.getA1 () << "\n"
409        << "a2 = " << f.getA2 () << "\n"
410        << "b0 = " << f.getB0 () << "\n"
411        << "b1 = " << f.getB1 () << "\n"
412        << "b2 = " << f.getB2 () << "\n";
413       ;
414 
415     std::cout << os.str();
416   }
417 
418   // Create an instance of a raw filter. This is as low as it gets, any
419   // lower and we will just have either a Biquad or a Cascade, and you'll
420   // be setting the coefficients manually.
421   {
422     // This is basically like eating uncooked food
423     Dsp::RBJ::LowPass f;
424     f.setup (44100, 440, 1);
425 
426     // calculate response at frequency 440 Hz
427     Dsp::complex_t response = f.response (440./44100);
428   }
429 
430   // Extract coefficients from a Cascade
431   {
432     Dsp::SimpleFilter <Dsp::Butterworth::HighPass <3> > f;
433     f.setup (3, 44100, 2000);
434 
435     std::ostringstream os;
436 
437     os << "numStages = " << f.getNumStages() << "\n"
438        << "a0[0] = " << f[0].getA0 () << "\n"
439        << "a1[0] = " << f[0].getA1 () << "\n"
440        << "a2[0] = " << f[0].getA2 () << "\n"
441        << "b0[0] = " << f[0].getB0 () << "\n"
442        << "b1[0] = " << f[0].getB1 () << "\n"
443        << "b2[0] = " << f[0].getB2 () << "\n"
444        << "a0[1] = " << f[1].getA0 () << "\n"
445        << "a1[1] = " << f[1].getA1 () << "\n"
446        << "a2[1] = " << f[1].getA2 () << "\n"
447        << "b0[1] = " << f[1].getB0 () << "\n"
448        << "b1[1] = " << f[1].getB1 () << "\n"
449        << "b2[1] = " << f[1].getB2 () << "\n"
450       ;
451 
452     std::cout << os.str();
453   }
454 }
455 
456 }
457