From 6bfdb57a5b833a17fba867b11f800bba211674b8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Sep 2008 02:46:14 -0700 Subject: [PATCH] Use a 12dB/oct rolloff instead of 24 for the lowpass filter --- Alc/ALu.c | 24 +++++------ Alc/lpfilter.c | 97 +++++++++++++++++---------------------------- OpenAL32/Include/alFilter.h | 6 +-- 3 files changed, 48 insertions(+), 79 deletions(-) diff --git a/Alc/ALu.c b/Alc/ALu.c index 61e11bbf..37d30851 100644 --- a/Alc/ALu.c +++ b/Alc/ALu.c @@ -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; } diff --git a/Alc/lpfilter.c b/Alc/lpfilter.c index 9f65cf3b..98019e2e 100644 --- a/Alc/lpfilter.c +++ b/Alc/lpfilter.c @@ -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; - - 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 */ - } + a0 = ProtoCoef.a0; + a1 = ProtoCoef.a1; + a2 = ProtoCoef.a2; + + 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 diff --git a/OpenAL32/Include/alFilter.h b/OpenAL32/Include/alFilter.h index e5e70a06..83912cfa 100644 --- a/OpenAL32/Include/alFilter.h +++ b/OpenAL32/Include/alFilter.h @@ -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 -- 2.11.4.GIT