1 /* Calf DSP Library
2  * Biquad filters
3  * Copyright (C) 2001-2007 Krzysztof Foltman
4  *
5  * Most of code in this file is based on freely
6  * available other work of other people (filter equations).
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General
19  * Public License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA  02110-1301  USA
22  */
23 #ifndef __CALF_BIQUAD_H
24 #define __CALF_BIQUAD_H
25 
26 #include <complex>
27 #include "primitives.h"
28 
29 namespace dsp {
30 
31 /**
32  * Coefficients for two-pole two-zero filter, for floating point values,
33  * plus a bunch of functions to set them to typical values.
34  *
35  * Coefficient calculation is based on famous Robert Bristow-Johnson's equations,
36  * except where it's not.
37  * The coefficient calculation is NOT mine, the only exception is the lossy
38  * optimization in Zoelzer and rbj HP filter code.
39  *
40  * See http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt for reference.
41  *
42  * don't use this for integers because it won't work
43  */
44 template<class Coeff = float>
45 class biquad_coeffs
46 {
47 public:
48     // filter coefficients
49     Coeff a0, a1, a2, b1, b2;
50     typedef std::complex<double> cfloat;
51 
biquad_coeffs()52     biquad_coeffs()
53     {
54         set_null();
55     }
56 
set_null()57     inline void set_null()
58     {
59         a0 = 1.0;
60         b1 = b2 = a1 = a2 = 0.f;
61     }
62 
63     /** Lowpass filter based on Robert Bristow-Johnson's equations
64      * Perhaps every synth code that doesn't use SVF uses these
65      * equations :)
66      * @param fc     resonant frequency
67      * @param q      resonance (gain at fc)
68      * @param sr     sample rate
69      * @param gain   amplification (gain at 0Hz)
70      */
71     inline void set_lp_rbj(float fc, float q, float sr, float gain = 1.0)
72     {
73         float omega=(float)(2*M_PI*fc/sr);
74         float sn=sin(omega);
75         float cs=cos(omega);
76         float alpha=(float)(sn/(2*q));
77         float inv=(float)(1.0/(1.0+alpha));
78 
79         a2 = a0 =  (float)(gain*inv*(1 - cs)*0.5f);
80         a1 =  a0 + a0;
81         b1 =  (float)(-2*cs*inv);
82         b2 =  (float)((1 - alpha)*inv);
83     }
84 
85     // different lowpass filter, based on Zoelzer's equations, modified by
86     // me (kfoltman) to use polynomials to approximate tangent function
87     // not very accurate, but perhaps good enough for synth work :)
88     // odsr is "one divided by samplerate"
89     // from how it looks, it perhaps uses bilinear transform - but who knows :)
90     inline void set_lp_zoelzer(float fc, float q, float odsr, float gain=1.0)
91     {
92         Coeff omega=(Coeff)(M_PI*fc*odsr);
93         Coeff omega2=omega*omega;
94         Coeff K=omega*(1+omega2*omega2*Coeff(1.0/1.45));
95         Coeff KK=K*K;
96         Coeff QK=q*(KK+1.f);
97         Coeff iQK=1.0f/(QK+K);
98         Coeff inv=q*iQK;
99         b2 =  (Coeff)(iQK*(QK-K));
100         b1 =  (Coeff)(2.f*(KK-1.f)*inv);
101         a2 = a0 =  (Coeff)(inv*gain*KK);
102         a1 =  a0 + a0;
103     }
104 
105     /** Highpass filter based on Robert Bristow-Johnson's equations
106      * @param fc     resonant frequency
107      * @param q      resonance (gain at fc)
108      * @param sr     sample rate
109      * @param gain   amplification (gain at sr/2)
110      */
111     inline void set_hp_rbj(float fc, float q, float esr, float gain=1.0)
112     {
113         Coeff omega=(float)(2*M_PI*fc/esr);
114         Coeff sn=sin(omega);
115         Coeff cs=cos(omega);
116         Coeff alpha=(float)(sn/(2*q));
117 
118         float inv=(float)(1.0/(1.0+alpha));
119 
120         a0 =  (Coeff)(gain*inv*(1 + cs)/2);
121         a1 =  -2.f * a0;
122         a2 =  a0;
123         b1 =  (Coeff)(-2*cs*inv);
124         b2 =  (Coeff)((1 - alpha)*inv);
125     }
126 
127     // this replaces sin/cos with polynomial approximation
128     inline void set_hp_rbj_optimized(float fc, float q, float esr, float gain=1.0)
129     {
130         Coeff omega=(float)(2*M_PI*fc/esr);
131         Coeff sn=omega+omega*omega*omega*(1.0/6.0)+omega*omega*omega*omega*omega*(1.0/120);
132         Coeff cs=1-omega*omega*(1.0/2.0)+omega*omega*omega*omega*(1.0/24);
133         Coeff alpha=(float)(sn/(2*q));
134 
135         float inv=(float)(1.0/(1.0+alpha));
136 
137         a0 =  (Coeff)(gain*inv*(1 + cs)*(1.0/2.0));
138         a1 =  -2.f * a0;
139         a2 =  a0;
140         b1 =  (Coeff)(-2*cs*inv);
141         b2 =  (Coeff)((1 - alpha)*inv);
142     }
143 
144     /** Bandpass filter based on Robert Bristow-Johnson's equations (normalized to 1.0 at center frequency)
145      * @param fc     center frequency (gain at fc = 1.0)
146      * @param q      =~ fc/bandwidth (not quite, but close)  - 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0))
147      * @param sr     sample rate
148      * @param gain   amplification (gain at sr/2)
149      */
150     inline void set_bp_rbj(double fc, double q, double esr, double gain=1.0)
151     {
152         float omega=(float)(2*M_PI*fc/esr);
153         float sn=sin(omega);
154         float cs=cos(omega);
155         float alpha=(float)(sn/(2*q));
156 
157         float inv=(float)(1.0/(1.0+alpha));
158 
159         a0 =  (float)(gain*inv*alpha);
160         a1 =  0.f;
161         a2 =  (float)(-gain*inv*alpha);
162         b1 =  (float)(-2*cs*inv);
163         b2 =  (float)((1 - alpha)*inv);
164     }
165 
166     // rbj's bandreject
167     inline void set_br_rbj(double fc, double q, double esr, double gain=1.0)
168     {
169         float omega=(float)(2*M_PI*fc/esr);
170         float sn=sin(omega);
171         float cs=cos(omega);
172         float alpha=(float)(sn/(2*q));
173 
174         float inv=(float)(1.0/(1.0+alpha));
175 
176         a0 =  (Coeff)(gain*inv);
177         a1 =  (Coeff)(-gain*inv*2*cs);
178         a2 =  (Coeff)(gain*inv);
179         b1 =  (Coeff)(-2*cs*inv);
180         b2 =  (Coeff)((1 - alpha)*inv);
181     }
182     // this is mine (and, I guess, it sucks/doesn't work)
set_allpass(float freq,float pole_r,float sr)183     void set_allpass(float freq, float pole_r, float sr)
184     {
185         float a=prewarp(freq, sr);
186         float q=pole_r;
187         set_bilinear(a*a+q*q, -2.0f*a, 1, a*a+q*q, 2.0f*a, 1);
188     }
189     /// prewarping for bilinear transform, maps given digital frequency to analog counterpart for analog filter design
prewarp(float freq,float sr)190     static inline float prewarp(float freq, float sr)
191     {
192         if (freq>sr*0.49) freq=(float)(sr*0.49);
193         return (float)(tan(M_PI*freq/sr));
194     }
195     /// convert analog angular frequency value to digital
unwarp(float omega,float sr)196     static inline float unwarp(float omega, float sr)
197     {
198         float T = 1.0 / sr;
199         return (2 / T) * atan(omega * T / 2);
200     }
201     /// convert analog filter time constant to digital counterpart
unwarpf(float t,float sr)202     static inline float unwarpf(float t, float sr)
203     {
204         // this is most likely broken and works by pure accident!
205         float omega = 1.0 / t;
206         omega = unwarp(omega, sr);
207         // I really don't know why does it have to be M_PI and not 2 * M_PI!
208         float f = M_PI / omega;
209         return f / sr;
210     }
211     /// set digital filter parameters based on given analog filter parameters
set_bilinear(float aa0,float aa1,float aa2,float ab0,float ab1,float ab2)212     void set_bilinear(float aa0, float aa1, float aa2, float ab0, float ab1, float ab2)
213     {
214         float q=(float)(1.0/(ab0+ab1+ab2));
215         a0 = (aa0+aa1+aa2)*q;
216         a1 = 2*(aa0-aa2)*q;
217         a2 = (aa0-aa1+aa2)*q;
218         b1 = 2*(ab0-ab2)*q;
219         b2 = (ab0-ab1+ab2)*q;
220     }
221 
222     /// RBJ peaking EQ
223     /// @param freq   peak frequency
224     /// @param q      q (correlated to freq/bandwidth, @see set_bp_rbj)
225     /// @param peak   peak gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
set_peakeq_rbj(float freq,float q,float peak,float sr)226     inline void set_peakeq_rbj(float freq, float q, float peak, float sr)
227     {
228         float A = sqrt(peak);
229         float w0 = freq * 2 * M_PI * (1.0 / sr);
230         float alpha = sin(w0) / (2 * q);
231         float ib0 = 1.0 / (1 + alpha/A);
232         a1 = b1 = -2*cos(w0) * ib0;
233         a0 = ib0 * (1 + alpha*A);
234         a2 = ib0 * (1 - alpha*A);
235         b2 = ib0 * (1 - alpha/A);
236     }
237 
238     /// RBJ low shelf EQ - amplitication of 'peak' at 0 Hz and of 1.0 (0dB) at sr/2 Hz
239     /// @param freq   corner frequency (gain at freq is sqrt(peak))
240     /// @param q      q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated below fc) is
241     /// @param peak   shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
set_lowshelf_rbj(float freq,float q,float peak,float sr)242     inline void set_lowshelf_rbj(float freq, float q, float peak, float sr)
243     {
244         float A = sqrt(peak);
245         float w0 = freq * 2 * M_PI * (1.0 / sr);
246         float alpha = sin(w0) / (2 * q);
247         float cw0 = cos(w0);
248         float tmp = 2 * sqrt(A) * alpha;
249         float b0 = 0.f, ib0 = 0.f;
250 
251         a0 =    A*( (A+1) - (A-1)*cw0 + tmp);
252         a1 =  2*A*( (A-1) - (A+1)*cw0);
253         a2 =    A*( (A+1) - (A-1)*cw0 - tmp);
254         b0 =        (A+1) + (A-1)*cw0 + tmp;
255         b1 =   -2*( (A-1) + (A+1)*cw0);
256         b2 =        (A+1) + (A-1)*cw0 - tmp;
257 
258         ib0 = 1.0 / b0;
259         b1 *= ib0;
260         b2 *= ib0;
261         a0 *= ib0;
262         a1 *= ib0;
263         a2 *= ib0;
264     }
265 
266     /// RBJ high shelf EQ - amplitication of 0dB at 0 Hz and of peak at sr/2 Hz
267     /// @param freq   corner frequency (gain at freq is sqrt(peak))
268     /// @param q      q (relates bandwidth and peak frequency), the higher q, the louder the resonant peak (situated above fc) is
269     /// @param peak   shelf gain (1.0 means no peak, >1.0 means a peak, less than 1.0 is a dip)
set_highshelf_rbj(float freq,float q,float peak,float sr)270     inline void set_highshelf_rbj(float freq, float q, float peak, float sr)
271     {
272         float A = sqrt(peak);
273         float w0 = freq * 2 * M_PI * (1.0 / sr);
274         float alpha = sin(w0) / (2 * q);
275         float cw0 = cos(w0);
276         float tmp = 2 * sqrt(A) * alpha;
277         float b0 = 0.f, ib0 = 0.f;
278 
279         a0 =    A*( (A+1) + (A-1)*cw0 + tmp);
280         a1 = -2*A*( (A-1) + (A+1)*cw0);
281         a2 =    A*( (A+1) + (A-1)*cw0 - tmp);
282         b0 =        (A+1) - (A-1)*cw0 + tmp;
283         b1 =    2*( (A-1) - (A+1)*cw0);
284         b2 =        (A+1) - (A-1)*cw0 - tmp;
285 
286         ib0 = 1.0 / b0;
287         b1 *= ib0;
288         b2 *= ib0;
289         a0 *= ib0;
290         a1 *= ib0;
291         a2 *= ib0;
292     }
293 
294     /// copy coefficients from another biquad
295     template<class U>
copy_coeffs(const biquad_coeffs<U> & src)296     inline void copy_coeffs(const biquad_coeffs<U> &src)
297     {
298         a0 = src.a0;
299         a1 = src.a1;
300         a2 = src.a2;
301         b1 = src.b1;
302         b2 = src.b2;
303     }
304 
305     /// Return the filter's gain at frequency freq
306     /// @param freq   Frequency to look up
307     /// @param sr     Filter sample rate (used to convert frequency to angular frequency)
freq_gain(float freq,float sr)308     float freq_gain(float freq, float sr) const
309     {
310         typedef std::complex<double> cfloat;
311         freq *= 2.0 * M_PI / sr;
312         cfloat z = 1.0 / exp(cfloat(0.0, freq));
313 
314         return std::abs(h_z(z));
315     }
316 
317     /// Return H(z) the filter's gain at frequency freq
318     /// @param z   Z variable (e^jw)
h_z(const cfloat & z)319     cfloat h_z(const cfloat &z) const
320     {
321 
322         return (cfloat(a0) + double(a1) * z + double(a2) * z*z) / (cfloat(1.0) + double(b1) * z + double(b2) * z*z);
323     }
324 
325 };
326 
327 /**
328  * Two-pole two-zero filter, for floating point values.
329  * Uses "traditional" Direct I form (separate FIR and IIR halves).
330  * don't use this for integers because it won't work
331  */
332 template<class Coeff = float, class T = float>
333 struct biquad_d1: public biquad_coeffs<Coeff>
334 {
335     using biquad_coeffs<Coeff>::a0;
336     using biquad_coeffs<Coeff>::a1;
337     using biquad_coeffs<Coeff>::a2;
338     using biquad_coeffs<Coeff>::b1;
339     using biquad_coeffs<Coeff>::b2;
340     /// input[n-1]
341     T x1;
342     /// input[n-2]
343     T x2;
344     /// output[n-1]
345     T y1;
346     /// output[n-2]
347     T y2;
348     /// Constructor (initializes state to all zeros)
biquad_d1biquad_d1349     biquad_d1()
350     {
351         reset();
352     }
353     /// direct I form with four state variables
processbiquad_d1354     inline T process(T in)
355     {
356         T out = in * a0 + x1 * a1 + x2 * a2 - y1 * b1 - y2 * b2;
357         x2 = x1;
358         y2 = y1;
359         x1 = in;
360         y1 = out;
361         return out;
362     }
363 
364     /// direct I form with zero input
process_zeroinbiquad_d1365     inline T process_zeroin()
366     {
367         T out = - y1 * b1 - y2 * b2;
368         y2 = y1;
369         y1 = out;
370         return out;
371     }
372 
373     /// simplified version for lowpass case with two zeros at -1
process_lpbiquad_d1374     inline T process_lp(T in)
375     {
376         T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
377         x2 = x1;
378         y2 = y1;
379         x1 = in;
380         y1 = out;
381         return out;
382     }
383     /// Sanitize (set to 0 if potentially denormal) filter state
sanitizebiquad_d1384     inline void sanitize()
385     {
386         dsp::sanitize(x1);
387         dsp::sanitize(y1);
388         dsp::sanitize(x2);
389         dsp::sanitize(y2);
390     }
391     /// Reset state variables
resetbiquad_d1392     inline void reset()
393     {
394         dsp::zero(x1);
395         dsp::zero(y1);
396         dsp::zero(x2);
397         dsp::zero(y2);
398     }
emptybiquad_d1399     inline bool empty() const {
400         return (y1 == 0.f && y2 == 0.f);
401     }
402 
403 };
404 
405 /**
406  * Two-pole two-zero filter, for floating point values.
407  * Uses slightly faster Direct II form (combined FIR and IIR halves).
408  * However, when used with wildly varying coefficients, it may
409  * make more zipper noise than Direct I form, so it's better to
410  * use it when filter coefficients are not changed mid-stream.
411  */
412 template<class Coeff = float, class T = float>
413 struct biquad_d2: public biquad_coeffs<Coeff>
414 {
415     using biquad_coeffs<Coeff>::a0;
416     using biquad_coeffs<Coeff>::a1;
417     using biquad_coeffs<Coeff>::a2;
418     using biquad_coeffs<Coeff>::b1;
419     using biquad_coeffs<Coeff>::b2;
420     /// state[n-1]
421     float w1;
422     /// state[n-2]
423     float w2;
424     /// Constructor (initializes state to all zeros)
biquad_d2biquad_d2425     biquad_d2()
426     {
427         reset();
428     }
429     /// direct II form with two state variables
processbiquad_d2430     inline T process(T in)
431     {
432         dsp::sanitize_denormal(in);
433         dsp::sanitize(in);
434         dsp::sanitize(w1);
435         dsp::sanitize(w2);
436 
437         T tmp = in - w1 * b1 - w2 * b2;
438         T out = tmp * a0 + w1 * a1 + w2 * a2;
439         w2 = w1;
440         w1 = tmp;
441         return out;
442     }
443 
444     // direct II form with two state variables, lowpass version
445     // interesting fact: this is actually slower than the general version!
process_lpbiquad_d2446     inline T process_lp(T in)
447     {
448         T tmp = in - w1 * b1 - w2 * b2;
449         T out = (tmp  + w2 + w1* 2) * a0;
450         w2 = w1;
451         w1 = tmp;
452         return out;
453     }
454 
455     /// Is the filter state completely silent? (i.e. set to 0 by sanitize function)
emptybiquad_d2456     inline bool empty() const {
457         return (w1 == 0.f && w2 == 0.f);
458     }
459 
460 
461     /// Sanitize (set to 0 if potentially denormal) filter state
sanitizebiquad_d2462     inline void sanitize()
463     {
464         dsp::sanitize(w1);
465         dsp::sanitize(w2);
466     }
467 
468     /// Reset state variables
resetbiquad_d2469     inline void reset()
470     {
471         dsp::zero(w1);
472         dsp::zero(w2);
473     }
474 };
475 
476 /**
477  * Two-pole two-zero filter, for floating point values.
478  * Uses "traditional" Direct I form (separate FIR and IIR halves).
479  * don't use this for integers because it won't work
480  */
481 template<class Coeff = float, class T = float>
482 struct biquad_d1_lerp: public biquad_coeffs<Coeff>
483 {
484     using biquad_coeffs<Coeff>::a0;
485     using biquad_coeffs<Coeff>::a1;
486     using biquad_coeffs<Coeff>::a2;
487     using biquad_coeffs<Coeff>::b1;
488     using biquad_coeffs<Coeff>::b2;
489     Coeff a0cur, a1cur, a2cur, b1cur, b2cur;
490     Coeff a0delta, a1delta, a2delta, b1delta, b2delta;
491     /// input[n-1]
492     T x1;
493     /// input[n-2]
494     T x2;
495     /// output[n-1]
496     T y1;
497     /// output[n-2]
498     T y2;
499     /// Constructor (initializes state to all zeros)
biquad_d1_lerpbiquad_d1_lerp500     biquad_d1_lerp()
501     {
502         reset();
503     }
504     #define _DO_COEFF(coeff) coeff##delta = (coeff - coeff##cur) * (frac)
big_stepbiquad_d1_lerp505     void big_step(Coeff frac)
506     {
507         _DO_COEFF(a0);
508         _DO_COEFF(a1);
509         _DO_COEFF(a2);
510         _DO_COEFF(b1);
511         _DO_COEFF(b2);
512     }
513     #undef _DO_COEFF
514     /// direct I form with four state variables
processbiquad_d1_lerp515     inline T process(T in)
516     {
517         T out = in * a0cur + x1 * a1cur + x2 * a2cur - y1 * b1cur - y2 * b2cur;
518         x2 = x1;
519         y2 = y1;
520         x1 = in;
521         y1 = out;
522         a0cur += a0delta;
523         a1cur += a1delta;
524         a2cur += a2delta;
525         b1cur += b1delta;
526         b2cur += b2delta;
527         return out;
528     }
529 
530     /// direct I form with zero input
process_zeroinbiquad_d1_lerp531     inline T process_zeroin()
532     {
533         T out = - y1 * b1 - y2 * b2;
534         y2 = y1;
535         y1 = out;
536         b1cur += b1delta;
537         b2cur += b2delta;
538         return out;
539     }
540 
541     /// simplified version for lowpass case with two zeros at -1
process_lpbiquad_d1_lerp542     inline T process_lp(T in)
543     {
544         T out = a0*(in + x1 + x1 + x2) - y1 * b1 - y2 * b2;
545         x2 = x1;
546         y2 = y1;
547         x1 = in;
548         y1 = out;
549         return out;
550     }
551     /// Sanitize (set to 0 if potentially denormal) filter state
sanitizebiquad_d1_lerp552     inline void sanitize()
553     {
554         dsp::sanitize(x1);
555         dsp::sanitize(y1);
556         dsp::sanitize(x2);
557         dsp::sanitize(y2);
558         dsp::sanitize(a0cur);
559         dsp::sanitize(a1cur);
560         dsp::sanitize(a2cur);
561         dsp::sanitize(b1cur);
562         dsp::sanitize(b2cur);
563     }
564     /// Reset state variables
resetbiquad_d1_lerp565     inline void reset()
566     {
567         dsp::zero(x1);
568         dsp::zero(y1);
569         dsp::zero(x2);
570         dsp::zero(y2);
571         dsp::zero(a0cur);
572         dsp::zero(a1cur);
573         dsp::zero(a2cur);
574         dsp::zero(b1cur);
575         dsp::zero(b2cur);
576     }
emptybiquad_d1_lerp577     inline bool empty() {
578         return (y1 == 0.f && y2 == 0.f);
579     }
580 
581 };
582 
583 /// Compose two filters in series
584 template<class F1, class F2>
585 class filter_compose {
586 public:
587     typedef std::complex<float> cfloat;
588     F1 f1;
589     F2 f2;
590 public:
process(float value)591     float process(float value) {
592         return f2.process(f1.process(value));
593     }
594 
h_z(const cfloat & z)595     inline cfloat h_z(const cfloat &z) const {
596         return f1.h_z(z) * f2.h_z(z);
597     }
598 
599     /// Return the filter's gain at frequency freq
600     /// @param freq   Frequency to look up
601     /// @param sr     Filter sample rate (used to convert frequency to angular frequency)
freq_gain(float freq,float sr)602     float freq_gain(float freq, float sr) const
603     {
604         typedef std::complex<double> cfloat;
605         freq *= 2.0 * M_PI / sr;
606         cfloat z = 1.0 / exp(cfloat(0.0, freq));
607 
608         return std::abs(h_z(z));
609     }
610 
sanitize()611     void sanitize() {
612         f1.sanitize();
613         f2.sanitize();
614     }
615 };
616 
617 /// Compose two filters in parallel
618 template<class F1, class F2>
619 class filter_sum {
620 public:
621     typedef std::complex<double> cfloat;
622     F1 f1;
623     F2 f2;
624 public:
process(float value)625     float process(float value) {
626         return f2.process(value) + f1.process(value);
627     }
628 
h_z(const cfloat & z)629     inline cfloat h_z(const cfloat &z) const {
630         return f1.h_z(z) + f2.h_z(z);
631     }
632 
633     /// Return the filter's gain at frequency freq
634     /// @param freq   Frequency to look up
635     /// @param sr     Filter sample rate (used to convert frequency to angular frequency)
freq_gain(float freq,float sr)636     float freq_gain(float freq, float sr) const
637     {
638         typedef std::complex<double> cfloat;
639         freq *= 2.0 * M_PI / sr;
640         cfloat z = 1.0 / exp(cfloat(0.0, freq));
641 
642         return std::abs(h_z(z));
643     }
644 
sanitize()645     void sanitize() {
646         f1.sanitize();
647         f2.sanitize();
648     }
649 };
650 
651 };
652 
653 #endif
654