Use a 12dB/oct rolloff instead of 24 for the lowpass filter

This commit is contained in:
Chris Robinson
2008-09-13 02:46:14 -07:00
parent 26e8ea60a5
commit 6bfdb57a5b
3 changed files with 47 additions and 78 deletions
+10 -14
View File
@@ -157,7 +157,6 @@ __inline ALuint aluChannelsFromFormat(ALenum format)
static __inline ALfloat lpFilter(FILTER *iir, ALfloat input)
{
unsigned int i;
float *hist1_ptr,*hist2_ptr,*coef_ptr;
ALfloat output,new_hist,history1,history2;
@@ -170,22 +169,19 @@ static __inline ALfloat lpFilter(FILTER *iir, ALfloat input)
* or filter gain */
output = input * (*coef_ptr++);
for(i = 0;i < FILTER_SECTIONS;i++)
{
history1 = *hist1_ptr; /* history values */
history2 = *hist2_ptr;
history1 = *hist1_ptr; /* history values */
history2 = *hist2_ptr;
output = output - history1 * (*coef_ptr++);
new_hist = output - history2 * (*coef_ptr++); /* poles */
output = output - history1 * (*coef_ptr++);
new_hist = output - history2 * (*coef_ptr++); /* poles */
output = new_hist + history1 * (*coef_ptr++);
output = output + history2 * (*coef_ptr++); /* zeros */
output = new_hist + history1 * (*coef_ptr++);
output = output + history2 * (*coef_ptr++); /* zeros */
*hist2_ptr++ = *hist1_ptr;
*hist1_ptr++ = new_hist;
hist1_ptr++;
hist2_ptr++;
}
*hist2_ptr++ = *hist1_ptr;
*hist1_ptr++ = new_hist;
hist1_ptr++;
hist2_ptr++;
return output;
}
+35 -60
View File
@@ -49,8 +49,8 @@ static void szxform(
* InitLowPassFilter()
*
* Initialize filter coefficients.
* We create a 4th order filter (24 db/oct rolloff), consisting
* of two second order sections.
* We create a 2nd order filter (12 db/oct rolloff), consisting
* of one second order section.
* --------------------------------------------------------------------
*/
int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
@@ -58,13 +58,12 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
float *coef;
double fs, fc; /* Sampling frequency, cutoff frequency */
double Q; /* Resonance > 1.0 < 1000 */
unsigned nInd;
double a0, a1, a2, b0, b1, b2;
double k; /* overall gain factor */
struct {
double a0, a1, a2; /* numerator coefficients */
double b0, b1, b2; /* denominator coefficients */
} ProtoCoef[FILTER_SECTIONS]; /* Filter prototype coefficients,
} ProtoCoef; /* Filter prototype coefficients,
1 for each filter section */
@@ -72,20 +71,12 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
* Setup filter s-domain coefficients
*/
/* Section 1 */
ProtoCoef[0].a0 = 1.0;
ProtoCoef[0].a1 = 0;
ProtoCoef[0].a2 = 0;
ProtoCoef[0].b0 = 1.0;
ProtoCoef[0].b1 = 0.765367;
ProtoCoef[0].b2 = 1.0;
/* Section 2 */
ProtoCoef[1].a0 = 1.0;
ProtoCoef[1].a1 = 0;
ProtoCoef[1].a2 = 0;
ProtoCoef[1].b0 = 1.0;
ProtoCoef[1].b1 = 1.847759;
ProtoCoef[1].b2 = 1.0;
ProtoCoef.a0 = 1.0;
ProtoCoef.a1 = 0;
ProtoCoef.a2 = 0;
ProtoCoef.b0 = 1.0;
ProtoCoef.b1 = 1.4142;
ProtoCoef.b2 = 1.0;
/* Clear the coefficient and history arrays */
memset(iir->coef, 0, sizeof(iir->coef));
@@ -102,18 +93,14 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
* Compute z-domain coefficients for each biquad section
* for new Cutoff Frequency and Resonance
*/
for (nInd = 0; nInd < FILTER_SECTIONS; nInd++)
{
a0 = ProtoCoef[nInd].a0;
a1 = ProtoCoef[nInd].a1;
a2 = ProtoCoef[nInd].a2;
a0 = ProtoCoef.a0;
a1 = ProtoCoef.a1;
a2 = ProtoCoef.a2;
b0 = ProtoCoef[nInd].b0;
b1 = ProtoCoef[nInd].b1 / Q; /* Divide by resonance or Q */
b2 = ProtoCoef[nInd].b2;
szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
coef += 4; /* Point to next filter section */
}
b0 = ProtoCoef.b0;
b1 = ProtoCoef.b1 / Q; /* Divide by resonance or Q */
b2 = ProtoCoef.b2;
szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
/* Update overall filter gain in coef array */
iir->coef[0] = k;
@@ -134,8 +121,8 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
* as input to szxform() to convert them to z-domain.
*
* Here's the butterworth polinomials for 2nd, 4th and 6th order sections.
* When we construct a 24 db/oct filter, we take to 2nd order
* sections and compute the coefficients separately for each section.
* When we construct a 12 db/oct filter, we take a 2nd order
* section and compute the coefficients.
*
* n Polinomials
* --------------------------------------------------------------------
@@ -144,10 +131,9 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
* 6 (s^2 + 0.5176387s + 1) (s^2 + 1.414214 + 1) (s^2 + 1.931852s + 1)
*
* Where n is a filter order.
* For n=4, or two second order sections, we have following equasions for each
* 2nd order stage:
* For n=2, or one second order section, we have following equasion:
*
* (1 / (s^2 + (1/Q) * 0.765367s + 1)) * (1 / (s^2 + (1/Q) * 1.847759s + 1))
* (1 / (s^2 + (1/Q) * 1.4142s + 1))
*
* Where Q is filter quality factor in the range of
* 1 to 1000. The overall filter Q is a product of all
@@ -157,12 +143,7 @@ int InitLowPassFilter(ALCcontext *Context, FILTER *iir)
*
* The nominator part is just 1.
* The denominator coefficients for stage 1 of filter are:
* b2 = 1; b1 = 0.765367; b0 = 1;
* numerator is
* a2 = 0; a1 = 0; a0 = 1;
*
* The denominator coefficients for stage 1 of filter are:
* b2 = 1; b1 = 1.847759; b0 = 1;
* b2 = 1; b1 = 1.4142; b0 = 1;
* numerator is
* a2 = 0; a1 = 0; a0 = 1;
*
@@ -315,17 +296,15 @@ static void szxform(
How to construct a kewl low pass resonant filter?
Lets assume we want to create a filter for analog synth.
The filter rolloff is 24 db/oct, which corresponds to 4th
The filter rolloff is 12 db/oct, which corresponds to 2nd
order filter. Filter of first order is equivalent to RC circuit
and has max rolloff of 6 db/oct.
We will use classical Butterworth IIR filter design, as it
exactly corresponds to our requirements.
A common practice is to chain several 2nd order sections,
or biquads, as they commonly called, in order to achive a higher
order filter. Each 2nd order section is a 2nd order filter, which
has 12 db/oct roloff. So, we need 2 of those sections in series.
Each 2nd order section is a 2nd order filter, which has 12 db/oct
rolloff. So, we only need one section
To compute those sections, we use standard Butterworth polinomials,
or so called s-domain representation and convert it into z-domain,
@@ -354,14 +333,14 @@ of 2nd order sections:
* 4 (s^2 + 0.765367s + 1) * (s^2 + 1.847759s + 1)
* 6 (s^2 + 0.5176387s + 1) * (s^2 + 1.414214 + 1) * (s^2 + 1.931852s + 1)
*
* For n=4 we have following equasion for the filter transfer function:
* For n=2 we have following equasion for the filter transfer function:
*
* 1 1
* T(s) = --------------------------- * ----------------------------
* s^2 + (1/Q) * 0.765367s + 1 s^2 + (1/Q) * 1.847759s + 1
* 1
* T(s) = -------------------------
* s^2 + (1/Q) * 1.4142s + 1
*
The filter consists of two 2nd order secions since highest s power is 2.
The filter consists of one 2nd order section since highest s power is 2.
Now we can take the coefficients, or the numbers by which s is multiplied
and plug them into a standard formula to be used by bilinear transform.
@@ -376,17 +355,13 @@ which means s^2 = 0 and s^1 = 0
Lets convert standard butterworth polinomials into this form:
0 + 0 + 1 0 + 0 + 1
-------------------------- * --------------------------
1 + ((1/Q) * 0.765367) + 1 1 + ((1/Q) * 1.847759) + 1
0 + 0 + 1
------------------------
1 + ((1/Q) * 1.4142) + 1
Section 1:
a2 = 0; a1 = 0; a0 = 1;
b2 = 1; b1 = 0.5176387; b0 = 1;
Section 2:
a2 = 0; a1 = 0; a0 = 1;
b2 = 1; b1 = 1.847759; b0 = 1;
b2 = 1; b1 = 1.4142; b0 = 1;
That Q is filter quality factor or resonance, in the range of
1 to 1000. The overall filter Q is a product of all 2nd order stages.
@@ -408,7 +383,7 @@ coefficients and the new filter cutoff frequency or resonance.
You also need to supply the sampling rate and filter gain you want
to achive. For our purposes the gain = 1.
We call szxform() function 2 times becase we have 2 filter sections.
We call szxform() function 1 time becase we have 1 filter section.
Each call provides different coefficients.
The gain argument to szxform() is a pointer to desired filter
@@ -418,7 +393,7 @@ double k = 1.0; // overall gain factor
Upon return from each call, the k argument will be set to a value,
by which to multiply our actual signal in order for the gain
to be one. On second call to szxform() we provide k that was
to be one. On following calls to szxform() we provide k that was
changed by the previous section. During actual audio filtering
function iir_filter() will use this k
+2 -4
View File
@@ -7,11 +7,9 @@
extern "C" {
#endif
#define FILTER_SECTIONS 2 /* 2 filter sections for 24 db/oct filter */
typedef struct {
float history[2*FILTER_SECTIONS]; /* history in filter */
float coef[4*FILTER_SECTIONS + 1]; /* coefficients of filter */
float history[2]; /* history in filter */
float coef[4 + 1]; /* coefficients of filter */
} FILTER;
#define AL_FILTER_TYPE 0x8001