Main Archive Specials Wiki | FAQ Links Submit Forum


Butterworth Optimized C++ Class

Type : 24db Resonant Lowpass
References : Posted by neotec

Notes :
This ist exactly the same as posted by "Zxform" (filters004.txt). The only difference is, that this version is an optimized one.

Parameters:
Cutoff [0.f -> Nyquist.f]
Resonance [0.f -> 1.f]

There are some minima and maxima defined, to make ist sound nice in all situations. This class is part of some of my VST Plugins, and works well and executes fast.


Code :
// FilterButterworth24db.h

#pragma once

class CFilterButterworth24db
{
public:
CFilterButterworth24db(void);
~CFilterButterworth24db(void);
void SetSampleRate(float fs);
void Set(float cutoff, float q);
float Run(float input);

private:
float t0, t1, t2, t3;
float coef0, coef1, coef2, coef3;
float history1, history2, history3, history4;
float gain;
float min_cutoff, max_cutoff;
};

// FilterButterworth24db.cpp

#include

#define BUDDA_Q_SCALE 6.f

#include "FilterButterworth24db.h"

CFilterButterworth24db::CFilterButterworth24db(void)
{
this->history1 = 0.f;
this->history2 = 0.f;
this->history3 = 0.f;
this->history4 = 0.f;

this->SetSampleRate(44100.f);
this->Set(22050.f, 0.0);
}

CFilterButterworth24db::~CFilterButterworth24db(void)
{
}

void CFilterButterworth24db::SetSampleRate(float fs)
{
float pi = 4.f * atanf(1.f);

this->t0 = 4.f * fs * fs;
this->t1 = 8.f * fs * fs;
this->t2 = 2.f * fs;
this->t3 = pi / fs;

this->min_cutoff = fs * 0.01f;
this->max_cutoff = fs * 0.45f;
}

void CFilterButterworth24db::Set(float cutoff, float q)
{
if (cutoff < this->min_cutoff)
cutoff = this->min_cutoff;
else if(cutoff > this->max_cutoff)
cutoff = this->max_cutoff;

if(q < 0.f)
q = 0.f;
else if(q > 1.f)
q = 1.f;

float wp = this->t2 * tanf(this->t3 * cutoff);
float bd, bd_tmp, b1, b2;

q *= BUDDA_Q_SCALE;
q += 1.f;

b1 = (0.765367f / q) / wp;
b2 = 1.f / (wp * wp);

bd_tmp = this->t0 * b2 + 1.f;

bd = 1.f / (bd_tmp + this->t2 * b1);

this->gain = bd * 0.5f;

this->coef2 = (2.f - this->t1 * b2);

this->coef0 = this->coef2 * bd;
this->coef1 = (bd_tmp - this->t2 * b1) * bd;

b1 = (1.847759f / q) / wp;

bd = 1.f / (bd_tmp + this->t2 * b1);

this->gain *= bd;
this->coef2 *= bd;
this->coef3 = (bd_tmp - this->t2 * b1) * bd;
}

float CFilterButterworth24db::Run(float input)
{
float output = input * this->gain;
float new_hist;

output -= this->history1 * this->coef0;
new_hist = output - this->history2 * this->coef1;

output = new_hist + this->history1 * 2.f;
output += this->history2;

this->history2 = this->history1;
this->history1 = new_hist;

output -= this->history3 * this->coef2;
new_hist = output - this->history4 * this->coef3;

output = new_hist + this->history3 * 2.f;
output += this->history4;

this->history4 = this->history3;
this->history3 = new_hist;

return output;
}



Comments


Added on : 22/01/07 by bob[ AT ]yahoo[ DOT ]com
Comment :
This sounds really nice, especially with resonance. Although it becomes unstable below 4K (at 44100 s/r), which explains why the min_cutoff value has been set quite high. Would using doubles help stabilise it?
Also, I can't figure out how to get a high pass out of this, can anybody help?
Cheers.




