Initial import
git-svn-id: http://moon:8086/svn/software/trunk/libsrc/iir@1 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
Executable
+280
@@ -0,0 +1,280 @@
|
|||||||
|
|
||||||
|
Cookbook formulae for audio EQ biquad filter coefficients
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
by Robert Bristow-Johnson <rbj@audioimagination.com>
|
||||||
|
|
||||||
|
|
||||||
|
All filter transfer functions were derived from analog prototypes (that
|
||||||
|
are shown below for each EQ filter type) and had been digitized using the
|
||||||
|
Bilinear Transform. BLT frequency warping has been taken into account for
|
||||||
|
both significant frequency relocation (this is the normal "prewarping" that
|
||||||
|
is necessary when using the BLT) and for bandwidth readjustment (since the
|
||||||
|
bandwidth is compressed when mapped from analog to digital using the BLT).
|
||||||
|
|
||||||
|
First, given a biquad transfer function defined as:
|
||||||
|
|
||||||
|
b0 + b1*z^-1 + b2*z^-2
|
||||||
|
H(z) = ------------------------ (Eq 1)
|
||||||
|
a0 + a1*z^-1 + a2*z^-2
|
||||||
|
|
||||||
|
This shows 6 coefficients instead of 5 so, depending on your architechture,
|
||||||
|
you will likely normalize a0 to be 1 and perhaps also b0 to 1 (and collect
|
||||||
|
that into an overall gain coefficient). Then your transfer function would
|
||||||
|
look like:
|
||||||
|
|
||||||
|
(b0/a0) + (b1/a0)*z^-1 + (b2/a0)*z^-2
|
||||||
|
H(z) = --------------------------------------- (Eq 2)
|
||||||
|
1 + (a1/a0)*z^-1 + (a2/a0)*z^-2
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
1 + (b1/b0)*z^-1 + (b2/b0)*z^-2
|
||||||
|
H(z) = (b0/a0) * --------------------------------- (Eq 3)
|
||||||
|
1 + (a1/a0)*z^-1 + (a2/a0)*z^-2
|
||||||
|
|
||||||
|
|
||||||
|
The most straight forward implementation would be the "Direct Form 1"
|
||||||
|
(Eq 2):
|
||||||
|
|
||||||
|
y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
|
||||||
|
- (a1/a0)*y[n-1] - (a2/a0)*y[n-2] (Eq 4)
|
||||||
|
|
||||||
|
This is probably both the best and the easiest method to implement in the
|
||||||
|
56K and other fixed-point or floating-point architechtures with a double
|
||||||
|
wide accumulator.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Begin with these user defined parameters:
|
||||||
|
|
||||||
|
Fs (the sampling frequency)
|
||||||
|
|
||||||
|
f0 ("wherever it's happenin', man." Center Frequency or
|
||||||
|
Corner Frequency, or shelf midpoint frequency, depending
|
||||||
|
on which filter type. The "significant frequency".)
|
||||||
|
|
||||||
|
dBgain (used only for peaking and shelving filters)
|
||||||
|
|
||||||
|
Q (the EE kind of definition, except for peakingEQ in which A*Q is
|
||||||
|
the classic EE Q. That adjustment in definition was made so that
|
||||||
|
a boost of N dB followed by a cut of N dB for identical Q and
|
||||||
|
f0/Fs results in a precisely flat unity gain filter or "wire".)
|
||||||
|
|
||||||
|
_or_ BW, the bandwidth in octaves (between -3 dB frequencies for BPF
|
||||||
|
and notch or between midpoint (dBgain/2) gain frequencies for
|
||||||
|
peaking EQ)
|
||||||
|
|
||||||
|
_or_ S, a "shelf slope" parameter (for shelving EQ only). When S = 1,
|
||||||
|
the shelf slope is as steep as it can be and remain monotonically
|
||||||
|
increasing or decreasing gain with frequency. The shelf slope, in
|
||||||
|
dB/octave, remains proportional to S for all other values for a
|
||||||
|
fixed f0/Fs and dBgain.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Then compute a few intermediate variables:
|
||||||
|
|
||||||
|
A = sqrt( 10^(dBgain/20) )
|
||||||
|
= 10^(dBgain/40) (for peaking and shelving EQ filters only)
|
||||||
|
|
||||||
|
w0 = 2*pi*f0/Fs
|
||||||
|
|
||||||
|
cos(w0)
|
||||||
|
sin(w0)
|
||||||
|
|
||||||
|
alpha = sin(w0)/(2*Q) (case: Q)
|
||||||
|
= sin(w0)*sinh( ln(2)/2 * BW * w0/sin(w0) ) (case: BW)
|
||||||
|
= sin(w0)/2 * sqrt( (A + 1/A)*(1/S - 1) + 2 ) (case: S)
|
||||||
|
|
||||||
|
FYI: The relationship between bandwidth and Q is
|
||||||
|
1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0)) (digital filter w BLT)
|
||||||
|
or 1/Q = 2*sinh(ln(2)/2*BW) (analog filter prototype)
|
||||||
|
|
||||||
|
The relationship between shelf slope and Q is
|
||||||
|
1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
|
||||||
|
|
||||||
|
2*sqrt(A)*alpha = sin(w0) * sqrt( (A^2 + 1)*(1/S - 1) + 2*A )
|
||||||
|
is a handy intermediate variable for shelving EQ filters.
|
||||||
|
|
||||||
|
|
||||||
|
Finally, compute the coefficients for whichever filter type you want:
|
||||||
|
(The analog prototypes, H(s), are shown for each filter
|
||||||
|
type for normalized frequency.)
|
||||||
|
|
||||||
|
|
||||||
|
LPF: H(s) = 1 / (s^2 + s/Q + 1)
|
||||||
|
|
||||||
|
b0 = (1 - cos(w0))/2
|
||||||
|
b1 = 1 - cos(w0)
|
||||||
|
b2 = (1 - cos(w0))/2
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HPF: H(s) = s^2 / (s^2 + s/Q + 1)
|
||||||
|
|
||||||
|
b0 = (1 + cos(w0))/2
|
||||||
|
b1 = -(1 + cos(w0))
|
||||||
|
b2 = (1 + cos(w0))/2
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BPF: H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
|
||||||
|
|
||||||
|
b0 = sin(w0)/2 = Q*alpha
|
||||||
|
b1 = 0
|
||||||
|
b2 = -sin(w0)/2 = -Q*alpha
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
BPF: H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
|
||||||
|
|
||||||
|
b0 = alpha
|
||||||
|
b1 = 0
|
||||||
|
b2 = -alpha
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
notch: H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
|
||||||
|
|
||||||
|
b0 = 1
|
||||||
|
b1 = -2*cos(w0)
|
||||||
|
b2 = 1
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
APF: H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
|
||||||
|
|
||||||
|
b0 = 1 - alpha
|
||||||
|
b1 = -2*cos(w0)
|
||||||
|
b2 = 1 + alpha
|
||||||
|
a0 = 1 + alpha
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
peakingEQ: H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
|
||||||
|
|
||||||
|
b0 = 1 + alpha*A
|
||||||
|
b1 = -2*cos(w0)
|
||||||
|
b2 = 1 - alpha*A
|
||||||
|
a0 = 1 + alpha/A
|
||||||
|
a1 = -2*cos(w0)
|
||||||
|
a2 = 1 - alpha/A
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
lowShelf: H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
|
||||||
|
|
||||||
|
b0 = A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha )
|
||||||
|
b1 = 2*A*( (A-1) - (A+1)*cos(w0) )
|
||||||
|
b2 = A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha )
|
||||||
|
a0 = (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha
|
||||||
|
a1 = -2*( (A-1) + (A+1)*cos(w0) )
|
||||||
|
a2 = (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
highShelf: H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
|
||||||
|
|
||||||
|
b0 = A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha )
|
||||||
|
b1 = -2*A*( (A-1) + (A+1)*cos(w0) )
|
||||||
|
b2 = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha )
|
||||||
|
a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha
|
||||||
|
a1 = 2*( (A-1) - (A+1)*cos(w0) )
|
||||||
|
a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FYI: The bilinear transform (with compensation for frequency warping)
|
||||||
|
substitutes:
|
||||||
|
|
||||||
|
1 1 - z^-1
|
||||||
|
(normalized) s <-- ----------- * ----------
|
||||||
|
tan(w0/2) 1 + z^-1
|
||||||
|
|
||||||
|
and makes use of these trig identities:
|
||||||
|
|
||||||
|
sin(w0) 1 - cos(w0)
|
||||||
|
tan(w0/2) = ------------- (tan(w0/2))^2 = -------------
|
||||||
|
1 + cos(w0) 1 + cos(w0)
|
||||||
|
|
||||||
|
|
||||||
|
resulting in these substitutions:
|
||||||
|
|
||||||
|
|
||||||
|
1 + cos(w0) 1 + 2*z^-1 + z^-2
|
||||||
|
1 <-- ------------- * -------------------
|
||||||
|
1 + cos(w0) 1 + 2*z^-1 + z^-2
|
||||||
|
|
||||||
|
|
||||||
|
1 + cos(w0) 1 - z^-1
|
||||||
|
s <-- ------------- * ----------
|
||||||
|
sin(w0) 1 + z^-1
|
||||||
|
|
||||||
|
1 + cos(w0) 1 - z^-2
|
||||||
|
= ------------- * -------------------
|
||||||
|
sin(w0) 1 + 2*z^-1 + z^-2
|
||||||
|
|
||||||
|
|
||||||
|
1 + cos(w0) 1 - 2*z^-1 + z^-2
|
||||||
|
s^2 <-- ------------- * -------------------
|
||||||
|
1 - cos(w0) 1 + 2*z^-1 + z^-2
|
||||||
|
|
||||||
|
|
||||||
|
The factor:
|
||||||
|
|
||||||
|
1 + cos(w0)
|
||||||
|
-------------------
|
||||||
|
1 + 2*z^-1 + z^-2
|
||||||
|
|
||||||
|
is common to all terms in both numerator and denominator, can be factored
|
||||||
|
out, and thus be left out in the substitutions above resulting in:
|
||||||
|
|
||||||
|
|
||||||
|
1 + 2*z^-1 + z^-2
|
||||||
|
1 <-- -------------------
|
||||||
|
1 + cos(w0)
|
||||||
|
|
||||||
|
|
||||||
|
1 - z^-2
|
||||||
|
s <-- -------------------
|
||||||
|
sin(w0)
|
||||||
|
|
||||||
|
|
||||||
|
1 - 2*z^-1 + z^-2
|
||||||
|
s^2 <-- -------------------
|
||||||
|
1 - cos(w0)
|
||||||
|
|
||||||
|
|
||||||
|
In addition, all terms, numerator and denominator, can be multiplied by a
|
||||||
|
common (sin(w0))^2 factor, finally resulting in these substitutions:
|
||||||
|
|
||||||
|
|
||||||
|
1 <-- (1 + 2*z^-1 + z^-2) * (1 - cos(w0))
|
||||||
|
|
||||||
|
s <-- (1 - z^-2) * sin(w0)
|
||||||
|
|
||||||
|
s^2 <-- (1 - 2*z^-1 + z^-2) * (1 + cos(w0))
|
||||||
|
|
||||||
|
1 + s^2 <-- 2 * (1 - 2*cos(w0)*z^-1 + z^-2)
|
||||||
|
|
||||||
|
|
||||||
|
The biquad coefficient formulae above come out after a little
|
||||||
|
simplification.
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,301 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* iir.c */
|
||||||
|
/*************************************************************************/
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "iir.h"
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Global Variables */
|
||||||
|
/*************************************************************************/
|
||||||
|
const char *filterTypeString[] =
|
||||||
|
{
|
||||||
|
"Unknown filter type",
|
||||||
|
"Butterworth-Lowpass",
|
||||||
|
"Butterworth-Highpass",
|
||||||
|
"Butterworth-Bandpass",
|
||||||
|
"Butterworth-Bandstop",
|
||||||
|
"Peaking-EQ",
|
||||||
|
"Low Shelving-EQ",
|
||||||
|
"High Shelving-EQ"
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
void IIRCalcFilterCoeff(struct _sIIRCoeff *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t q, unsigned order, unsigned filterType)
|
||||||
|
{
|
||||||
|
unsigned p;
|
||||||
|
iir_float_t qp;
|
||||||
|
|
||||||
|
// IIRInit(pCoeff, order);
|
||||||
|
|
||||||
|
for(p=0; p < order/2;p++)
|
||||||
|
{
|
||||||
|
qp = q * IIRCalcQp(p+1, order);
|
||||||
|
IIRCalcPartFilterCoeff2(&pCoeff[p], 1.0, fa, fg, qp, filterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff1(struct _sIIRCoeff *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t Qi, unsigned filterType)
|
||||||
|
{
|
||||||
|
iir_float_t K, a0;
|
||||||
|
iir_float_t alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = (iir_float_t)(2*iir_pi*fg/fa);
|
||||||
|
ks = (iir_float_t)sin(omega);
|
||||||
|
kc = (iir_float_t)cos(omega);
|
||||||
|
alpha = 0.5f*ks /Qi;
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
a0 = K/Qi + 1;
|
||||||
|
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi)/a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f/a0;
|
||||||
|
pCoeff->bk1 = 1.0f/a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi) /a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f*K /a0;
|
||||||
|
pCoeff->bk1 = -1.0f*K /a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff2(struct _sIIRCoeff *pCoeff, iir_float_t A, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType)
|
||||||
|
{
|
||||||
|
iir_float_t a0;
|
||||||
|
iir_float_t alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = (iir_float_t)(2*iir_pi*fg/fa);
|
||||||
|
ks = (iir_float_t)sin(omega);
|
||||||
|
kc = (iir_float_t)cos(omega);
|
||||||
|
alpha = 0.5f*ks /qp;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5f*(1 - kc) /a0;
|
||||||
|
pCoeff->bk1 = (1 - kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5f*(1 - kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5f*(1 + kc) /a0;
|
||||||
|
pCoeff->bk1 = -(1 + kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5f*(1 + kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = alpha /a0;
|
||||||
|
pCoeff->bk1 = 0;
|
||||||
|
pCoeff->bk2 = -alpha /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDSTOP:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f /a0;
|
||||||
|
pCoeff->bk1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->bk2 = 1.0f /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_PEAKING:
|
||||||
|
|
||||||
|
a0 = 1 + (alpha/A);
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - (alpha/A)) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = (1 + (alpha*A)) /a0;
|
||||||
|
pCoeff->bk1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->bk2 = (1 - (alpha*A)) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR(struct _sIIRCoeff *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints)
|
||||||
|
{
|
||||||
|
iir_float_t xp, yp;
|
||||||
|
unsigned i, p;
|
||||||
|
unsigned numSec = order/2;
|
||||||
|
|
||||||
|
for (i=0; i<numPoints; i++)
|
||||||
|
{
|
||||||
|
xp = xn[i];
|
||||||
|
for (p=0; p < numSec; p++)
|
||||||
|
{
|
||||||
|
yp = pCoeff[p].bk0*xp
|
||||||
|
+ pCoeff[p].bk1*pCoeff[p].xn1
|
||||||
|
+ pCoeff[p].bk2*pCoeff[p].xn2
|
||||||
|
- pCoeff[p].ak1*pCoeff[p].yn1
|
||||||
|
- pCoeff[p].ak2*pCoeff[p].yn2;
|
||||||
|
|
||||||
|
pCoeff[p].yn2 = pCoeff[p].yn1;
|
||||||
|
pCoeff[p].yn1 = yp;
|
||||||
|
pCoeff[p].xn2 = pCoeff[p].xn1;
|
||||||
|
pCoeff[p].xn1 = xp;
|
||||||
|
xp = yp;
|
||||||
|
}
|
||||||
|
yn[i] = yp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIRInit(struct _sIIRCoeff *pCoeff, unsigned order)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0; n < order/2; n++)
|
||||||
|
{
|
||||||
|
pCoeff[n].ak0 = 0;
|
||||||
|
pCoeff[n].ak1 = 0;
|
||||||
|
pCoeff[n].ak2 = 0;
|
||||||
|
pCoeff[n].bk0 = 0;
|
||||||
|
pCoeff[n].bk1 = 0;
|
||||||
|
pCoeff[n].bk2 = 0;
|
||||||
|
pCoeff[n].xn1 = 0;
|
||||||
|
pCoeff[n].xn2 = 0;
|
||||||
|
pCoeff[n].yn1 = 0;
|
||||||
|
pCoeff[n].yn2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRBilTrans(iir_float_t fg, iir_float_t fa)
|
||||||
|
{
|
||||||
|
return 1.0f/(iir_float_t)(tan(iir_pi*fg/fa));
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRCalcQp(unsigned p, unsigned N)
|
||||||
|
{
|
||||||
|
return 1.0f/(iir_float_t)(2*sin(iir_pi*(2*p-1)/(2*N)));
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRS(struct _sIIRCoeff *pCoeff, iir_float_t xn, unsigned order)
|
||||||
|
{
|
||||||
|
iir_float_t xp, yp;
|
||||||
|
unsigned p;
|
||||||
|
|
||||||
|
xp = xn;
|
||||||
|
yp = 0;
|
||||||
|
for (p=0; p < order/2; p++)
|
||||||
|
{
|
||||||
|
|
||||||
|
yp = (iir_float_t) (pCoeff[p].bk0*xp
|
||||||
|
+ pCoeff[p].bk1*pCoeff[p].xn1
|
||||||
|
+ pCoeff[p].bk2*pCoeff[p].xn2
|
||||||
|
- pCoeff[p].ak1*pCoeff[p].yn1
|
||||||
|
- pCoeff[p].ak2*pCoeff[p].yn2);
|
||||||
|
|
||||||
|
pCoeff[p].yn2 = pCoeff[p].yn1;
|
||||||
|
pCoeff[p].yn1 = yp;
|
||||||
|
pCoeff[p].xn2 = pCoeff[p].xn1;
|
||||||
|
pCoeff[p].xn1 = xp;
|
||||||
|
xp = yp;
|
||||||
|
}
|
||||||
|
return yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR_lin_init(iir_lin_t *pObj, unsigned order)
|
||||||
|
{
|
||||||
|
pObj->order = order;
|
||||||
|
pObj->pX = (iir_float_t*)malloc((order+1)*sizeof(iir_float_t));
|
||||||
|
pObj->pY = (iir_float_t*)malloc((order+1)*sizeof(iir_float_t));
|
||||||
|
memset(pObj->pX, 0, (order+1)*sizeof(iir_float_t));
|
||||||
|
memset(pObj->pY, 0, (order+1)*sizeof(iir_float_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR_lin_free(iir_lin_t *pObj)
|
||||||
|
{
|
||||||
|
if (pObj->pX)
|
||||||
|
free(pObj->pX);
|
||||||
|
|
||||||
|
if (pObj->pY)
|
||||||
|
free(pObj->pY);
|
||||||
|
|
||||||
|
pObj->order = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIR_lin_process(iir_lin_t *pObj, iir_float_t *pB, iir_float_t *pA, iir_float_t x)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
iir_float_t y;
|
||||||
|
|
||||||
|
if (!pObj->order)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i=pObj->order; i >= 1; i--)
|
||||||
|
pObj->pX[i] = pObj->pX[i-1];
|
||||||
|
|
||||||
|
for (i=pObj->order; i >= 1; i--)
|
||||||
|
pObj->pY[i] = pObj->pY[i-1];
|
||||||
|
|
||||||
|
pObj->pX[0] = x;
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
for (i=0; i <= pObj->order; i++)
|
||||||
|
y += pObj->pX[i]*pB[i];
|
||||||
|
|
||||||
|
for (i=1; i <= pObj->order; i++)
|
||||||
|
y -= pObj->pY[i]*pA[i];
|
||||||
|
|
||||||
|
pObj->pY[0] = y;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Executable
+225
@@ -0,0 +1,225 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* iir.c
|
||||||
|
/*************************************************************************/
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "iir.h"
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Global Variables
|
||||||
|
/*************************************************************************/
|
||||||
|
const char *filterTypeString[] =
|
||||||
|
{
|
||||||
|
"Unknown filter type",
|
||||||
|
"Butterworth-Lowpass",
|
||||||
|
"Butterworth-Highpass",
|
||||||
|
"Butterworth-Bandpass",
|
||||||
|
"Butterworth-Bandstop",
|
||||||
|
"Peaking-EQ",
|
||||||
|
"Low Shelving-EQ",
|
||||||
|
"High Shelving-EQ"
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
void IIRCalcFilterCoeff(struct _sIIRCoeff *pCoeff, double fa, double fg, double q, unsigned order, unsigned filterType)
|
||||||
|
{
|
||||||
|
unsigned p;
|
||||||
|
double qp;
|
||||||
|
|
||||||
|
IIRInit(pCoeff, order);
|
||||||
|
|
||||||
|
for(p=0; p < order/2;p++)
|
||||||
|
{
|
||||||
|
qp = q * IIRCalcQp(p+1, order);
|
||||||
|
IIRCalcPartFilterCoeff2(&pCoeff[p], 1.0, fa, fg, qp, filterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff1(struct _sIIRCoeff *pCoeff, double fa, double fg, double Qi, unsigned filterType)
|
||||||
|
{
|
||||||
|
double K, a0;
|
||||||
|
double alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = 2*pi*fg/fa;
|
||||||
|
ks = sin(omega);
|
||||||
|
kc = cos(omega);
|
||||||
|
alpha = 0.5*ks /Qi;
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
a0 = K/Qi + 1;
|
||||||
|
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi)/a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0/a0;
|
||||||
|
pCoeff->bk1 = 1.0/a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi) /a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0*K /a0;
|
||||||
|
pCoeff->bk1 = -1.0*K /a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff2(struct _sIIRCoeff *pCoeff, double A, double fa, double fg, double qp, unsigned filterType)
|
||||||
|
{
|
||||||
|
double a0;
|
||||||
|
double alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = 2*pi*fg/fa;
|
||||||
|
ks = sin(omega);
|
||||||
|
kc = cos(omega);
|
||||||
|
alpha = 0.5*ks /qp;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = -2.0*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5*(1 - kc) /a0;
|
||||||
|
pCoeff->bk1 = (1 - kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5*(1 - kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = -2.0*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5*(1 + kc) /a0;
|
||||||
|
pCoeff->bk1 = -(1 + kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5*(1 + kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = -2.0*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = alpha /a0;
|
||||||
|
pCoeff->bk1 = 0;
|
||||||
|
pCoeff->bk2 = -alpha /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDSTOP:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = -2.0*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0 /a0;
|
||||||
|
pCoeff->bk1 = -2.0*kc /a0;
|
||||||
|
pCoeff->bk2 = 1.0 /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_PEAKING:
|
||||||
|
|
||||||
|
a0 = 1 + (alpha/A);
|
||||||
|
pCoeff->ak0 = 1.0;
|
||||||
|
pCoeff->ak1 = -2.0*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - (alpha/A)) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = (1 + (alpha*A)) /a0;
|
||||||
|
pCoeff->bk1 = -2.0*kc /a0;
|
||||||
|
pCoeff->bk2 = (1 - (alpha*A)) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR(struct _sIIRCoeff *pCoeff, double *xn, double *yn, unsigned order, unsigned numPoints)
|
||||||
|
{
|
||||||
|
double xp, yp;
|
||||||
|
unsigned i, p;
|
||||||
|
unsigned numSec = order/2;
|
||||||
|
|
||||||
|
for (i=0; i<numPoints; i++)
|
||||||
|
{
|
||||||
|
xp = xn[i];
|
||||||
|
for (p=0; p < numSec; p++)
|
||||||
|
{
|
||||||
|
yp = pCoeff[p].bk0*xp
|
||||||
|
+ pCoeff[p].bk1*pCoeff[p].xn1
|
||||||
|
+ pCoeff[p].bk2*pCoeff[p].xn2
|
||||||
|
- pCoeff[p].ak1*pCoeff[p].yn1
|
||||||
|
- pCoeff[p].ak2*pCoeff[p].yn2;
|
||||||
|
|
||||||
|
pCoeff[p].yn2 = pCoeff[p].yn1;
|
||||||
|
pCoeff[p].yn1 = yp;
|
||||||
|
pCoeff[p].xn2 = pCoeff[p].xn1;
|
||||||
|
pCoeff[p].xn1 = xp;
|
||||||
|
xp = yp;
|
||||||
|
}
|
||||||
|
yn[i] = yp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIRInit(struct _sIIRCoeff *pCoeff, unsigned order)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0; n < order/2; n++)
|
||||||
|
{
|
||||||
|
pCoeff[n].ak0 = 0;
|
||||||
|
pCoeff[n].ak1 = 0;
|
||||||
|
pCoeff[n].ak2 = 0;
|
||||||
|
pCoeff[n].bk0 = 0;
|
||||||
|
pCoeff[n].bk1 = 0;
|
||||||
|
pCoeff[n].bk2 = 0;
|
||||||
|
pCoeff[n].xn1 = 0;
|
||||||
|
pCoeff[n].xn2 = 0;
|
||||||
|
pCoeff[n].yn1 = 0;
|
||||||
|
pCoeff[n].yn2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double IIRBilTrans(double fg, double fa)
|
||||||
|
{
|
||||||
|
return 1.0/(tan(pi*fg/fa));
|
||||||
|
}
|
||||||
|
|
||||||
|
double IIRCalcQp(unsigned p, unsigned N)
|
||||||
|
{
|
||||||
|
return 1.0/(2*sin(pi*(2*p-1)/(2*N)));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "iir.h"
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
const char *filterTypeString[] =
|
||||||
|
{
|
||||||
|
"Unknown filter type",
|
||||||
|
"Butterworth-Lowpass",
|
||||||
|
"Butterworth-Highpass",
|
||||||
|
"Butterworth-Bandpass",
|
||||||
|
"Butterworth-Bandstop"
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int IIRCalcPartFilterCoeff1(CIIRCoeff *pCoeff, double fg, double fa, double Qi, unsigned filterType)
|
||||||
|
{
|
||||||
|
double K, A0;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
A0 = 1.0 /(K/Qi + 1);
|
||||||
|
|
||||||
|
pCoeff->m_pak[0] = 1.0;
|
||||||
|
pCoeff->m_pak[1] = (1 - K/Qi) * A0;
|
||||||
|
|
||||||
|
pCoeff->m_pbk[0] = 1.0;// * A0;
|
||||||
|
pCoeff->m_pbk[1] = 1.0;// * A0;
|
||||||
|
pCoeff->m_aScale = A0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
A0 = 1.0 /(K/Qi + 1);
|
||||||
|
|
||||||
|
pCoeff->m_pak[0] = 1.0;
|
||||||
|
pCoeff->m_pak[1] = (1 - K/Qi) * A0;
|
||||||
|
|
||||||
|
pCoeff->m_pbk[0] = 1.0*K;// * A0;
|
||||||
|
pCoeff->m_pbk[1] = -1.0*K;// * A0;
|
||||||
|
pCoeff->m_aScale = A0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff2(CIIRCoeff *pCoeff, double fg, double fa, double Qi, unsigned filterType)
|
||||||
|
{
|
||||||
|
double K, KK, A0,B0;
|
||||||
|
double alpha, omega, sn, cs;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
KK = K*K;
|
||||||
|
A0 = 1.0 /(1 + KK + K/Qi);
|
||||||
|
|
||||||
|
omega = 2*pi*fg/fa;
|
||||||
|
sn = sin(omega);
|
||||||
|
cs = cos(omega);
|
||||||
|
alpha = 0.5*sn /Qi;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
pCoeff->m_pak[0] = 1.0 ;
|
||||||
|
pCoeff->m_pak[1] = 2 *(1 - KK) * A0;
|
||||||
|
pCoeff->m_pak[2] = (1 + KK - K/Qi) * A0;
|
||||||
|
|
||||||
|
pCoeff->m_pbk[0] = 1.0*A0;
|
||||||
|
pCoeff->m_pbk[1] = 2.0*A0;
|
||||||
|
pCoeff->m_pbk[2] = 1.0*A0;
|
||||||
|
|
||||||
|
pCoeff->m_aScale = A0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
pCoeff->m_pak[0] = 1.0;
|
||||||
|
pCoeff->m_pak[1] = 2 *(1 - KK) * A0;
|
||||||
|
pCoeff->m_pak[2] = (KK - K/Qi + 1) * A0;
|
||||||
|
|
||||||
|
pCoeff->m_pbk[0] = 1.0*KK * A0;
|
||||||
|
pCoeff->m_pbk[1] = -2.0*KK * A0;
|
||||||
|
pCoeff->m_pbk[2] = 1.0*KK * A0;
|
||||||
|
pCoeff->m_aScale = A0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IIRBilTrans(double fg, double fa)
|
||||||
|
{
|
||||||
|
return 1.0/(tan(pi*fg/fa));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR(double *xn, double *yn, CIIRCoeff *pCoeff, unsigned numPoints)
|
||||||
|
{
|
||||||
|
unsigned n, k;
|
||||||
|
double y1, y2;
|
||||||
|
|
||||||
|
for (n=0; n < numPoints; n++)
|
||||||
|
{
|
||||||
|
y1 = 0;
|
||||||
|
y2 = 0;
|
||||||
|
for (k=0; k <= pCoeff->m_Nb; k++)
|
||||||
|
{
|
||||||
|
y1 = y1 + (pCoeff->m_pbk[k] * xn[pCoeff->m_Nb-k+n]);
|
||||||
|
if (!_finite(y1))
|
||||||
|
printf("\nException: MATH ERROR!\n");
|
||||||
|
}
|
||||||
|
// y1 = y1 *pCoeff->m_aScale;
|
||||||
|
|
||||||
|
for (k=1; k <= pCoeff->m_Na; k++)
|
||||||
|
{
|
||||||
|
y2 = y2 - (pCoeff->m_pak[k] * yn[pCoeff->m_Na-k+n]);
|
||||||
|
if (!_finite(y2))
|
||||||
|
printf("\nException: MATH ERROR!\n");
|
||||||
|
}
|
||||||
|
// y2 = y2 *pCoeff->m_bScale;
|
||||||
|
|
||||||
|
yn[pCoeff->m_Nb+n] = (y1 + y2);
|
||||||
|
if (!_finite(yn[pCoeff->m_Nb+n]))
|
||||||
|
printf("\nException: MATH ERROR!\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcFilterCoeff(double fg, double fa, double Qi, unsigned N, CIIRCoeff *pCoeff, unsigned filterType)
|
||||||
|
{
|
||||||
|
unsigned p, order_ap, order_bp;
|
||||||
|
div_t result;
|
||||||
|
unsigned numEvenFilterParts, filterCnt;
|
||||||
|
|
||||||
|
CIIRCoeff Temp(N, N);
|
||||||
|
CIIRCoeff Coeff1(1,1);
|
||||||
|
CIIRCoeff Coeff2(2,2);
|
||||||
|
|
||||||
|
double Qp;
|
||||||
|
FILE *pFile;
|
||||||
|
|
||||||
|
pFile = fopen("filter.out","w");
|
||||||
|
fprintf(pFile,"IIR-Filter Version 1.0\n");
|
||||||
|
fprintf(pFile,"Filter Coefficients for %u-Order-%s, Qi = %4.2f\n",N, filterTypeString[filterType],Qi);
|
||||||
|
fprintf(pFile,"fg = %9.2f Hz\nfa = %9.2f Hz\n",fg, fa);
|
||||||
|
|
||||||
|
p = 1;
|
||||||
|
filterCnt = 1;
|
||||||
|
|
||||||
|
switch (N)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
order_ap = 2;
|
||||||
|
order_bp = 2;
|
||||||
|
result = div(N,2);
|
||||||
|
numEvenFilterParts = result.quot;
|
||||||
|
|
||||||
|
if (result.rem != 0)
|
||||||
|
{
|
||||||
|
IIRCalcPartFilterCoeff1(&Coeff1, fg, fa, 1.0*Qi, filterType);
|
||||||
|
fprintf(pFile,"\n1.Partfilter Np = 1, Qp = 1.00\n");
|
||||||
|
IIRPrintCoeff(pFile,&Coeff1, 1);
|
||||||
|
filterCnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (p <= numEvenFilterParts)
|
||||||
|
{
|
||||||
|
Qp = IIRCalcQp(p, N);
|
||||||
|
if (p == 1)
|
||||||
|
{
|
||||||
|
IIRCalcPartFilterCoeff2(&Temp, fg, fa, Qp*Qi, filterType);
|
||||||
|
fprintf(pFile,"\n%u.Partfilter Np = 2, Qp = %4.2f\n", filterCnt, Qp);
|
||||||
|
IIRPrintCoeff(pFile, &Temp, 2);
|
||||||
|
filterCnt++;
|
||||||
|
p++;
|
||||||
|
if (numEvenFilterParts > 1)
|
||||||
|
continue;
|
||||||
|
memcpy(pCoeff->m_pak, Temp.m_pak, (order_ap+1)*sizeof(double));
|
||||||
|
memcpy(pCoeff->m_pbk, Temp.m_pbk, (order_bp+1)*sizeof(double));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IIRCalcPartFilterCoeff2(&Coeff2, fg, fa, Qp*Qi, filterType);
|
||||||
|
fprintf(pFile,"\n%u.Partfilter Np = 2, Qp = %4.2f\n", filterCnt, Qp);
|
||||||
|
IIRPrintCoeff(pFile,&Coeff2, 2);
|
||||||
|
order_ap = IIRMulPolynom(Coeff2.m_pak, 2, Temp.m_pak, order_ap, pCoeff->m_pak);
|
||||||
|
order_bp = IIRMulPolynom(Coeff2.m_pbk, 2, Temp.m_pbk, order_bp, pCoeff->m_pbk);
|
||||||
|
memcpy(Temp.m_pak, pCoeff->m_pak, (order_ap+1)*sizeof(double));
|
||||||
|
memcpy(Temp.m_pbk, pCoeff->m_pbk, (order_bp+1)*sizeof(double));
|
||||||
|
filterCnt++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.rem != 0)
|
||||||
|
{
|
||||||
|
if (result.quot == 0)
|
||||||
|
{
|
||||||
|
memcpy(pCoeff->m_pak, Coeff1.m_pak, 2*sizeof(double));
|
||||||
|
memcpy(pCoeff->m_pbk, Coeff1.m_pbk, 2*sizeof(double));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
order_ap = IIRMulPolynom(Coeff1.m_pak, 1, Temp.m_pak, order_ap, pCoeff->m_pak);
|
||||||
|
order_bp = IIRMulPolynom(Coeff1.m_pbk, 1, Temp.m_pbk, order_bp, pCoeff->m_pbk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(pFile,"\n\nResulted Filter N = %u\n",N);
|
||||||
|
IIRPrintCoeff(pFile,pCoeff, N);
|
||||||
|
// ScaleCoeff(pCoeff);
|
||||||
|
fprintf(pFile,"\n\nNormalized Filterkernel N = %u\n",N);
|
||||||
|
IIRPrintCoeff(pFile,pCoeff, N);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(pFile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned IIRMulPolynom(double *pA, unsigned orderA, double *pB, unsigned orderB, double *pProduct)
|
||||||
|
{
|
||||||
|
unsigned cntA, cntB, newOrder;
|
||||||
|
|
||||||
|
newOrder = orderA+orderB;
|
||||||
|
memset(pProduct, 0, (newOrder+1)*sizeof(double));
|
||||||
|
|
||||||
|
for (cntA=0; cntA <= orderA; cntA++)
|
||||||
|
{
|
||||||
|
for (cntB=0; cntB <= orderB; cntB++)
|
||||||
|
pProduct[cntA+cntB] += pA[cntA] * pB[cntB];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IIRCalcQp(unsigned p, unsigned N)
|
||||||
|
{
|
||||||
|
return 1.0/(2*sin(pi*(2*p-1)/(2*N)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIRPrintCoeff(FILE *pFile, CIIRCoeff *pCoeff, unsigned N)
|
||||||
|
{
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
|
||||||
|
for (k=0; k <= N; k++)
|
||||||
|
{
|
||||||
|
fprintf(pFile,"a[%2u] = %9.6g, b[%2u] = %9.6g\n",k,pCoeff->m_pak[k],k,pCoeff->m_pbk[k]);
|
||||||
|
}
|
||||||
|
fprintf(pFile,"aScale = %9.6g, bScale = %9.6g\n\n",pCoeff->m_aScale, pCoeff->m_bScale);
|
||||||
|
|
||||||
|
fprintf(pFile,";DSP Coefficients\ncoef\n");
|
||||||
|
for (k=N; k > 0; k--)
|
||||||
|
{
|
||||||
|
fprintf(pFile,"\tdc\t%9.7g\t; a%u\n",pCoeff->m_pak[k]/2.0,k);
|
||||||
|
}
|
||||||
|
for (k=N; k > 0; k--)
|
||||||
|
{
|
||||||
|
fprintf(pFile,"\tdc\t%9.7g\t; b%u\n",pCoeff->m_pbk[k]/2.0,k);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ScaleCoeff(CIIRCoeff *pCoeff)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
double val;
|
||||||
|
|
||||||
|
val=0;
|
||||||
|
for(i=0; i <= pCoeff->m_Na; i++)
|
||||||
|
val=MaxMag(val, pCoeff->m_pak[i]);
|
||||||
|
|
||||||
|
for(i=0; i <= pCoeff->m_Na; i++)
|
||||||
|
pCoeff->m_pak[i] /= val;
|
||||||
|
|
||||||
|
pCoeff->m_aScale = val;
|
||||||
|
|
||||||
|
val=0;
|
||||||
|
for(i=0; i <= pCoeff->m_Nb; i++)
|
||||||
|
val=MaxMag(val, pCoeff->m_pbk[i]);
|
||||||
|
|
||||||
|
for(i=0; i <= pCoeff->m_Nb; i++)
|
||||||
|
pCoeff->m_pbk[i] /= val;
|
||||||
|
|
||||||
|
pCoeff->m_bScale = val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double MinMag(double val1, double val2)
|
||||||
|
{
|
||||||
|
if(fabs(val1) < fabs(val2))
|
||||||
|
return fabs(val1);
|
||||||
|
|
||||||
|
return fabs(val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double MaxMag(double val1, double val2)
|
||||||
|
{
|
||||||
|
if(fabs(val1) > fabs(val2))
|
||||||
|
return fabs(val1);
|
||||||
|
|
||||||
|
return fabs(val2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* iir.h */
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifndef IIR_H
|
||||||
|
#define IIR_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef iir_pi
|
||||||
|
#define iir_pi 3.1415926535897932384626433832795
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IIR_FILTERTYPE_UNKNOWN 0x00000000
|
||||||
|
#define IIR_FILTERTYPE_LOWPASS 0x00000001
|
||||||
|
#define IIR_FILTERTYPE_HIGHPASS 0x00000002
|
||||||
|
#define IIR_FILTERTYPE_BANDPASS 0x00000003
|
||||||
|
#define IIR_FILTERTYPE_BANDSTOP 0x00000004
|
||||||
|
#define IIR_FILTERTYPE_PEAKING 0x00000005
|
||||||
|
#define IIR_FILTERTYPE_LOWSHELF 0x00000006
|
||||||
|
#define IIR_FILTERTYPE_HIGHSHELF 0x00000007
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifndef iir_float_t
|
||||||
|
#define iir_float_t float
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _sComplex
|
||||||
|
{
|
||||||
|
iir_float_t pRealData, pImagData;
|
||||||
|
} Complex;
|
||||||
|
|
||||||
|
typedef struct _sIIRCoeff
|
||||||
|
{
|
||||||
|
|
||||||
|
iir_float_t ak0, ak1, ak2;
|
||||||
|
iir_float_t bk0, bk1, bk2;
|
||||||
|
iir_float_t xn1, xn2;
|
||||||
|
iir_float_t yn1, yn2;
|
||||||
|
|
||||||
|
}IIRCOEFF;
|
||||||
|
|
||||||
|
typedef struct _sIIRParam
|
||||||
|
{
|
||||||
|
/* General Params */
|
||||||
|
iir_float_t fg, Qf;
|
||||||
|
|
||||||
|
/* for shelving EQs */
|
||||||
|
iir_float_t beta;
|
||||||
|
|
||||||
|
/* for peaking and shelving EQs */
|
||||||
|
iir_float_t A;
|
||||||
|
}IIRPARAM;
|
||||||
|
|
||||||
|
typedef struct _siir_lin_t
|
||||||
|
{
|
||||||
|
unsigned order;
|
||||||
|
iir_float_t *pX, *pY;
|
||||||
|
} iir_lin_t;
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void IIRInit(struct _sIIRCoeff *pCoeff, unsigned order);
|
||||||
|
int IIRCalcPartFilterCoeff1(struct _sIIRCoeff *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType);
|
||||||
|
int IIRCalcPartFilterCoeff2(struct _sIIRCoeff *pCoeff, iir_float_t A, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType);
|
||||||
|
void IIRCalcFilterCoeff(struct _sIIRCoeff *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t q, unsigned order, unsigned filterType);
|
||||||
|
|
||||||
|
iir_float_t IIRBilTrans(iir_float_t fg, iir_float_t fa);
|
||||||
|
iir_float_t IIRCalcQp(unsigned p, unsigned N);
|
||||||
|
void IIR(struct _sIIRCoeff *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints);
|
||||||
|
iir_float_t IIRS(struct _sIIRCoeff *pCoeff, iir_float_t xn, unsigned order);
|
||||||
|
|
||||||
|
void IIRSSE(struct _sIIRCoeff *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints);
|
||||||
|
void IIRPrintCoeff(FILE *pFile, struct _sIIRCoeff *pCoeff, unsigned N);
|
||||||
|
void ScaleCoeff(struct _sIIRCoeff *pCoeff);
|
||||||
|
iir_float_t MinMag(iir_float_t val1, iir_float_t val2);
|
||||||
|
iir_float_t MaxMag(iir_float_t val1, iir_float_t val2);
|
||||||
|
|
||||||
|
void IIR_lin_init(iir_lin_t *pObj, unsigned order);
|
||||||
|
void IIR_lin_free(iir_lin_t *pObj);
|
||||||
|
iir_float_t IIR_lin_process(iir_lin_t *pObj, iir_float_t *pB, iir_float_t *pA, iir_float_t x);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // IIR_H
|
||||||
|
/******************************************************************************/
|
||||||
Executable
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* iir.h
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifndef IIR_H
|
||||||
|
#define IIR_H
|
||||||
|
|
||||||
|
#define pi 3.1415926535897932384626433832795
|
||||||
|
|
||||||
|
#define IIR_FILTERTYPE_UNKNOWN 0x00000000
|
||||||
|
#define IIR_FILTERTYPE_LOWPASS 0x00000001
|
||||||
|
#define IIR_FILTERTYPE_HIGHPASS 0x00000002
|
||||||
|
#define IIR_FILTERTYPE_BANDPASS 0x00000003
|
||||||
|
#define IIR_FILTERTYPE_BANDSTOP 0x00000004
|
||||||
|
#define IIR_FILTERTYPE_PEAKING 0x00000005
|
||||||
|
#define IIR_FILTERTYPE_LOWSHELF 0x00000006
|
||||||
|
#define IIR_FILTERTYPE_HIGHSHELF 0x00000007
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
typedef struct _sComplex
|
||||||
|
{
|
||||||
|
double pRealData, pImagData;
|
||||||
|
} Complex;
|
||||||
|
|
||||||
|
typedef struct _sIIRCoeff
|
||||||
|
{
|
||||||
|
|
||||||
|
double ak0, ak1, ak2;
|
||||||
|
double bk0, bk1, bk2;
|
||||||
|
double xn1, xn2;
|
||||||
|
double yn1, yn2;
|
||||||
|
|
||||||
|
}IIRCOEFF;
|
||||||
|
|
||||||
|
typedef struct _sIIRParam
|
||||||
|
{
|
||||||
|
/* General Params */
|
||||||
|
double fg, Qf;
|
||||||
|
|
||||||
|
/* for shelving EQs */
|
||||||
|
double beta;
|
||||||
|
|
||||||
|
/* for peaking and shelving EQs */
|
||||||
|
double A;
|
||||||
|
}IIRPARAM;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void IIRInit(struct _sIIRCoeff *pCoeff, unsigned order);
|
||||||
|
int IIRCalcPartFilterCoeff1(struct _sIIRCoeff *pCoeff, double fa, double fg, double qp, unsigned filterType);
|
||||||
|
int IIRCalcPartFilterCoeff2(struct _sIIRCoeff *pCoeff, double A, double fa, double fg, double qp, unsigned filterType);
|
||||||
|
void IIRCalcFilterCoeff(struct _sIIRCoeff *pCoeff, double fa, double fg, double q, unsigned order, unsigned filterType);
|
||||||
|
|
||||||
|
double IIRBilTrans(double fg, double fa);
|
||||||
|
double IIRCalcQp(unsigned p, unsigned N);
|
||||||
|
void IIR(struct _sIIRCoeff *pCoeff, double *xn, double *yn, unsigned order, unsigned numPoints);
|
||||||
|
void IIRPrintCoeff(FILE *pFile, struct _sIIRCoeff *pCoeff, unsigned N);
|
||||||
|
void ScaleCoeff(struct _sIIRCoeff *pCoeff);
|
||||||
|
double MinMag(double val1, double val2);
|
||||||
|
double MaxMag(double val1, double val2);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // IIR_H
|
||||||
|
/******************************************************************************/
|
||||||
@@ -0,0 +1,295 @@
|
|||||||
|
/*************************************************************************/
|
||||||
|
/* iir.c */
|
||||||
|
/*************************************************************************/
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "iir2.h"
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Global Variables */
|
||||||
|
/*************************************************************************/
|
||||||
|
const char *filterTypeString[] =
|
||||||
|
{
|
||||||
|
"Unknown filter type",
|
||||||
|
"Butterworth-Lowpass",
|
||||||
|
"Butterworth-Highpass",
|
||||||
|
"Butterworth-Bandpass",
|
||||||
|
"Butterworth-Bandstop",
|
||||||
|
"Peaking-EQ",
|
||||||
|
"Low Shelving-EQ",
|
||||||
|
"High Shelving-EQ"
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
void IIRCalcFilterCoeff(iir_coef_t *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t q, unsigned order, unsigned filterType)
|
||||||
|
{
|
||||||
|
unsigned p;
|
||||||
|
iir_float_t qp;
|
||||||
|
|
||||||
|
// IIRInit(pCoeff, order);
|
||||||
|
|
||||||
|
for(p=0; p < order/2;p++)
|
||||||
|
{
|
||||||
|
qp = q * IIRCalcQp(p+1, order);
|
||||||
|
IIRCalcPartFilterCoeff2(&pCoeff[p], 1.0, fa, fg, qp, filterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff1(iir_coef_t *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t Qi, unsigned filterType)
|
||||||
|
{
|
||||||
|
iir_float_t K, a0;
|
||||||
|
iir_float_t alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = (iir_float_t)(2*pi*fg/fa);
|
||||||
|
ks = (iir_float_t)sin(omega);
|
||||||
|
kc = (iir_float_t)cos(omega);
|
||||||
|
alpha = 0.5f*ks /Qi;
|
||||||
|
|
||||||
|
K = IIRBilTrans(fg, fa);
|
||||||
|
a0 = K/Qi + 1;
|
||||||
|
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi)/a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f/a0;
|
||||||
|
pCoeff->bk1 = 1.0f/a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = (1 - K/Qi) /a0;
|
||||||
|
pCoeff->ak2 = 0.0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f*K /a0;
|
||||||
|
pCoeff->bk1 = -1.0f*K /a0;
|
||||||
|
pCoeff->bk2 = 0.0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = (unsigned)-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IIRCalcPartFilterCoeff2(iir_coef_t *pCoeff, iir_float_t A, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType)
|
||||||
|
{
|
||||||
|
iir_float_t a0;
|
||||||
|
iir_float_t alpha, omega, ks, kc;
|
||||||
|
unsigned error;
|
||||||
|
|
||||||
|
omega = (iir_float_t)(2*pi*fg/fa);
|
||||||
|
ks = (iir_float_t)sin(omega);
|
||||||
|
kc = (iir_float_t)cos(omega);
|
||||||
|
alpha = 0.5f*ks /qp;
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
switch(filterType)
|
||||||
|
{
|
||||||
|
case IIR_FILTERTYPE_LOWPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5f*(1 - kc) /a0;
|
||||||
|
pCoeff->bk1 = (1 - kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5f*(1 - kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_HIGHPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 0.5f*(1 + kc) /a0;
|
||||||
|
pCoeff->bk1 = -(1 + kc) /a0;
|
||||||
|
pCoeff->bk2 = 0.5f*(1 + kc) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDPASS:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = alpha /a0;
|
||||||
|
pCoeff->bk1 = 0;
|
||||||
|
pCoeff->bk2 = -alpha /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_BANDSTOP:
|
||||||
|
|
||||||
|
a0 = 1 + alpha;
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - alpha) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = 1.0f /a0;
|
||||||
|
pCoeff->bk1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->bk2 = 1.0f /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_FILTERTYPE_PEAKING:
|
||||||
|
|
||||||
|
a0 = 1 + (alpha/A);
|
||||||
|
pCoeff->ak0 = 1.0f;
|
||||||
|
pCoeff->ak1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->ak2 = (1 - (alpha/A)) /a0;
|
||||||
|
|
||||||
|
pCoeff->bk0 = (1 + (alpha*A)) /a0;
|
||||||
|
pCoeff->bk1 = -2.0f*kc /a0;
|
||||||
|
pCoeff->bk2 = (1 - (alpha*A)) /a0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR(iir_state_t *pState, iir_coef_t *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints)
|
||||||
|
{
|
||||||
|
iir_float_t xp, yp;
|
||||||
|
unsigned i, p;
|
||||||
|
unsigned numSec = order/2;
|
||||||
|
|
||||||
|
for (i=0; i<numPoints; i++)
|
||||||
|
{
|
||||||
|
xp = xn[i];
|
||||||
|
for (p=0; p < numSec; p++)
|
||||||
|
{
|
||||||
|
yp = pCoeff[p].bk0*xp
|
||||||
|
+ pCoeff[p].bk1*pState[p].xn1
|
||||||
|
+ pCoeff[p].bk2*pState[p].xn2
|
||||||
|
- pCoeff[p].ak1*pState[p].yn1
|
||||||
|
- pCoeff[p].ak2*pState[p].yn2;
|
||||||
|
|
||||||
|
pState[p].yn2 = pState[p].yn1;
|
||||||
|
pState[p].yn1 = yp;
|
||||||
|
pState[p].xn2 = pState[p].xn1;
|
||||||
|
pState[p].xn1 = xp;
|
||||||
|
xp = yp;
|
||||||
|
}
|
||||||
|
yn[i] = yp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIRInit(iir_state_t *pState, unsigned order)
|
||||||
|
{
|
||||||
|
unsigned n;
|
||||||
|
for(n=0; n < order/2; n++)
|
||||||
|
{
|
||||||
|
pState[n].xn1 = 0;
|
||||||
|
pState[n].xn2 = 0;
|
||||||
|
pState[n].yn1 = 0;
|
||||||
|
pState[n].yn2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRBilTrans(iir_float_t fg, iir_float_t fa)
|
||||||
|
{
|
||||||
|
return 1.0f/(iir_float_t)(tan(pi*fg/fa));
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRCalcQp(unsigned p, unsigned N)
|
||||||
|
{
|
||||||
|
return 1.0f/(iir_float_t)(2*sin(pi*(2*p-1)/(2*N)));
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIRS(iir_state_t *pState, iir_coef_t *pCoeff, iir_float_t xn, unsigned order)
|
||||||
|
{
|
||||||
|
iir_float_t xp, yp;
|
||||||
|
unsigned p;
|
||||||
|
|
||||||
|
xp = xn;
|
||||||
|
yp = 0;
|
||||||
|
for (p=0; p < order/2; p++)
|
||||||
|
{
|
||||||
|
|
||||||
|
yp = (iir_float_t) (pCoeff[p].bk0*xp
|
||||||
|
+ pCoeff[p].bk1*pState[p].xn1
|
||||||
|
+ pCoeff[p].bk2*pState[p].xn2
|
||||||
|
- pCoeff[p].ak1*pState[p].yn1
|
||||||
|
- pCoeff[p].ak2*pState[p].yn2);
|
||||||
|
|
||||||
|
pState[p].yn2 = pState[p].yn1;
|
||||||
|
pState[p].yn1 = yp;
|
||||||
|
pState[p].xn2 = pState[p].xn1;
|
||||||
|
pState[p].xn1 = xp;
|
||||||
|
xp = yp;
|
||||||
|
}
|
||||||
|
return yp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR_lin_init(iir_lin_t *pObj, unsigned order)
|
||||||
|
{
|
||||||
|
pObj->order = order;
|
||||||
|
pObj->pX = (iir_float_t*)malloc((order+1)*sizeof(iir_float_t));
|
||||||
|
pObj->pY = (iir_float_t*)malloc((order+1)*sizeof(iir_float_t));
|
||||||
|
memset(pObj->pX, 0, (order+1)*sizeof(iir_float_t));
|
||||||
|
memset(pObj->pY, 0, (order+1)*sizeof(iir_float_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void IIR_lin_free(iir_lin_t *pObj)
|
||||||
|
{
|
||||||
|
if (pObj->pX)
|
||||||
|
free(pObj->pX);
|
||||||
|
|
||||||
|
if (pObj->pY)
|
||||||
|
free(pObj->pY);
|
||||||
|
|
||||||
|
pObj->order = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
iir_float_t IIR_lin_process(iir_lin_t *pObj, iir_float_t *pB, iir_float_t *pA, iir_float_t x)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
iir_float_t y;
|
||||||
|
|
||||||
|
if (!pObj->order)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i=pObj->order; i >= 1; i--)
|
||||||
|
pObj->pX[i] = pObj->pX[i-1];
|
||||||
|
|
||||||
|
for (i=pObj->order; i >= 1; i--)
|
||||||
|
pObj->pY[i] = pObj->pY[i-1];
|
||||||
|
|
||||||
|
pObj->pX[0] = x;
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
for (i=0; i <= pObj->order; i++)
|
||||||
|
y += pObj->pX[i]*pB[i];
|
||||||
|
|
||||||
|
for (i=1; i <= pObj->order; i++)
|
||||||
|
y -= pObj->pY[i]*pA[i];
|
||||||
|
|
||||||
|
pObj->pY[0] = y;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* iir.h */
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifndef IIR_H
|
||||||
|
#define IIR_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifndef pi
|
||||||
|
#define pi 3.1415926535897932384626433832795
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define IIR_FILTERTYPE_UNKNOWN 0x00000000
|
||||||
|
#define IIR_FILTERTYPE_LOWPASS 0x00000001
|
||||||
|
#define IIR_FILTERTYPE_HIGHPASS 0x00000002
|
||||||
|
#define IIR_FILTERTYPE_BANDPASS 0x00000003
|
||||||
|
#define IIR_FILTERTYPE_BANDSTOP 0x00000004
|
||||||
|
#define IIR_FILTERTYPE_PEAKING 0x00000005
|
||||||
|
#define IIR_FILTERTYPE_LOWSHELF 0x00000006
|
||||||
|
#define IIR_FILTERTYPE_HIGHSHELF 0x00000007
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifndef iir_float_t
|
||||||
|
#define iir_float_t float
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _sComplex
|
||||||
|
{
|
||||||
|
iir_float_t pRealData, pImagData;
|
||||||
|
} Complex;
|
||||||
|
|
||||||
|
typedef struct _siir_coef_t
|
||||||
|
{
|
||||||
|
|
||||||
|
iir_float_t ak0, ak1, ak2;
|
||||||
|
iir_float_t bk0, bk1, bk2;
|
||||||
|
|
||||||
|
} iir_coef_t;
|
||||||
|
|
||||||
|
typedef struct _siir_state_t
|
||||||
|
{
|
||||||
|
|
||||||
|
iir_float_t xn1, xn2;
|
||||||
|
iir_float_t yn1, yn2;
|
||||||
|
|
||||||
|
} iir_state_t;
|
||||||
|
|
||||||
|
typedef struct _sIIRParam
|
||||||
|
{
|
||||||
|
/* General Params */
|
||||||
|
iir_float_t fg, Qf;
|
||||||
|
|
||||||
|
/* for shelving EQs */
|
||||||
|
iir_float_t beta;
|
||||||
|
|
||||||
|
/* for peaking and shelving EQs */
|
||||||
|
iir_float_t A;
|
||||||
|
}IIRPARAM;
|
||||||
|
|
||||||
|
typedef struct _siir_lin_t
|
||||||
|
{
|
||||||
|
unsigned order;
|
||||||
|
iir_float_t *pX, *pY;
|
||||||
|
} iir_lin_t;
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void IIRInit(iir_state_t *pState, unsigned order);
|
||||||
|
int IIRCalcPartFilterCoeff1(iir_coef_t *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType);
|
||||||
|
int IIRCalcPartFilterCoeff2(iir_coef_t *pCoeff, iir_float_t A, iir_float_t fa, iir_float_t fg, iir_float_t qp, unsigned filterType);
|
||||||
|
void IIRCalcFilterCoeff(iir_coef_t *pCoeff, iir_float_t fa, iir_float_t fg, iir_float_t q, unsigned order, unsigned filterType);
|
||||||
|
|
||||||
|
iir_float_t IIRBilTrans(iir_float_t fg, iir_float_t fa);
|
||||||
|
iir_float_t IIRCalcQp(unsigned p, unsigned N);
|
||||||
|
void IIR(iir_state_t *pState, iir_coef_t *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints);
|
||||||
|
iir_float_t IIRS(iir_state_t *pState, iir_coef_t *pCoeff, iir_float_t xn, unsigned order);
|
||||||
|
|
||||||
|
void IIRSSE(iir_coef_t *pCoeff, iir_float_t *xn, iir_float_t *yn, unsigned order, unsigned numPoints);
|
||||||
|
void IIRPrintCoeff(FILE *pFile, iir_coef_t *pCoeff, unsigned N);
|
||||||
|
void ScaleCoeff(iir_coef_t *pCoeff);
|
||||||
|
iir_float_t MinMag(iir_float_t val1, iir_float_t val2);
|
||||||
|
iir_float_t MaxMag(iir_float_t val1, iir_float_t val2);
|
||||||
|
|
||||||
|
void IIR_lin_init(iir_lin_t *pObj, unsigned order);
|
||||||
|
void IIR_lin_free(iir_lin_t *pObj);
|
||||||
|
iir_float_t IIR_lin_process(iir_lin_t *pObj, iir_float_t *pB, iir_float_t *pA, iir_float_t x);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // IIR_H
|
||||||
|
/******************************************************************************/
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
#define pi 3.1415926535897932384626433832795
|
||||||
|
|
||||||
|
#define IIR_FILTERTYPE_UNKNOWN 0x00000000
|
||||||
|
#define IIR_FILTERTYPE_LOWPASS 0x00000001
|
||||||
|
#define IIR_FILTERTYPE_HIGHPASS 0x00000002
|
||||||
|
#define IIR_FILTERTYPE_BANDPASS 0x00000003
|
||||||
|
#define IIR_FILTERTYPE_BANDSTOP 0x00000004
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
typedef struct _sComplex
|
||||||
|
{
|
||||||
|
double pRealData, pImagData;
|
||||||
|
} Complex;
|
||||||
|
|
||||||
|
class CIIRCoeff
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CIIRCoeff(unsigned Na=0, unsigned Nb=0)
|
||||||
|
{
|
||||||
|
Init(Na, Nb);
|
||||||
|
}
|
||||||
|
~CIIRCoeff()
|
||||||
|
{
|
||||||
|
if(m_pak!=0)
|
||||||
|
delete [] m_pak;
|
||||||
|
|
||||||
|
if(m_pbk!=0)
|
||||||
|
delete [] m_pbk;
|
||||||
|
}
|
||||||
|
void Init(unsigned Na=0, unsigned Nb=0)
|
||||||
|
{
|
||||||
|
m_pak= 0;
|
||||||
|
m_pbk= 0;
|
||||||
|
m_Na = Na;
|
||||||
|
m_Nb = Nb;
|
||||||
|
m_aScale = 1.0;
|
||||||
|
m_bScale = 1.0;
|
||||||
|
|
||||||
|
if(Na!=0)
|
||||||
|
m_pak = new double[Na+1];
|
||||||
|
|
||||||
|
if(Nb!=0)
|
||||||
|
m_pbk = new double[Nb+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
double *m_pak, m_aScale;
|
||||||
|
unsigned m_Na;
|
||||||
|
double *m_pbk, m_bScale;
|
||||||
|
unsigned m_Nb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int IIRCalcPartFilterCoeff1(CIIRCoeff *pCoeff, double fg, double fa, double Qi, unsigned filterType);
|
||||||
|
int IIRCalcPartFilterCoeff2(CIIRCoeff *pCoeff, double fg, double fa, double Qi, unsigned filterType);
|
||||||
|
int IIRCalcFilterCoeff(double fg, double fa, double Qi, unsigned N, CIIRCoeff *pCoeff, unsigned filterType);
|
||||||
|
double IIRBilTrans(double fg, double fa);
|
||||||
|
double IIRCalcQp(unsigned p, unsigned N);
|
||||||
|
unsigned IIRMulPolynom(double *pA, unsigned orderA, double *pB, unsigned orderB, double *pProduct);
|
||||||
|
void IIR(double *xn, double *yn, CIIRCoeff *pCoeff, unsigned numPoints);
|
||||||
|
void IIRPrintCoeff(FILE *pFile, CIIRCoeff *pCoeff, unsigned N);
|
||||||
|
void ScaleCoeff(CIIRCoeff *pCoeff);
|
||||||
|
double MinMag(double val1, double val2);
|
||||||
|
double MaxMag(double val1, double val2);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,100 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="iir" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** NICHT BEARBEITEN **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||||
|
|
||||||
|
CFG=iir - Win32 Debug
|
||||||
|
!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE
|
||||||
|
!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "iir.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben
|
||||||
|
!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "iir.mak" CFG="iir - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Für die Konfiguration stehen zur Auswahl:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "iir - Win32 Release" (basierend auf "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE "iir - Win32 Debug" (basierend auf "Win32 (x86) Static Library")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "iir - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /I "../../include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||||
|
# ADD BASE RSC /l 0x407 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x407 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo /out:"..\..\lib\release\iir.lib"
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "iir - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||||
|
# ADD BASE RSC /l 0x407 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x407 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LIB32=link.exe -lib
|
||||||
|
# ADD BASE LIB32 /nologo
|
||||||
|
# ADD LIB32 /nologo /out:"..\..\lib\debug\iir.lib"
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "iir - Win32 Release"
|
||||||
|
# Name "iir - Win32 Debug"
|
||||||
|
# Begin Group "Quellcodedateien"
|
||||||
|
|
||||||
|
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\iir.cpp
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# Begin Group "Header-Dateien"
|
||||||
|
|
||||||
|
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\Include\Iir.h
|
||||||
|
# End Source File
|
||||||
|
# End Group
|
||||||
|
# End Target
|
||||||
|
# End Project
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||||
|
# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN!
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Project: "iir"=.\iir.dsp - Package Owner=<4>
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<4>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Global:
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<3>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
<h1>Erstellungsprotokoll</h1>
|
||||||
|
<h3>
|
||||||
|
--------------------Konfiguration: iir - Win32 Debug--------------------
|
||||||
|
</h3>
|
||||||
|
<h3>Befehlszeilen</h3>
|
||||||
|
Erstellen der temporären Datei "E:\WIN95\TEMP\RSP4230.TMP" mit Inhalten
|
||||||
|
[
|
||||||
|
/nologo /MLd /W3 /Gm /GX /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fp"Debug/iir.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
|
||||||
|
"G:\work\Develop\MSVC\LIBSRC\IIR\iir.cpp"
|
||||||
|
]
|
||||||
|
Creating command line "cl.exe @E:\WIN95\TEMP\RSP4230.TMP"
|
||||||
|
Erstellen der Befehlzeile "link.exe -lib /nologo /out:"..\..\lib\debug\iir.lib" .\Debug\iir.obj "
|
||||||
|
<h3>Ausgabefenster</h3>
|
||||||
|
Kompilierung läuft...
|
||||||
|
iir.cpp
|
||||||
|
g:\work\develop\msvc\libsrc\iir\iir.cpp(63) : warning C4101: 'B0' : Unreferenzierte lokale Variable
|
||||||
|
Bibliothek wird erstellt...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Ergebnisse</h3>
|
||||||
|
iir.lib - 0 Fehler, 1 Warnung(en)
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user