source: cpu/x86/iirfilter.fth @ 1276

Revision 1276, 10.1 KB checked in by wmb, 13 months ago (diff)

IIR Filter - changed input length argument from #samples to /inbuf.

Line 
1purpose: IIR digital upsampling filter
2
3\ This file implements x2 and x3 upsampling filters using techniques
4\ described in chapter 10 of _Multirate Signal Processing for Communications
5\ Systems_ by fredric j harris, Prentice Hall 2004, ISBN 0-13-146511-2
6\ The filters may be cascaded for x6 upsampling (e.g. for 8kHz -> 48kHz)
7
8d# 15 constant mulscale  \ Bits for fractional multipliers
9
100 [if]
11
12\ iir-cascade-z2 implements a folded cascade of multiple sections
13\ of a second-order IIR filter block in a polynomial in Z^2.
14\ This is the basic building block for half-band all-pass
15\ recursive filters, as described in section 10.2 of the book
16\ cited above.   
17\ This code is commented out because we don't use it directly,
18\ instead using the -z1 variant below in a polyphase structure.
19
20code iir-cascade-z2 ( sample weights z #sections -- out )
21   cx pop     \ CX: Number of sections
22
23   bp bx mov  \ Save
24   bp pop     \ BP: Z list
25   si dx mov  \ Save SI
26   si pop     \ SI: Filter weights
27   ax pop     \ AX: current value
28   dx push    \ Saved SI on stack
29   bx push    \ Saved BP on stack
30   begin
31      ax           bx  mov   \ Save last value
32      d# 12 [bp]   ax  sub   \ last - Z[i+1]
33      0 [si]           imul  \ alpha[i] * (last - Z[i+1])  (kills DX)
34      4 [si]       si  lea   \ Increment alpha pointer
35      mulscale #   dx  ax  shrd  \ Scale down by multiplier scale factor
36
37      d#  4 [bp]   ax  add   \ Z[i] + alpha[i] * (last - Z[i+1])
38
39      0 [bp]       dx  mov
40      dx       4 [bp]  mov
41      bx       0 [bp]  mov   \ Update Z[i]
42
43      8 [bp]       bp  lea   \ Point to next Z[i]
44   loopa
45
46   0 [bp]       dx  mov
47   dx       4 [bp]  mov
48   ax       0 [bp]  mov   \ Update Z[i]
49
50   bp pop     \ Restore BP
51   si pop     \ Restore SI
52   ax push    \ Return value
53c;
54[then]
55
56\ iir-cascade-z1 is the same basic structure as iir-cascade-z2,
57\ but the Z^-2 delays have been replaced by Z^-1.  It is used
58\ in the polyphase upsampler "up2".  When the rate is doubled,
59\ a zero sample is inserted between each pair of input samples,
60\ causing every other computation of the Z^-2 polynomial to be
61\ unnecessary.  The basic block can thus be restructured to have
62\ a single delay element, and the block only has to run at half
63\ the rate.  See section 10.6 (figure 10.48) of the book.
64
65code iir-cascade-z1 ( sample weights z #sections -- out )
66   cx pop     \ CX: Number of sections
67
68   bp bx mov  \ Save
69   bp pop     \ BP: Z list
70   si dx mov  \ Save SI
71   si pop     \ SI: Filter weights
72   ax pop     \ AX: current value
73   dx push    \ Saved SI on stack
74   bx push    \ Saved BP on stack
75   begin
76      ax           bx  mov   \ Save last value
77      d#  4 [bp]   ax  sub   \ last - Z[i+1]
78
79      0 [si]           imul  \ alpha[i] * (last - Z[i+1])  (kills DX)
80      4 [si]       si  lea   \ Increment alpha pointer
81      mulscale #  dx  ax  shrd  \ Scale down by multiplier scale factor
82
83      d#  0 [bp]   ax  add   \ Z[i] + alpha[i] * (last - Z[i+1])
84
85      bx       0 [bp]  mov   \ Update Z[i]
86
87      4 [bp]       bp  lea   \ Point to next Z[i]
88   loopa
89   ax          0 [bp]  mov   \ Update Z[i]
90
91   bp pop     \ Restore BP
92   si pop     \ Restore SI
93   ax push    \ Return value
94c;
95
96\ iir-cascade-z2g implements the generalized second-order
97\ low pass filter cascade as shown in section 10.5.1 (figure 10.35)
98\ of the book.  The generalized form allows arbitrary bandwidths
99\ as opposed to the half-band restriction of the simplifed form.
100\ We use this form for 3x upsampling so we can set the lowpass
101\ frequency to Fnyquist/3.
102
103code iir-cascade-z2g  ( sample weights z #sections -- out )
104   cx pop     \ CX: Number of sections
105
106   bp bx mov  \ Save
107   bp pop     \ BP: Z list
108   si dx mov  \ Save SI
109   si pop     \ SI: Filter weights
110   ax pop     \ AX: current value
111   dx push    \ Saved SI on stack
112   bx push    \ Saved BP on stack
113   di push    \ Saved DI on stack
114   begin
115      ax           bx  mov   \ Save last value
116      d# 12 [bp]   ax  sub   \ last - Z[3]
117
118      4 [si]           imul  \ c2 * (last - Z[3])  (kills DX)
119      mulscale #   dx  ax  shrd  \ Scale down by multiplier scale factor
120
121      d#  4 [bp]   ax  add   \ next += Z[1]
122
123      ax           di  mov   \ Save next in di to free up ax
124
125      d#  0 [bp]   ax  mov   \ AX: Z[0]
126      ax   d#  4 [bp]  mov   \ Z[1] = Z[0]
127      bx   d#  0 [bp]  mov   \ Z[0] = sample
128
129      d#  8 [bp]   ax  sub   \ Z[0] - Z[2]
130
131      0 [si]           imul  \ c1 * (Z[0] - Z[2])
132      mulscale #   dx  ax  shrd  \ Scale down by multiplier scale factor
133
134      di           ax  add   \ AX: next
135
136      8 [si]       si  lea   \ Increment coefficient pointer
137      8 [bp]       bp  lea   \ Increment Z pointer
138   loopa
139
140   0 [bp]       dx  mov
141   dx       4 [bp]  mov      \ Z[1] = Z[0]
142   ax       0 [bp]  mov      \ Z[0] = next
143
144   di pop     \ Restore DI
145   bp pop     \ Restore BP
146   si pop     \ Restore SI
147   ax push    \ Return value
148c;
149
150
151\ Convert a decimal fraction to a scaled integer multiplier
152: mul:  ( "coef" -- )
153   safe-parse-word  push-decimal $number drop pop-base  ( n )
154   1 mulscale lshift  d# 1,000,000,000 */                         ( n' )
155   ,
156;
157
158\ Clip too-large sample values to +- 15 bits
159code saturate  ( s -- s' )
160   ax pop
161   d# 32767 # ax cmp  >  if
162      d# 32767 # ax mov
163   else
164      d# -32767 # ax cmp  <  if
165         d# -32767 # ax mov
166      then
167   then
168   ax push
169c;
170
1710 [if]
172: saturate  ( n -- )
173   dup d#  32767 >  if  drop d#  32767  then
174   dup d# -32767 <  if  drop d# -32767  then
175;
176[then]
177
1782 constant #coefs
179
180\ This is for a 9-pole, 9-zero 2-path all-pass halfband IIR filter
181\ From page 278 of the book
182create weights0 mul: .101467517  mul: .612422841   \ Even path
183create weights1 mul: .342095596  mul: .867647439   \ Odd path
184
1850 [if]
186\ This is for a 5-pole, 5-zero 2-path all-pass halfband IIR filter
187\ From page 276 of the book
188create weights2 mul: .141348600   \ Even path
189create weights3 mul: .589994800   \ Odd path
190[then]
191
192\ This is for a 5-pole, 5-zero 2-path all-pass third-band IIR filter
193\ G0 and G1 use iir-cascade-z2g, H1 uses iir-cascade-z1
194\ This is derived from the 5-pole halfband coefficients according
195\ to the formulas on page 291, with fb/fs = 1/6 (fb/fnyquist = 1/3).
196\ The book has a missing sign somewhere, because without the minus
197\ signs on c1 and b, you end up with the corner frequency at 2/6
198\ instead of 1/6.  I had to figure that out the hard way, by trial
199\ and error (looking at spectra of impulse responses).  If you
200\ replace b with -b in equation 10.25, the c1's work out automatically.
201
202\               c1                c2
203create coefg0   mul: -.605502011  mul: .211004022    \ Even path G0
204create coefg1   mul: -.817448745  mul: .634897489    \ Odd path G1
205\               b
206create coefh1   mul: -.267949192
207
208
2092 1+ /n* constant buflen0  \ 2 stages of Z^-1 plus final feedback
210
211buflen0 buffer: z0  \ First upsampler even path delay buffer
212buflen0 buffer: z1  \ First upsampler odd path delay buffer
213
2141 1+ 2* /n* constant buflen1  \ 1 stage of Z^-2 plus final feedback
215
216buflen1 buffer: z2  \ 3x upsampler even path G0 delay buffer
217buflen1 buffer: z3  \ 3x upsampler odd path G1 delay buffer
218buflen1 buffer: z4  \ 3x upsampler odd path H1 delay buffer
219
220: init-upsample  ( -- )
221   z0 buflen0 erase
222   z1 buflen0 erase
223   z2 buflen1 erase
224   z3 buflen1 erase
225   z4 buflen1 erase
226;
227
228\ This is a polyphase decomposition of upsampling by 2
229\ The filter prototype for each path is a polynomial in Z^2,
230\ but when you apply the polyphase transform with Nobel's identity,
231\ the paths split and the output summation becomes a commutation.
232: up2  ( sample -- out1 out0 )
233   dup weights0 z0 2 iir-cascade-z1 saturate >r
234       weights1 z1 2 iir-cascade-z1 saturate r>
235;
236
2370 [if]
2380 value lastsample
239
240\ This is the base rate (non-polyphase) version of up2
241\ Both paths run for each sample, with Z^-1 between the
242\ paths, summing the paths to get a lowpass output.
243: filter2  ( sample -- out )
244   lastsample weights1 z3 2 iir-cascade-z2  >r  ( sample r: out1 )
245   to lastsample
246   lastsample weights0 z2 2 iir-cascade-z2  r>  ( out0 out1 )
247   + saturate                                   ( out )
248;
249[then]
250
251\ Third-band interpolation filter.  You have to run this once
252\ for each output sample, with 0-stuffing between input samples.
253\ It would be nice to polyphase this, but doing so requires a
254\ complex design methodology for the IIR filter sections that
255\ I haven't mastered.
256: interp3  ( sample -- out )
257   dup  coefg0 z2 1 iir-cascade-z2g     ( sample out0 )
258   swap coefh1 z4 1 iir-cascade-z1      ( out0 outh )
259        coefg1 z3 1 iir-cascade-z2g     ( out0 out1 )
260   + saturate                           ( )
261;
262
263\ Upsample by a factor of 3
264: up3  ( sample -- out2 out1 out0 )
265   interp3 >r  0 interp3 >r  0 interp3  r> r>
266;
267
2680 value sample-stride
269variable sample-outp
270: out,  ( value -- )
271   sample-outp @ w!  sample-stride  sample-outp +!
272;
273
274\ Stride is 2 for mono, 4 for stereo
275\ For stereo you must call it twice with an offset of 2 for
276\ inbuf and outbuf on the second call
277: 8khz>48khz  ( inbuf /inbuf outbuf stride -- )
278   to sample-stride         ( inbuf /inbuf outbuf )
279   sample-outp !            ( inbuf /inbuf )
280   init-upsample            ( inbuf /inbuf )
281   bounds  ?do              ( )
282      i <w@                 ( sample )
283      up2                   ( s1 s0 )
284      up3 out, out, out,    ( s1 )
285      up3 out, out, out,    ( )
286   sample-stride +loop      ( )
287;
288
289: 16khz>48khz  ( inbuf /inbuf outbuf stride -- )
290   to sample-stride         ( inbuf /inbuf outbuf )
291   sample-outp !            ( inbuf /inbuf )
292   init-upsample            ( inbuf /inbuf )
293   bounds  ?do              ( )
294      i <w@                 ( sample )
295      up3 out, out, out,    ( )
296   sample-stride +loop      ( )
297;
298
2990 [if]
300\ Here is some Matlab/Octave code to do the "coefg.." coefficient
301\ transformations
302function retval = c1(a, b)
303   retval = 2 * b * (1 + a) / (1 + a * b * b);
304end
305function retval = c2(a, b)
306   retval = ((b * b) + a) / (1 + a * b * b);
307end
308function retval = cvtb(frac)
309   retval = (1 - tan(pi * frac)) / (1 + tan(pi * frac));
310end
311function cvtfilt (bw) % bw is fb/fs
312  a0 = .141348600
313  a1 = .589994800
314
315  b = cvtb(bw)
316  c10 = c1(a0, b)
317  c20 = c2(a0, b)
318  c11 = c1(a1, b)
319  c21 = c2(a1, b)
320end
321cvtfilt(1/6);
322[then]
Note: See TracBrowser for help on using the repository browser.