|
#include "Biquad.h"
|
#define _USE_MATH_DEFINES
|
#include "math.h"
|
|
namespace ReverbHallRoom
|
{
|
Biquad::Biquad()
|
{
|
ClearBuffers();
|
}
|
|
Biquad::Biquad(FilterType filterType, float fs)
|
{
|
Type = filterType;
|
SetSamplerate(fs);
|
|
SetGainDb(0.0f);
|
Frequency = (float)(fs * 0.25f);
|
SetQ(0.5f);
|
ClearBuffers();
|
}
|
|
Biquad::~Biquad()
|
{
|
|
}
|
|
|
float Biquad::GetSamplerate()
|
{
|
return fs;
|
}
|
|
void Biquad::SetSamplerate(float fs)
|
{
|
this->fs = fs;
|
fsInv = 1.0f / fs;
|
Update();
|
}
|
|
float Biquad::GetGainDb()
|
{
|
return gainDB;
|
}
|
|
float Biquad::GetGain()
|
{
|
return gain;
|
}
|
|
void Biquad::SetGainDb (float value)
|
{
|
// Clamp value between -60 and 60
|
if (value < -60)
|
value = -60;
|
else if (value > 60)
|
value = 60;
|
|
gainDB = value;
|
gain = powf (10.0f, value / 20.0f);
|
}
|
|
void Biquad::SetGain (float value)
|
{
|
if (value < 0.001f)
|
value = 0.001f; // -60dB
|
else if (value > 1000.0f)
|
value = 1000.0f; // 60dB
|
|
gain = value;
|
gainDB = log10f (gain) * 20;
|
}
|
|
float Biquad::GetQ()
|
{
|
return q;
|
}
|
|
void Biquad::SetQ(float value)
|
{
|
if (value < 0.001f)
|
value = 0.001f;
|
q = value;
|
}
|
|
// this is the newer set of formulas from http://www.earlevel.com/main/2011/01/02/biquad-formulas/
|
// Note that for shelf and peak filters, I had to invert the if/else statements for boost and cut, as
|
// I was getting the inverse desired effect, very odd...
|
void Biquad::Update()
|
{
|
auto Fc = Frequency;
|
//auto Fs = fs;
|
|
auto V = powf(10, fabsf(gainDB) / 20.0f);
|
//auto K = tanf(M_PI * Fc / Fs);
|
auto K = tanf(M_PI * Fc * fsInv);
|
auto Q = q;
|
double norm = 1.0;
|
|
switch (Type)
|
{
|
case FilterType::LowPass6db:
|
a1 = -expf(-2.0 * M_PI * (Fc * fsInv));
|
b0 = 1.0 + a1;
|
b1 = b2 = a2 = 0;
|
break;
|
case FilterType::HighPass6db:
|
a1 = -expf(-2.0 * M_PI * (Fc * fsInv));
|
b0 = a1;
|
b1 = -a1;
|
b2 = a2 = 0;
|
break;
|
case FilterType::LowPass:
|
norm = 1 / (1 + K / Q + K * K);
|
b0 = K * K * norm;
|
b1 = 2 * b0;
|
b2 = b0;
|
a1 = 2 * (K * K - 1) * norm;
|
a2 = (1 - K / Q + K * K) * norm;
|
break;
|
case FilterType::HighPass:
|
norm = 1 / (1 + K / Q + K * K);
|
b0 = 1 * norm;
|
b1 = -2 * b0;
|
b2 = b0;
|
a1 = 2 * (K * K - 1) * norm;
|
a2 = (1 - K / Q + K * K) * norm;
|
break;
|
case FilterType::BandPass:
|
norm = 1 / (1 + K / Q + K * K);
|
b0 = K / Q * norm;
|
b1 = 0;
|
b2 = -b0;
|
a1 = 2 * (K * K - 1) * norm;
|
a2 = (1 - K / Q + K * K) * norm;
|
break;
|
case FilterType::Notch:
|
norm = 1 / (1 + K / Q + K * K);
|
b0 = (1 + K * K) * norm;
|
b1 = 2 * (K * K - 1) * norm;
|
b2 = b0;
|
a1 = b1;
|
a2 = (1 - K / Q + K * K) * norm;
|
break;
|
case FilterType::Peak:
|
if (gainDB >= 0)
|
{
|
norm = 1 / (1 + 1 / Q * K + K * K);
|
b0 = (1 + V / Q * K + K * K) * norm;
|
b1 = 2 * (K * K - 1) * norm;
|
b2 = (1 - V / Q * K + K * K) * norm;
|
a1 = b1;
|
a2 = (1 - 1 / Q * K + K * K) * norm;
|
}
|
else
|
{
|
norm = 1 / (1 + V / Q * K + K * K);
|
b0 = (1 + 1 / Q * K + K * K) * norm;
|
b1 = 2 * (K * K - 1) * norm;
|
b2 = (1 - 1 / Q * K + K * K) * norm;
|
a1 = b1;
|
a2 = (1 - V / Q * K + K * K) * norm;
|
}
|
break;
|
case FilterType::LowShelf:
|
if (gainDB >= 0)
|
{
|
norm = 1 / (1 + sqrtf(2) * K + K * K);
|
b0 = (1 + sqrtf(2 * V) * K + V * K * K) * norm;
|
b1 = 2 * (V * K * K - 1) * norm;
|
b2 = (1 - sqrtf(2 * V) * K + V * K * K) * norm;
|
a1 = 2 * (K * K - 1) * norm;
|
a2 = (1 - sqrtf(2) * K + K * K) * norm;
|
}
|
else
|
{
|
norm = 1 / (1 + sqrtf(2 * V) * K + V * K * K);
|
b0 = (1 + sqrtf(2) * K + K * K) * norm;
|
b1 = 2 * (K * K - 1) * norm;
|
b2 = (1 - sqrtf(2) * K + K * K) * norm;
|
a1 = 2 * (V * K * K - 1) * norm;
|
a2 = (1 - sqrtf(2 * V) * K + V * K * K) * norm;
|
}
|
break;
|
case FilterType::HighShelf:
|
if (gainDB >= 0)
|
{
|
norm = 1 / (1 + sqrtf(2) * K + K * K);
|
b0 = (V + sqrtf(2 * V) * K + K * K) * norm;
|
b1 = 2 * (K * K - V) * norm;
|
b2 = (V - sqrtf(2 * V) * K + K * K) * norm;
|
a1 = 2 * (K * K - 1) * norm;
|
a2 = (1 - sqrtf(2) * K + K * K) * norm;
|
}
|
else
|
{
|
norm = 1 / (V + sqrtf(2 * V) * K + K * K);
|
b0 = (1 + sqrtf(2) * K + K * K) * norm;
|
b1 = 2 * (K * K - 1) * norm;
|
b2 = (1 - sqrtf(2) * K + K * K) * norm;
|
a1 = 2 * (K * K - V) * norm;
|
a2 = (V - sqrtf(2 * V) * K + K * K) * norm;
|
}
|
break;
|
}
|
}
|
|
double Biquad::GetResponse(float freq) const
|
{
|
double phi = powf((sinf(2 * M_PI * freq / (2.0 * fs))), 2);
|
double y = ((powf(b0 + b1 + b2, 2.0) - 4.0 * (b0 * b1 + 4.0 * b0 * b2 + b1 * b2) * phi + 16.0 * b0 * b2 * phi * phi) / (powf(1.0 + a1 + a2, 2.0) - 4.0 * (a1 + 4.0 * a2 + a1 * a2) * phi + 16.0 * a2 * phi * phi));
|
// y gives you power gain, not voltage gain, and this a 10 * log_10(g) formula instead of 20 * log_10(g)
|
// by taking the sqrt we get a value that's more suitable for signal processing, i.e. the voltage gain
|
return sqrtf(y);
|
}
|
|
void Biquad::ClearBuffers()
|
{
|
y = 0;
|
x2 = 0;
|
y2 = 0;
|
x1 = 0;
|
y1 = 0;
|
}
|
}
|