1 /********************************************************************
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
9 * by the XIPHOPHORUS Company http://www.xiph.org/ *
11 ********************************************************************
13 function: Direct Form II IIR filters, plus some specializations
14 last mod: $Id: iir.c,v 1.10 2001/02/26 03:50:41 xiphmont Exp $
16 ********************************************************************/
18 /* LPC is actually a degenerate case of form I/II filters, but we need
27 void IIR_init(IIR_state *s,int stages,float gain, float *A, float *B){
28 memset(s,0,sizeof(IIR_state));
31 s->coeff_A=_ogg_malloc(stages*sizeof(float));
32 s->coeff_B=_ogg_malloc((stages+1)*sizeof(float));
33 s->z_A=_ogg_calloc(stages*2,sizeof(float));
35 memcpy(s->coeff_A,A,stages*sizeof(float));
36 memcpy(s->coeff_B,B,(stages+1)*sizeof(float));
39 void IIR_clear(IIR_state *s){
41 _ogg_free(s->coeff_A);
42 _ogg_free(s->coeff_B);
44 memset(s,0,sizeof(IIR_state));
48 void IIR_reset(IIR_state *s){
49 memset(s->z_A,0,sizeof(float)*s->stages*2);
52 float IIR_filter(IIR_state *s,float in){
53 int stages=s->stages,i;
54 float newA= in*s->gain;
56 float *zA=s->z_A+s->ring;
58 for(i=0;i<stages;i++){
59 newA+= s->coeff_A[i] * zA[i];
60 newB+= s->coeff_B[i] * zA[i];
62 newB+=newA*s->coeff_B[stages];
64 zA[0]=zA[stages]=newA;
65 if(++s->ring>=stages)s->ring=0;
69 /* this assumes the symmetrical structure of the feed-forward stage of
70 a typical bandpass to save multiplies */
71 float IIR_filter_Band(IIR_state *s,float in){
72 int stages=s->stages,i;
73 int stages2=stages>>1;
74 float newA= in*s->gain;
76 float *zA=s->z_A+s->ring;
78 newA+= s->coeff_A[0] * zA[0];
79 for(i=1;i<stages2;i++){
80 newA+= s->coeff_A[i] * zA[i];
81 newB+= s->coeff_B[i] * (zA[i]-zA[stages-i]);
83 newB+= s->coeff_B[i] * zA[i];
85 newA+= s->coeff_A[i] * zA[i];
89 zA[0]=zA[stages]=newA;
90 if(++s->ring>=stages)s->ring=0;
96 /* z^-stage, z^-stage+1... */
97 static float cheb_bandpass_B[]={-1.f,0.f,5.f,0.f,-10.f,0.f,10.f,0.f,-5.f,0.f,1f};
98 static float cheb_bandpass_A[]={-0.6665900311f,
109 static float data[128]={
239 /* comparison test code from http://www-users.cs.york.ac.uk/~fisher/mkfilter/
240 (the above page kicks ass, BTW)*/
244 #define GAIN 4.599477515e+02f
246 static float xv[NZEROS+1], yv[NPOLES+1];
248 static float filterloop(float next){
249 xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5];
250 xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8]; xv[8] = xv[9]; xv[9] = xv[10];
251 xv[10] = next / GAIN;
252 yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5];
253 yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8]; yv[8] = yv[9]; yv[9] = yv[10];
254 yv[10] = (xv[10] - xv[0]) + 5 * (xv[2] - xv[8]) + 10 * (xv[6] - xv[4])
255 + ( -0.6665900311f * yv[0]) + ( 1.0070146601f * yv[1])
256 + ( -3.1262875409f * yv[2]) + ( 3.5017171569f * yv[3])
257 + ( -6.2779211945f * yv[4]) + ( 5.2966481740f * yv[5])
258 + ( -6.7570216587f * yv[6]) + ( 4.0760335768f * yv[7])
259 + ( -3.9134284363f * yv[8]) + ( 1.3997338886f * yv[9]);
266 /* run the pregenerated Chebyshev filter, then our own distillation
267 through the generic and specialized code */
268 float *work=_ogg_malloc(128*sizeof(float));
272 for(i=0;i<128;i++)work[i]=filterloop(data[i]);
274 FILE *out=fopen("IIR_ref.m","w");
275 for(i=0;i<128;i++)fprintf(out,"%g\n",work[i]);
279 IIR_init(&iir,NPOLES,GAIN,cheb_bandpass_A,cheb_bandpass_B);
280 for(i=0;i<128;i++)work[i]=IIR_filter(&iir,data[i]);
282 FILE *out=fopen("IIR_gen.m","w");
283 for(i=0;i<128;i++)fprintf(out,"%g\n",work[i]);
288 IIR_init(&iir,NPOLES,GAIN,cheb_bandpass_A,cheb_bandpass_B);
289 for(i=0;i<128;i++)work[i]=IIR_filter_ChebBand(&iir,data[i]);
291 FILE *out=fopen("IIR_cheb.m","w");
292 for(i=0;i<128;i++)fprintf(out,"%g\n",work[i]);