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