Collection of Useful C++ Classes for DSP


#1

A while ago I wrote some C++ classes for generating IIR (infinite impulse response) filters, with an associated demo project written in Juce to show how the filters are used. Not sure if I posted it before but here it is:

“A Collection of Useful C++ Classes for Digital Signal Processing”

Biquads, plus N-order Butterworth, Chebyshev, and Elliptic curve filters, plus some utilities. It’s under the MIT license, so unlimited commercial usage is encouraged. Here’s the demo screenshot:

[attachment=0]dspfiltersdemo.png[/attachment]


#2

Cool! Thanks for sharing!


#3

Awesome, thanks. Could I build a multi-band compressor with those components? Or are they all just bandpass filters or different types?

Bruce


#4

Well there are three parts to this library. One is a set of utilities for manipulating audio buffers. Two is a full set of biquad filters, the ones from RBJ: Low Pass, High Pass, Band Pass (constant skirt), Band Pass (constant peak), Band Stop, All Pass, Low Shelf, High Shelf, and Peak/Notch.

The third set of classes is the most interesting. There are prototype classes for each of the common curves: Butterworth, Chebyshev I, Chebyshev II, Elliptic, and Bessel. Then there are a set of pole/zero transformation classes that can take any prototype and turn it into a filter with given specifications. The transformations are: Low Pass, High Pass, Band Pass, and Band Stop. Any filter prototype can be realized by transforming a prototype using a supplied transformation class. To implement the shelving filters, each prototype also comes with a shelving version. Using a single template declaration you can produce pretty much any kind of filter. The filters provided are:

BiquadLowPass, BiquadHighPass, BiquadBandPass1, BiquadBandPass2, BiquadBandStop BiquadAllPass, BiquadLowShelf, BiquadHighShelf, BiquadPeak, ButterLowPass, ButterHighPass, ButterBandPass, ButterBandStop ButterLowShelf, ButterHighShelf, ButterPeak, ChebyILowPass, ChebyIHighPass, ChebyIBandPass, ChebyIBandStop ChebyILowShelf, ChebyIHighShelf, ChebyIPeak, ChebyIILowPass, ChebyIIHighPass, ChebyIIBandPass, ChebyIIBandStop ChebyIILowShelf, ChebyIIHighShelf, ChebyIIPeak, EllipticLowPass, EllipticHighPass, EllipticBandPass, EllipticBandStop, BesselLowPass, BesselHighPass, BesselBandPass, BesselBandStop

So to answer your question, yes you can build a a multiband compressor.


#5

Cool :mrgreen:
would be great to have them inside Juces IIRFilter, so you can use them inside a IIRFilterAudioSource :smiley:


#6

Oh, cool, I’ve been playing around a bit with your classes some months ago.
I didn’t notice the project owner was you!
First I had to adapt them to the git’s tip, but they are really nice!
I’m planning of building a parametric eq with them sooner or later.


#7

Just wanted to point out that this library has been rewritten and the demo upgraded. It uses the latest tip.

[quote=“chkn”]Cool :mrgreen:
would be great to have them inside Juces IIRFilter, so you can use them inside a IIRFilterAudioSource :D[/quote]

Did it, and more:

http://code.google.com/p/dspfilterscpp/source/browse/trunk/demo/FilteringAudioSource.h

You can change the filter parameters during playback and it will smooth it out (if you use SmoothedFilterDesign).


#8

Great stuff! Thanks a lot! One thing I encounter when experimenting with it: what about default parameter values for a filter? When I initiate a new BandPassFilter, for instance, and don’t explicitly set the bandwidth, it will be something like 1.35e+056… or am I overlooking something?


#9

There are no defaults, you have to initialize each filter parameter


#10

Ok, got it all figured out. The only thing that seems a little odd is that the bandshelve filters should show symmetrical response when plotted on a log frequency scale, but they are not even symmetrical when plotted on a linear scale (e.g. with a center frequency of 1000 Hz the falloff on the “right” side would start at 1500 Hz and on the “left” side at less than 500 Hz…). Just trying to figure out from the included papers what’s going on there…


