Implement "vicanek" matched biquad filters
This commit is contained in:
parent
6e3d3706dc
commit
5599e136c0
@ -220,7 +220,11 @@ namespace ARDOUR { namespace DSP {
|
|||||||
AllPass,
|
AllPass,
|
||||||
Peaking,
|
Peaking,
|
||||||
LowShelf,
|
LowShelf,
|
||||||
HighShelf
|
HighShelf,
|
||||||
|
MatchedLowPass,
|
||||||
|
MatchedHighPass,
|
||||||
|
MatchedBandPass0dB,
|
||||||
|
MatchedPeaking
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Instantiate Biquad Filter
|
/** Instantiate Biquad Filter
|
||||||
@ -260,6 +264,9 @@ namespace ARDOUR { namespace DSP {
|
|||||||
/** reset filter state */
|
/** reset filter state */
|
||||||
void reset () { _z1 = _z2 = 0.0; }
|
void reset () { _z1 = _z2 = 0.0; }
|
||||||
private:
|
private:
|
||||||
|
void set_vicanek_poles (const double W0, const double Q, const double A = 1.0);
|
||||||
|
void calc_vicanek (const double W0, double& A0, double& A1, double& A2, double& phi0, double& phi1, double& phi2);
|
||||||
|
|
||||||
double _rate;
|
double _rate;
|
||||||
float _z1, _z2;
|
float _z1, _z2;
|
||||||
double _a1, _a2;
|
double _a1, _a2;
|
||||||
|
@ -206,6 +206,35 @@ Biquad::configure (Biquad const& other)
|
|||||||
_b2 = other._b2;
|
_b2 = other._b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Biquad::set_vicanek_poles (const double W0, const double Q, const double A)
|
||||||
|
{
|
||||||
|
/* https://www.vicanek.de/articles/BiquadFits.pdf */
|
||||||
|
const double Q2 = Q * Q;
|
||||||
|
const double AA = A * A;
|
||||||
|
const double p = 1. / (4 * AA * Q2);
|
||||||
|
|
||||||
|
_a2 = exp (-.5 * W0 / (A * Q));
|
||||||
|
_a1 = p <= 1.
|
||||||
|
? -2 * _a2 * cos (W0 * sqrt (1 - p))
|
||||||
|
: -2 * _a2 * cosh (W0 * sqrt (p - 1));
|
||||||
|
_a2 = _a2 * _a2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Biquad::calc_vicanek (const double W0, double& A0, double& A1, double& A2, double& phi0, double& phi1, double& phi2)
|
||||||
|
{
|
||||||
|
#define SQR(x) ((x) * (x))
|
||||||
|
A0 = SQR(1. + _a1 + _a2);
|
||||||
|
A1 = SQR(1. - _a1 + _a2);
|
||||||
|
A2 = -4 * _a2;
|
||||||
|
|
||||||
|
phi1 = SQR(sin (.5 * W0));
|
||||||
|
phi0 = 1.0 - phi1;
|
||||||
|
phi2 = 4 * phi0 * phi1;
|
||||||
|
#undef SQR
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Biquad::compute (Type type, double freq, double Q, double gain)
|
Biquad::compute (Type type, double freq, double Q, double gain)
|
||||||
{
|
{
|
||||||
@ -219,12 +248,15 @@ Biquad::compute (Type type, double freq, double Q, double gain)
|
|||||||
*/
|
*/
|
||||||
const double A = pow (10.0, (gain / 40.0));
|
const double A = pow (10.0, (gain / 40.0));
|
||||||
const double W0 = (2.0 * M_PI * freq) / _rate;
|
const double W0 = (2.0 * M_PI * freq) / _rate;
|
||||||
|
|
||||||
const double sinW0 = sin (W0);
|
const double sinW0 = sin (W0);
|
||||||
const double cosW0 = cos (W0);
|
const double cosW0 = cos (W0);
|
||||||
const double alpha = sinW0 / (2.0 * Q);
|
const double alpha = sinW0 / (2.0 * Q);
|
||||||
const double beta = sqrt (A) / Q;
|
const double beta = sqrt (A) / Q;
|
||||||
|
|
||||||
double _a0;
|
double _a0;
|
||||||
|
double A0, A1, A2;
|
||||||
|
double phi0, phi1, phi2;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LowPass:
|
case LowPass:
|
||||||
@ -307,6 +339,70 @@ Biquad::compute (Type type, double freq, double Q, double gain)
|
|||||||
_a1 = 2.0 * ((A - 1) - ((A + 1) * cosW0));
|
_a1 = 2.0 * ((A - 1) - ((A + 1) * cosW0));
|
||||||
_a2 = (A + 1) - ((A - 1) * cosW0) - (beta * sinW0);
|
_a2 = (A + 1) - ((A - 1) * cosW0) - (beta * sinW0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MatchedHighPass:
|
||||||
|
_a0 = 1.0;
|
||||||
|
set_vicanek_poles (W0, Q);
|
||||||
|
calc_vicanek (W0, A0, A1, A2, phi0, phi1, phi2);
|
||||||
|
|
||||||
|
_b0 = sqrt (A0 * phi0 + A1 * phi1 + A2 * phi2) / (4 * phi1) * Q;
|
||||||
|
_b1 = -2 * _b0;
|
||||||
|
_b2 = _b0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MatchedLowPass:
|
||||||
|
_a0 = 1.0;
|
||||||
|
set_vicanek_poles (W0, Q);
|
||||||
|
calc_vicanek (W0, A0, A1, A2, phi0, phi1, phi2);
|
||||||
|
{
|
||||||
|
const double B0_2 = 1 + _a1 + _a2; // = sqrt (B0)
|
||||||
|
const double B1 = ((A0 * phi0 + A1 * phi1 + A2 * phi2) * Q * Q - A0 * phi0) / phi1;
|
||||||
|
|
||||||
|
_b0 = .5 * (B0_2 + sqrt (B1));
|
||||||
|
_b1 = B0_2 -_b0;
|
||||||
|
_b2 = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MatchedBandPass0dB: /* Constant 0 dB peak gain */
|
||||||
|
_a0 = 1.0;
|
||||||
|
set_vicanek_poles (W0, Q);
|
||||||
|
{
|
||||||
|
float fq = 2 * freq / _rate;
|
||||||
|
float fq2 = fq * fq;
|
||||||
|
|
||||||
|
_b1 = -.5 * (1 - _a1 + _a2) * fq / Q / sqrt ((1 - fq2) * (1 - fq2) + fq2 / (Q * Q));
|
||||||
|
_b0 = .5 * ((1 + _a1 + _a2) / (W0 * Q) - _b1);
|
||||||
|
_b2 = -_b0 - _b1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MatchedPeaking:
|
||||||
|
_a0 = 1.0;
|
||||||
|
set_vicanek_poles (W0, Q, A);
|
||||||
|
calc_vicanek (W0, A0, A1, A2, phi0, phi1, phi2);
|
||||||
|
{
|
||||||
|
const double AA = A * A;
|
||||||
|
const double AAAA = AA * AA;
|
||||||
|
|
||||||
|
const double R1 = (phi0 * A0 + phi1 * A1 + phi2 * A2) * AAAA;
|
||||||
|
const double R2 = (A1 - A0 + 4 * (phi0 - phi1) * A2) * AAAA;
|
||||||
|
|
||||||
|
const double B0 = A0;
|
||||||
|
const double B2 = (R1 - phi1 * R2 - B0) / (4 * phi1 * phi1);
|
||||||
|
const double B1 = R2 + B0 + 4 * (phi1 - phi0) * B2;
|
||||||
|
|
||||||
|
const double B0_2 = 1 + _a1 + _a2; // = sqrt (B0)
|
||||||
|
|
||||||
|
_b1 = .5 * (B0_2 - sqrt (B1));
|
||||||
|
|
||||||
|
const double w = B0_2 - _b1;
|
||||||
|
|
||||||
|
_b0 = .5 * (w + sqrt (w * w + B2));
|
||||||
|
_b2 = -B2 / (4 * _b0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
abort(); /*NOTREACHED*/
|
abort(); /*NOTREACHED*/
|
||||||
break;
|
break;
|
||||||
|
@ -3082,6 +3082,10 @@ LuaBindings::common (lua_State* L)
|
|||||||
.addConst ("Peaking", ARDOUR::DSP::Biquad::Peaking)
|
.addConst ("Peaking", ARDOUR::DSP::Biquad::Peaking)
|
||||||
.addConst ("LowShelf", ARDOUR::DSP::Biquad::LowShelf)
|
.addConst ("LowShelf", ARDOUR::DSP::Biquad::LowShelf)
|
||||||
.addConst ("HighShelf", ARDOUR::DSP::Biquad::HighShelf)
|
.addConst ("HighShelf", ARDOUR::DSP::Biquad::HighShelf)
|
||||||
|
.addConst ("MatchedLowPass", ARDOUR::DSP::Biquad::MatchedLowPass)
|
||||||
|
.addConst ("MatchedHighPass", ARDOUR::DSP::Biquad::MatchedHighPass)
|
||||||
|
.addConst ("MatchedBandPass0dB", ARDOUR::DSP::Biquad::MatchedBandPass0dB)
|
||||||
|
.addConst ("MatchedPeaking", ARDOUR::DSP::Biquad::MatchedPeaking)
|
||||||
.endNamespace ()
|
.endNamespace ()
|
||||||
|
|
||||||
.beginNamespace ("NoiseType")
|
.beginNamespace ("NoiseType")
|
||||||
|
Loading…
Reference in New Issue
Block a user