Dear Julius,

thank you for your input!

My filter theory background is nearly non-existent (for now!) so I try to catch 
up as best I can, this is very helpful indeed!

My aim here is more to build something that is musically similar and 
approximates the analog design relatively close while maintaining numerical 
stability when quickly changing parameters and input. 
Based on your comments, I would have thought that the normalized-ladder form of 
the filter would be a better choice for this purpose, however I found that it 
blows up with a corner frequency >= 15278 (at a constant SR of 48k).

Next step will be to adapt my multimode filter design to incorporate your `lp1` 
filters and test their behaviour...

Below my partial rework of your sketch (mainly renaming of variables to make 
them more explicit for me to understand), possibly helpful for others on this 
list, too :)

all the best
        Till

```
//////////////////////////////////////////
import("stdfaust.lib");

// Frequency-scaling parameter for real-time bilinear form = corner frequency:

// direct form 1st order implemented with filling 
// appropriate params into 2nd order direct form and setting a0 = 0. 
// JOS: does not optimise properly on faust-level (possibly C compiler does?)
// JOS: needs oversampling for numerical stability
lp1_direct(freq) = fi.tf2snp( b2, b1, b0, a1, a0, w1(freq) ) with {
    // freq argument: corner frequency (-3dB drop-off)
    w1(fc) = 2 * ma.PI * max( fc, 20 );

    g = 1.0;   // dc gain

    // Analog zero at infinity:
    b2 = 0;  b1 = g;  b0 = 0;

    // Analog poles in normalized form (a2=1 implicitly):
    a1 = 1.0;
    a0 = 0; // Optimization should remove anything associated with this

};

// normalized-ladder 
// JOS: digital counterpart for musical use at normal audio sampling rates
// blows (@ SR = 48000) for freq >= 15278
lp1_ladder(freq) = 0.5*(1-p) * fi.iir_nl(bvd,avd) with { 
    // freq argument: corner frequency (-3dB drop-off)
    w1(fc) = 2 * ma.PI * max( fc, 20 );

    bvd = (1,1); 
    avd = -p; 
    p = 1-w1(freq)/pl.SR; 
};

process = os.square(freq) <: lp1_direct(ffreq), lp1_ladder(ffreq) with {
    ffreq = hslider("ffreq", 20, 20, 20000, 0.001);
    freq = hslider("freq", 20, 20, 10000, 0.001);
};
```



--
Till Bovermann

https://tai-studio.org | http://lfsaw.de | https://www.instagram.com/_lfsaw/