Added on : 22/01/07 by neotec
Comment :
I have checked the peak output of this filter and especially for low frequences ... there is a simple fix, which makes it sound better with low frequences: change the line in Set(...) that reads 'this->gain = bd * 0.5f;' to 'this->gain = bd;'




Added on : 22/01/07 by bob[ AT ]yahoo[ DOT ]com
Comment :
Thanks for the quick reply. I've tried your change and it's made a slight tonal difference here, but the tests were not particularly scientific. I've discovered more detail in the problem, and it's one that has been commented on with other filters: If I sweep the filter quickly up or down the low frequencies it blows out really badly, even with zero Q. I'm new to filter math, so excuse my ignorance if this is a common thing with Butterworth.





Added on : 23/01/07 by neotec
Comment :
Yep ... this filter reacts very extreme on fast cutoff changes. I've added a function to my VST Synthesizer, which 'fades' the cutoff value from actual value to the desired one in about 0.05 seconds. My modulation envelopes do have similar restrictions concerning speed.



Added on : 23/01/07 by neotec
Comment :
If you want to know how this filter sounds, visit the kvraudio forum, and search here: "KVR Forum » Instruments" for "Cetone VST Plugins".



Added on : 29/01/07 by nobody[ AT ]nowhere[ DOT ]com
Comment :
I'm wondering about that tanh in the "Set."

Could replace with a pade appromimation, maybe. What is the range of inputs going into it?

In other words, how small and big does this get?...

this->t3 * cutoff




Added on : 29/01/07 by toast[ AT ]somewhereyoucantfind[ DOT ]com
Comment :
Possible small optimization. It depends on how smart your compiler is, but sections like this...

    output = new_hist + this->history3 * 2.f;
    output += this->history4;

can be changed to this to change the multiply to an addition:

output = this->history3;
output += output+new_Hist+this->history4;




Added on : 29/01/07 by toast[ AT ]somewhereyoucantfind[ DOT ]com
Comment :
While I'm at it, one of these divisions can easily be switched to a multiply...

b1 = (1.847759f / q) / wp;

b1=(1.847759f/(q*wp);




Added on : 04/02/07 by bob[ AT ]yahoo[ DOT ]com
Comment :
Four times oversampling removes the problems with fast cut-off sweeps at low values.
This filter has the same shape as a normal biquad filter, with a more pronounced resonance boost.




Added on : 12/01/08 by musicdsp[ DOT ]org[ AT ]mindcontrol[ DOT ]org
Comment :
Why would oversampling solve the problem? If you over-sample, the poles have to reach even further into the relative frequencies, and stability would become more of a problem AFAICT.



Added on : 21/01/08 by bob[ AT ]yahoo[ DOT ]com
Comment :
It just seems to. If you 4X over-sample, then it gives it a 4X chance to recover from each sweep change, presuming you're not changing the filter cut-off at 4X also.




Added on : 25/01/08 by musicdsp[ AT ]dsparsons[ DOT ]co[ DOT ]uk[ DOT ]nowhere
Comment :
thing is with 4X oversampling on this is that you'll be reducing precision on omega (wp here), and so should probably shift to double rather than float to help accuracy.



Added on : 26/06/12 by froese[ AT ]gemini-sites[ DOT ]de
Comment :
hi,
i tried to use this with samples with sampleRate 2000 and values in range +-2^15, cutoff frequency at 500Hz. is it correct to normalize the input to have values 0.0f -> 1.0f?
i get only chaotic values in return, is this only suitable for audio signals?
thanx!




Added on : 13/07/12 by froese[ AT ]gemini-sites[ DOT ]de
Comment :
forget my previous comment, it works fine. thanx!              



Added on : 23/04/13 by scott[ AT ]mail[ DOT ]com
Comment :
Did anyone figure out how to get a high pass out of this?

Thanks!




Add your own comment
Comments are displayed in fixed width, no HTML code allowed!
Email:

Comment:

Are you human?



Site created and maintained by Bram
Graphic design by line.out | Server sponsered by fxpansion