Changing the thread title to reflect the new approach. Extract of the
original thread;

- I suggested using iemlib's hi pass filter recipe to improve
frequency response of [hip~]
- Christof Ressi pointed to formula in
http://www.arpchord.com/pdf/coeffs_first_order_filters_0p1.pdf
- this formula calculates feedback coefficient k = (1 - sin(a)) /
cos(a) where a = 2 * pi * fc / SR
- the filter implementation is y[n] = (x[n] - x[n-1]) * (1 + k) / 2
+   k * y[n-1]
- following convention in d_filter.c (and pd tilde classes in
general), trig functions should best be approximated
- Cyrille provided libre office linear regression result for (1-sin(x))/cos(x)

Thanks for the useful infos and discussion. My 'math coach' suggested
using odd powers of -(x-pi/2) in an approximation polynomial for
(1-sin(x))/cos(x). The best accuracy/performance balance I could get
is with this 5th degree polynomial:

(-(x-pi/2))*0.4908 - (x-pi/2)^3*0.04575 - (x-pi/2)^5*0.00541

Using this approximation in the filter formula, response at cutoff
frequency is -3 dB with +/-0.06 dB accuracy in the required range 0 <
x < pi. It can be efficiently implemented in C, analogous to an
approximation Miller uses in [bp~]. So that is what I'll try next.

Attached patch hip~-models.pd illustrates and compares filter recipes
using vanilla objects:

- current implementation, most efficient, accuracy +/- 3 dB
- implementation with trig functions, least efficient, accuracy +/- 0.01 dB
- implementation with approximation for trig functions, efficient,
accuracy +/- 0.06 dB

A note on efficiency: coefficients in [hip~] are only recalculated
when cutoff frequency is changed. How important is performance for a
function rarely called? I'm much aware of the motto 'never optimize
early', yet I spent much time on finding a fast approximation, for
several reasons: it's a nice math challenge, instructive for cases
where performance matters more, and I want to respect Miller's code
efficiency when proposing a change. Today pd is even deployed on
embedded devices so the frugal coding approach is still relevant.
After 20 years.

Katja