> On 29. May 2021, at 01:43, Julius Smith <julius.sm...@gmail.com> wrote:
> 
> Hi Till,
> 
> Thanks for the cool app note - very interesting!
> 
> I am not familiar with fi.svf.lp, but I've done similar things.
> 
> If you just want to emulate the analog for testing purposes, I would use 
> direct form first-order filters and oversample like crazy.
> 
> If you want to deploy a digital counterpart for musical use at normal audio 
> sampling rates, I would use the normalized ladder form in place of direct 
> form (see code below).
> 
> You could also make a wave digital filter version (now that we have 
> wdmodels.lib thanks to Dirk).
> 
> These "multimode filters" look like normal state variable filters to me 
> (following early analog filters used for solving differential equations in a 
> state-space format, according to my experience - I first encountered one in 
> 1972 or so at Rice University).
> 
> I have a small write-up on digitizing state-variable filters here: 
> https://ccrma.stanford.edu/~jos/svf/
> but I didn't get into normalized structures (there are several).
> 
> I just compared direct-form to normalized-ladder in the test example below.  
> The Faust compiler does not catch the optimization opportunity (1st-order 
> filter written in 2nd-order form), so it's up to the C++ compiler to find 
> that.  I don't have time to look at the assembly code right now, so please 
> let us know if you check on that and learn the result.  It should not be 
> necessary to write out tf1snp explicitly.
> 
> // Test tf1snp defined as tf2snp with two 0 coefficients and compare to 
> direct-form
> 
> import("stdfaust.lib");
> 
> fc = 1000; // -3 dB frequency
> g = 1.0;   // dc gain
> 
> // Analog zero at infinity:
> b2 = 0;  b1 = g;  b0 = 0;
> 
> // Analog poles in normalized form (a2=1 implicitly):
> a1 = 1.0;
> a0 = 0; // Optimization should remove anything associated with this
> 
> // Frequency-scaling parameter for real-time bilinear xform = corner 
> frequency:
> w1 = 2*ma.PI*max(fc,20);
> 
> filter1 = fi.tf2snp(b2,b1,b0,a1,a0,w1);
> filter2 = 0.5*(1-p) * fi.iir_nl(bvd,avd) with { bvd = (1,1); avd = -p; p = 
> 1-w1/pl.SR; };
> process = 1-1' <: filter1, filter2;
> 
> // Check using faust2octave
> 
> On Fri, May 28, 2021 at 7:21 AM Till Bovermann <lf...@lfsaw.de> wrote:
> Hello list,
> 
> long time again that I sneaked in here...
> but still working with faust and it is generally a nice experience, thanks 
> everybody!
> 
> I am currently looking into multimode filters; we are considering building 
> something (analog) with a multimode filter based on the design found in 
> 
> http://www.soundsemiconductor.com/downloads/AN701.pdf
> 
> p.19
> 
> however with a feedback path to control filter resonance (as outlined on p.16)
> 
> 
> I build a preliminary emulation in faust like so [1].
> 
> However, I am not happy with the dynamics of the filter when changing its 
> parameters.
> 
> I was thinking to use the fi.svf.lp filter as a building block instead but it 
> is (AFAICS, it is not mentioned in the doc string...) not a 1st order design 
> (which is crucial here, partly for consistency with the to be emulated analog 
> circuitry, partly because the phase shift should be consistent to be able to 
> deal with the resonance feedback).
> 
> 
> I would be very much interested in other approaches, especially one's that 
> are closely related to the analog filter topology and its numerical stability 
> when applying dynamics to the filter freq and q.
> 
> 
> any help much appreciated!
> 
> 
> 
>         Till
> 
> 
> (I am also searching for a gentle introduction to filter theory, i.e. 
> explanations of Laplace / z-transforms, transfer functions, (virtual) analog 
> filter design, ... I am OK in general maths but filter theory escapes me and 
> most of the literature I find online assumes to much preliminary knowledge or 
> dives too fast into mathematical deeps for me to follow...)
> 
> 
> [1]
> 
> a parametric multimode filter inspired by Semiconductor's AN701
> 
> #### Parameters
> + a_amp: amplitude of original signal added in the mixing stage 
>        * `< 0` — use table
>        * `> 0` — use this amplitude
> + freq; filter frequency
> + q; Q-factor
> + idx; indicates one of the 45 modes of the filter based on Sound 
> Semiconductor's AN701
> [ 4LP, 3LP1HP, 3LP1AP, 3LP, 2LP2HP, 2LP1HP1AP, 2LP1HP, 2LP2AP, 2LP1AP, 2LP, 
> 2LP1NT, 1LP3HP, 1LP2HP1AP, 1LP2HP, 1LP1HP2AP, 1LP1HP1AP, 1LP1HP, 1LP1HP1NT, 
> 1LP3AP, 1LP2AP, 1LP1AP, 1LP1AP1NT, 1LP, 1LP1NT, 4HP, 3HP1AP, 3HP, 2HP2AP, 
> 2NP1AP, 2HP, 2HP1NT, 1HP3AP, 1HP2AP, 1HP1AP, 1HP1AP1NT, 1HP, 1HP1NT, 4AP1, 
> 3AP, 2AP, 2AP1NT, 1AP, 1AP1NT, 1NT, 2NT ]
> 
> #### Usage
> 
> process = _ : multimode(freq, q, idx, a_amp) * amp <: _,_ with {
>     freq = hslider("[0]freq[scale:exp]",100,10,20000,0.001);
>     q = hslider("[1]Q",0,0,15,0.001);
>     idx = hslider("[2]mode",10, 0,44,1);
>     amp = hslider("outamp",1, 0,4,0.001);
>     a_amp = hslider("a_amp",-0.001, -0.001,1.0001,0.001);
> };
> 
> ```
> multimode(freq, q, idx, a_amp) = _ : mix ~ _ : !, _ with {
> 
>     a(fbck, x) = x - (q * fbck);
> 
>     node = _ : lp(freq) * ba.db2linear(-3) * (-1) with {
>             lp(freq) = fi.lowpass(1, freq);
>     };
> 
>     b(fbck, x) = a(fbck, x) : node;
>     c(fbck, x) = b(fbck, x) : node;
>     d(fbck, x) = c(fbck, x) : node;
>     e(fbck, x) = d(fbck, x) : node;
> 
>     // factors for filter modes according to Sound Semiconductor's AN701, 45 
> states
>     a_table = waveform{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
> 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
> 0.16666666666667, 0.11111111111111, 0.33333333333333, 1.0, 0.2, 0.5, 
> 0.14285714285714, 0.35, 0.125, 0.33333333333333, 1.0, 1.0, 0.25, 0.75, 
> 0.083333333333333, 0.25, 1.0, 0.5, 0.16666666666667, 0.5, 0.125 };
>     b_table = waveform{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
> 0.0, 0.33333333333333, 0.2, 0.125, 0.125, 0.33333333333333, 1.0, 0.25, 
> 0.083333333333333, 0.25, 0.5, 0.16666666666667, 1.0, 0.5, 0.66666666666667, 
> 0.55555555555556, 1.0, 0.0625, 0.8, 1.0, 0.57142857142857, 0.05, 0.625, 1.0, 
> 0.066666666666667, 1.0, 0.75, 1.0, 0.5, 1.0, 0.0625, 1.0, 0.66666666666667, 
> 1.0, 0.5 };
>     c_table = waveform{ 0.0, 0.0, 0.0, 0.0, 0.5, 0.33333333333333, 1.0, 0.25, 
> 0.5, 1.0, 0.5, 1.0, 0.8, 0.625, 0.625, 1.0, 1.0, 0.75, 0.5, 1.0, 1.0, 
> 0.66666666666667, 0.0, 1.0, 1.0, 1.0, 1.0, 0.1875, 1.0, 0.5, 1.0, 0.4, 1.0, 
> 0.66666666666667, 0.0, 0.0, 1.0, 0.125, 1.0, 1.0, 0.25, 0.0, 1.0, 1.0, 1.0 };
>     d_table = waveform{ 0.0, 1.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 
> 1.0, 1.0, 1.0, 1.0, 1.0, 0.66666666666667, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 
> 1.0, 0.66666666666667, 0.77777777777778, 0.33333333333333, 0.75, 0.4, 0.0, 
> 0.85714285714286, 1.0, 0.5, 0.0, 0.66666666666667, 0.0, 0.5, 
> 0.083333333333333, 0.66666666666667, 0.0, 1.0, 0.0, 0.66666666666667, 0.0, 
> 1.0 };
>     e_table = waveform{ 1.0, 1.0, 1.0, 0.0, 0.5, 0.66666666666667, 0.0, 1.0, 
> 0.0, 0.0, 1.0, 0.33333333333333, 0.4, 0.5, 0.5, 0.0, 0.0, 0.5, 
> 0.66666666666667, 0.0, 0.0, 0.66666666666667, 0.0, 0.0, 0.16666666666667, 
> 0.22222222222222, 0.0, 0.25, 0.0, 0.0, 0.28571428571429, 0.4, 0.0, 0.0, 
> 0.26666666666667, 0.0, 0.0, 0.66666666666667, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 
> 0.5};
> 
>     fac(table, i) = table, int(i): rdtable : si.smoo;
>     fac_custom_amp(table, i, a_amp) = select2(a_amp >= 0, fac(table, i), 
> a_amp);
> 
>     mix(fbck, x) = e(fbck, x), (
>         a(fbck, x) * fac_custom_amp(a_table, idx, a_amp) + 
>         b(fbck, x) * fac(b_table, idx) + 
>         c(fbck, x) * fac(c_table, idx) + 
>         d(fbck, x) * fac(d_table, idx) + 
>         e(fbck, x) * fac(e_table, idx)
>     );
> };
> ```
> 
> --
> Till Bovermann
> 
> https://tai-studio.org | http://lfsaw.de | https://www.instagram.com/_lfsaw/
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
> _______________________________________________
> Faudiostream-users mailing list
> Faudiostream-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/faudiostream-users
> 
> 
> -- 
> "Anybody who knows all about nothing knows everything" -- Leonard Susskind



_______________________________________________
Faudiostream-users mailing list
Faudiostream-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/faudiostream-users

Reply via email to