Thanks so much for all the efforts you all put in to this seemingly simple task 
(that I am incapable to tackle myself)...

I'll try out stuff and will report back :)


all the best
        Till

--
Till Bovermann

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











> On 29. May 2021, at 18:52, Julius Smith <julius.sm...@gmail.com> wrote:
> 
> Hi Till,
> 
> Sorry to introduce confusion here.   The "p = 1-w1(freq)/pl.SR" formula is 
> only reasonable at low frequencies.  At 48kHz, instability is expected.  I 
> was only doing a low-frequency test.
> 
> Also, both filter1 and filter2 in my code example are normalized ladders.  I 
> did initially have a comparison to direct form, as the erroneous first 
> comment indicates (which threw me off when I sent the email in haste due to a 
> timeout), but looking at it now, the purpose of the filter1 and filter2 
> comparison is to check that fi.iir_nl (which can design normalized ladders of 
> any order, including order 1) matches fi.tf2snp (which can only do order 2).  
> The comparison was inconvenienced by the fact that tf2snp maps frequencies 
> through the bilinear transform while iir_nl does not, hence the direct pole 
> calculation for filter2.
> 
> I agree that the right path is to use tf2snp specialized to first-order 
> (filter1).  (I have now added tf1snp (and tf1s) to filters.lib doing this, 
> and they are finished if the C++ optimization does the right thing.)
> 
> Sorry for emailing in a rush!  The alternative was most likely no email at 
> all until two weeks from now :-)
> 
> - Julius
> 
> On Sat, May 29, 2021 at 6:38 AM Till Bovermann <lf...@lfsaw.de> wrote:
> >
> > 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
> >
> 
> 
> --
> "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