#11

Well all of the bandpass, bandstop, band shelf filters use the same technique, which is to use the bandpass transformation to go from the analog prototype to the digital filter. So its all the same for elliptic, butterworth, etc…


#12

Ok… I think the issue can best be seen with a 1-order bandshelf. Increasing the bandwidth will cause the peak to move towards lower frequencies…


#13

With this little parameter transformation, all the Bandpass/Stop/Shelve filters are symmetrical in logarithmic frequency space for a given center frequency f0 and width Q:

params[0]=sample_rate; params[1]=2; //Order... N=log(sqr(1/(2*Q)+sqrt(1/sqr(2*Q)+1)))/log(2.); f1=f0/sqrt(pow(2,N)); f2=f1*pow(2,N); params[2]=(f1+f2)/2; //"fake" center frequency params[VinnFilter[i]->findParamId(Dsp::ParamID::idGain)]=dBgain; params[VinnFilter[i]->findParamId(Dsp::ParamID::idBandwidthHz)]=f2-f1;


#14

Hey that’s pretty nifty! You should be able to encapsulate that logic into its own class so it sits along side the existing code, without too much trouble, and it can be used the same way as all the other objects.

If you could be so kind as to share your formula with the KVR folks here, that would be super!

http://www.kvraudio.com/forum/viewtopic.php?t=249926&sid=b748bded760b53c19918436c66bae2f7


#15

Hi

I’m trying to implement a Time-varying EQ (The filters parameter value will be automatically calculated based on each input block; more like an intelligent equalizer). I have some problems/questions:

  1. Somewhere in one forum here in this website I read someone said that we should use individual filter for each channel and we cannot use one filter for all channel. Is that right?

  2. In my first version, I used to declare, create and initialize filters outside the processBlock function and used to set parameters and call the filters inside the processBlock. for some unknown reasons I didn’t work. Now I create new filters inside the processBlock and destroy it at the end of processBlock. The only problem I have is the ticks that sounds like distortion after filtering, although i have no boosting and just cut some frequencies. I’m sure that’s because the filter parameters changes so fast (every new buffer in my case 256 samples) as when I pass constant parameters to the filter it works fine but sounds distorted when my parameters alter. Any solution for Time varying filtering when parameters changes so fast (even faster than a user quickly changing EQ knob manually)?

Thanks!


#16

[BUG]

 

The RBJ Biquad  High Shelf Filter, the Frequency-Paramter behaves antiproportional to than it should!


#17

Yes, I reported this to Vinn several months ago. Patch to shared\DSPfilters\source\RBJ.cpp is as follows:

 


old    new    
...    ...    @@ -156,12 +156,12 @@ void HighShelf::setup (double sampleRate,
156    156       double sn = sin (w0);
157    157       double AL = sn / 2 * ::std::sqrt ((A + 1/A) * (1/shelfSlope - 1) + 2);
158    158       double sq = 2 * sqrt(A) * AL;
159        -  double b0 =    A*( (A+1) - (A-1)*cs + sq );
160        -  double b1 = -2*A*( (A-1) - (A+1)*cs );
161        -  double b2 =    A*( (A+1) - (A-1)*cs - sq );
162        -  double a0 =        (A+1) + (A-1)*cs + sq;
163        -  double a1 =    2*( (A-1) + (A+1)*cs );
164        -  double a2 =        (A+1) + (A-1)*cs - sq;
    159    +  double b0 =    A*( (A+1) + (A-1)*cs + sq );
    160    +  double b1 = -2*A*( (A-1) + (A+1)*cs );
    161    +  double b2 =    A*( (A+1) + (A-1)*cs - sq );
    162    +  double a0 =        (A+1) - (A-1)*cs + sq;
    163    +  double a1 =    2*( (A-1) - (A+1)*cs );
    164    +  double a2 =        (A+1) - (A-1)*cs - sq;
165    165       setCoefficients (a0, a1, a2, b0, b1, b2);
166    166     }
167    167     


#18

BIG Thanks for fixing this :)