On Tue, Oct 18, 2016 at 10:28 AM, cyrille henry <c...@chnry.net> wrote:
>
>
> Le 18/10/2016 à 00:47, katja a écrit :
>>
>> The filter recipe that Christof pointed to was easy to plug into the C
>> code of [hip~] and works perfectly. But when looking further in
>> d_filter.c I came across an approximation function 'sigbp_qcos()' used
>> in the bandpass filter. It made me realize once more how passionate
>> Miller is about efficiency. I'm not going to make a fool of myself by
>> submitting a 'fix' using two trig functions to calculate a filter
>> coefficient when a simple approximation could do the job. So that is
>> what I'm now looking into, with help of a math friend: an efficient
>> polynomial approximation for (1-sin(x))/cos(x).
>
> according to libre office linear regression, for x between 0 and Pi,
> (1-sin(x))/cos(x) is about :
> -0.057255x³ + 0.27018x² - 0.9157x + 0.99344
>
> the calc is in attachment, if you want to tune the input source or
> precision.
> cheers
> c
#N canvas 56 116 600 483 10;
#X obj 142 161 osc~;
#X msg 501 129 \; pd dsp 1;
#X msg 501 176 \; pd dsp 0;
#X floatatom 142 98 8 0 0 0 - - -, f 8;
#X obj 142 260 env~ 8192;
#X floatatom 142 297 5 0 0 0 - - -, f 5;
#X text 171 161 test signal;
#X text 54 447 Katja Vetter Oct. 2016;
#X text 194 97 Hz;
#X obj 227 260 env~ 8192;
#X floatatom 227 301 5 0 0 0 - - -, f 5;
#X obj 227 331 -;
#X floatatom 227 365 5 0 0 0 - - -, f 5;
#X obj 145 45 hsl 200 15 0 22050 0 0 empty empty frequency(coarse)
20 8 0 10 -262144 -260097 -1 19900 0;
#X text 19 58 edge cases;
#X obj 57 257 env~ 8192;
#X floatatom 57 298 5 0 0 0 - - -, f 5;
#X obj 57 328 -;
#X floatatom 57 362 5 0 0 0 - - -, f 5;
#X obj 359 260 env~ 8192;
#X floatatom 359 301 5 0 0 0 - - -, f 5;
#X obj 359 331 -;
#X floatatom 359 365 5 0 0 0 - - -, f 5;
#X obj 145 16 hsl 200 15 0 1000 0 0 empty empty frequency(fine) 20
8 0 10 -262144 -260097 -1 9000 0;
#N canvas 110 156 413 534 hip1~ 0;
#X obj 40 25 inlet~;
#X obj 40 436 outlet~;
#X floatatom 122 414 8 0 0 0 - - -, f 8;
#X text 177 413 normalisation;
#X obj 40 405 *~ 0;
#X obj 40 345 rpole~;
#X obj 90 56 cnv 15 300 300 empty empty empty 20 12 0 14 -233017 -66577
0;
#X obj 306 126 atan;
#X msg 306 99 1;
#X obj 122 24 inlet;
#X floatatom 306 181 8 0 0 0 - - -, f 8;
#X obj 137 88 samplerate~;
#X obj 122 118 /;
#X obj 306 71 loadbang;
#X obj 122 169 *;
#X floatatom 155 119 8 0 0 0 - - -, f 8;
#X floatatom 152 186 8 0 0 0 - - -, f 8;
#X text 361 181 pi;
#X text 161 23 cut off frequency f(0);
#X obj 40 378 rzero~ 1;
#X obj 306 152 * 8;
#X obj 122 388 / 2;
#X text 204 185 a = 2*pi*fc/SR;
#X obj 122 361 + 1;
#X floatatom 154 330 8 0 0 0 - - -, f 8;
#X obj 122 258 t b f;
#X msg 122 283 1;
#X obj 122 313 -;
#X text 210 328 1-a;
#X text 36 474 Efficient [hip~] as implemented in vanilla pd (d_filter.c).
;
#X obj 122 231 clip 0 1;
#X connect 0 0 5 0;
#X connect 4 0 1 0;
#X connect 5 0 19 0;
#X connect 7 0 20 0;
#X connect 8 0 7 0;
#X connect 9 0 12 0;
#X connect 11 0 12 1;
#X connect 11 0 15 0;
#X connect 12 0 14 0;
#X connect 13 0 11 0;
#X connect 13 0 8 0;
#X connect 14 0 16 0;
#X connect 14 0 30 0;
#X connect 19 0 4 0;
#X connect 20 0 10 0;
#X connect 20 0 14 1;
#X connect 21 0 2 0;
#X connect 21 0 4 1;
#X connect 23 0 21 0;
#X connect 25 0 26 0;
#X connect 25 1 27 1;
#X connect 26 0 27 0;
#X connect 27 0 23 0;
#X connect 27 0 24 0;
#X connect 27 0 5 1;
#X connect 30 0 25 0;
#X restore 57 208 pd hip1~;
#N canvas 547 139 433 582 hip2~ 0;
#X obj 40 25 inlet~;
#X obj 40 453 outlet~;
#X floatatom 121 431 8 0 0 0 - - -, f 8;
#X text 176 430 normalisation;
#X obj 40 422 *~ 0;
#X obj 40 362 rpole~;
#X obj 89 52 cnv 15 300 300 empty empty empty 20 12 0 14 -233017 -66577
0;
#X obj 121 287 /;
#X floatatom 165 304 8 0 0 0 - - -, f 8;
#X obj 306 113 atan;
#X msg 306 89 1;
#X obj 121 24 inlet;
#X floatatom 306 168 8 0 0 0 - - -, f 8;
#X obj 136 78 samplerate~;
#X obj 121 105 /;
#X obj 306 61 loadbang;
#X obj 121 156 *;
#X floatatom 154 106 8 0 0 0 - - -, f 8;
#X floatatom 151 173 8 0 0 0 - - -, f 8;
#X text 160 23 cut off frequency f(0);
#X obj 40 395 rzero~ 1;
#X obj 306 139 * 8;
#X obj 186 230 cos;
#X obj 152 230 sin;
#X obj 121 199 t b f f;
#X obj 121 262 -;
#X msg 121 230 1;
#X obj 121 405 / 2;
#X text 220 302 k = (1-sin(a))/cos(a);
#X text 203 172 a = 2*pi*fc/SR;
#X obj 121 378 + 1;
#X text 35 528 http://www.arpchord.com/pdf/coeffs_first_order_filters_0p1.pdf
;
#X text 36 478 filter recipe adapted from Cliff Sparks \, Coefficients
of Recursive Linear Time-Invariant First-Order Low-Pass and High-Pass
Filters;
#X floatatom 164 279 8 0 0 0 - - -, f 8;
#X text 216 279 1-sin(a);
#X text 357 169 2*pi;
#X obj 121 324 clip -1 1;
#X floatatom 135 357 8 0 0 0 - - -, f 8;
#X connect 0 0 5 0;
#X connect 4 0 1 0;
#X connect 5 0 20 0;
#X connect 7 0 8 0;
#X connect 7 0 36 0;
#X connect 9 0 21 0;
#X connect 10 0 9 0;
#X connect 11 0 14 0;
#X connect 13 0 14 1;
#X connect 13 0 17 0;
#X connect 14 0 16 0;
#X connect 15 0 13 0;
#X connect 15 0 10 0;
#X connect 16 0 18 0;
#X connect 16 0 24 0;
#X connect 20 0 4 0;
#X connect 21 0 12 0;
#X connect 21 0 16 1;
#X connect 22 0 7 1;
#X connect 23 0 25 1;
#X connect 24 0 26 0;
#X connect 24 1 23 0;
#X connect 24 2 22 0;
#X connect 25 0 7 0;
#X connect 25 0 33 0;
#X connect 26 0 25 0;
#X connect 27 0 2 0;
#X connect 27 0 4 1;
#X connect 30 0 27 0;
#X connect 36 0 30 0;
#X connect 36 0 5 1;
#X connect 36 0 37 0;
#X restore 228 211 pd hip2~;
#N canvas 558 130 433 538 hip3~ 0;
#X obj 40 25 inlet~;
#X obj 40 436 outlet~;
#X floatatom 122 414 8 0 0 0 - - -, f 8;
#X text 177 413 normalisation;
#X obj 40 405 *~ 0;
#X obj 40 345 rpole~;
#X obj 90 56 cnv 15 300 300 empty empty empty 20 12 0 14 -233017 -66577
0;
#X obj 306 126 atan;
#X msg 306 99 1;
#X obj 122 24 inlet;
#X floatatom 306 181 8 0 0 0 - - -, f 8;
#X obj 137 88 samplerate~;
#X obj 122 118 /;
#X obj 306 71 loadbang;
#X obj 122 169 *;
#X floatatom 155 119 8 0 0 0 - - -, f 8;
#X floatatom 152 186 8 0 0 0 - - -, f 8;
#X text 361 181 pi;
#X text 161 23 cut off frequency f(0);
#X obj 40 378 rzero~ 1;
#X obj 306 152 * 8;
#X obj 122 388 / 2;
#X text 204 185 a = 2*pi*fc/SR;
#X obj 122 361 + 1;
#X floatatom 142 262 10 0 0 0 - - -, f 10;
#N canvas 235 281 517 393 approx(1-sin(a))/cos(a) 0;
#X obj 47 12 inlet;
#X obj 47 266 outlet;
#X floatatom 61 114 10 0 0 0 - - -, f 10;
#X obj 47 232 clip -1 1;
#X obj 245 29 loadbang;
#X obj 47 141 expr ($f1*$f2)+(pow($f1 \, 3)*$f3)+(pow($f1 \, 5)*$f4)
;
#X text 138 179 (x-pi/2)*a + (x-pi/2)^3*b + (x-pi/2)^5*c;
#X text 196 93 a;
#X text 302 93 b;
#X text 402 93 c;
#X floatatom 61 179 10 0 0 0 - - -, f 10;
#X obj 23 45 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
-1;
#X msg 146 94 -0.4908;
#X msg 245 94 -0.04575;
#X msg 344 95 -0.00541;
#N canvas 1 90 450 300 pi/2 0;
#X obj 48 86 atan;
#X msg 48 59 1;
#X obj 48 31 loadbang;
#X obj 48 148 outlet;
#X floatatom 105 129 8 0 0 0 - - -, f 8;
#X obj 48 112 * 2;
#X text 163 128 pi/2;
#X connect 0 0 5 0;
#X connect 1 0 0 0;
#X connect 2 0 1 0;
#X connect 5 0 3 0;
#X connect 5 0 4 0;
#X restore 62 46 pd pi/2;
#X obj 47 75 -;
#X floatatom 73 77 8 0 0 0 - - -, f 8;
#X text 140 205 Approximation for (1-sin(x))/cos(x). General formula
proposed by Joe Deken. Factors a b c tuned 'by hand' \, Katja Vetter
Oct. 2016;
#X text 45 302 Results would be slightly different in double precision
\, being not exactly 1 for fc=0 and -1 for fc=SR/2. To be sure \, result
-0.999999 should probably be forced to -1 and 0.999999 to 1;
#X connect 0 0 16 0;
#X connect 3 0 1 0;
#X connect 4 0 12 0;
#X connect 4 0 13 0;
#X connect 4 0 14 0;
#X connect 5 0 3 0;
#X connect 5 0 10 0;
#X connect 11 0 16 0;
#X connect 12 0 5 1;
#X connect 13 0 5 2;
#X connect 14 0 5 3;
#X connect 15 0 16 1;
#X connect 15 0 17 0;
#X connect 16 0 5 0;
#X connect 16 0 2 0;
#X restore 122 228 pd approx(1-sin(a))/cos(a);
#X obj 122 297 clip -1 1;
#X floatatom 140 331 8 0 0 0 - - -, f 8;
#X text 195 333 feedback coefficient;
#X text 39 466 Hipass filter recipe using an approximation of (1-sin(a))/cos(a)
for efficient calculation of the coefficient.;
#X connect 0 0 5 0;
#X connect 4 0 1 0;
#X connect 5 0 19 0;
#X connect 7 0 20 0;
#X connect 8 0 7 0;
#X connect 9 0 12 0;
#X connect 11 0 12 1;
#X connect 11 0 15 0;
#X connect 12 0 14 0;
#X connect 13 0 11 0;
#X connect 13 0 8 0;
#X connect 14 0 16 0;
#X connect 14 0 25 0;
#X connect 19 0 4 0;
#X connect 20 0 10 0;
#X connect 20 0 14 1;
#X connect 21 0 2 0;
#X connect 21 0 4 1;
#X connect 23 0 21 0;
#X connect 25 0 24 0;
#X connect 25 0 26 0;
#X connect 26 0 23 0;
#X connect 26 0 5 1;
#X connect 26 0 27 0;
#X restore 359 211 pd hip3~;
#X text 62 231 cheapest;
#X text 233 234 trig functions;
#X text 367 233 approximation;
#X text 92 362 dB gain;
#X text 261 365 dB gain;
#X text 394 364 dB gain;
#X obj 235 77 hsl 200 15 0 22050 0 0 empty empty frequency(coarse)
20 8 0 10 -262144 -260097 -1 2300 1;
#X floatatom 232 101 8 0 0 0 - - -, f 8;
#X msg 22 81 -10;
#X msg 68 81 600000;
#X text 365 31 << cutoff and test signal;
#X text 447 77 << test signal only;
#X text 53 404 Test attenuation at cutoff frequency for different hipass
filter recipes. Output should be -3dB at cutoff.;
#X connect 0 0 4 0;
#X connect 0 0 25 0;
#X connect 0 0 24 0;
#X connect 0 0 26 0;
#X connect 3 0 25 1;
#X connect 3 0 0 0;
#X connect 3 0 24 1;
#X connect 3 0 26 1;
#X connect 4 0 5 0;
#X connect 5 0 11 1;
#X connect 5 0 17 1;
#X connect 5 0 21 1;
#X connect 9 0 10 0;
#X connect 10 0 11 0;
#X connect 11 0 12 0;
#X connect 13 0 3 0;
#X connect 15 0 16 0;
#X connect 16 0 17 0;
#X connect 17 0 18 0;
#X connect 19 0 20 0;
#X connect 20 0 21 0;
#X connect 21 0 22 0;
#X connect 23 0 13 0;
#X connect 24 0 15 0;
#X connect 25 0 9 0;
#X connect 26 0 19 0;
#X connect 33 0 0 0;
#X connect 33 0 34 0;
#X connect 35 0 3 0;
#X connect 36 0 3 0;
_______________________________________________
Pd-list@lists.iem.at mailing list
UNSUBSCRIBE and account-management -> 
https://lists.puredata.info/listinfo/pd-list

Reply via email to