Weird Artifacts/Distortion with SVFTrap2

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

AUTO-ADMIN: Non-MP3, WAV, OGG, SoundCloud, YouTube, Vimeo, Twitter and Facebook links in this post have been protected automatically. Once the member reaches 5 posts the links will function as normal.
Hey KVR & DSP People,

I'm currently working on a dj eqalizer inspired by the https://playdifferently.org/ (https://playdifferently.org/) . For the lowpass/highpass i'm using 2 sequential svf with Q=1.0/sqrt(2.0) so a Linkwitz Riley 4.

Interestingly I'm getting weird artifacts when I'm lowpassing some tracks, sometimes it's subtle, sometimes quite noticeable.

I tried two SVF implementations, the one from faust (https://github.com/grame-cncm/faustlibr ... .lib#L2657 (https://github.com/grame-cncm/faustlibraries/blob/master/filters.lib#L2657)) and a slightly modified version with oversampling. But I get similar problems with both versions.

Might this be an expected problem with SVF? Is there any other Filter Circuit I could try?

Here a recording of the resulting broken audio: https://soundcloud.com/obsoleszenz/svfk ... 2429dacdcb

Here an interactive version in the FaustIDE where you can play around with the code and drag own music files in: https://t.ly/9rNL4 (https://t.ly/9rNL4) (sorry for the url shortener, kvr spam detection wasnt happy with the long url)

Maybe someone can help me out?
~obsoleszenz


Raw Code:

Code: Select all (#)

import("stdfaust.lib");

svf = environment {
	// Internal implementation
	svf(T,F,Q,G) = tick ~ (_,_) : !,!,si.dot(3, mix)
	with {
		tick(ic1eq, ic2eq, v0) =
            ic1eq_1,
            ic2eq_1,
			v0, v1_0*0.5+v1_1*0.5, v2_0*0.5+v2_1*0.5
		with {
            a1 = 1/(1+g*(g+k));
            a2 = g*a1;
            a3 = g*a2;

            v3_0 = v0 - ic2eq;
			v1_0 = a1 * ic1eq + a2 * v3_0;
			v2_0 = ic2eq + a2 * ic1eq + a3*v3_0;
            
            ic1eq_0 = 2*v1_0 - ic1eq;
            ic2eq_0 = 2*v2_0 - ic2eq;

            v3_1 = v0 - ic2eq_0;
			v1_1 = a1 * ic1eq_0 + a2 * v3_0;
			v2_1 = ic2eq_0 + a2 * ic1eq_0 + a3*v3_0;
            
            ic1eq_1 = 2*v1_1 - ic1eq_0;
            ic2eq_1 = 2*v2_1 - ic2eq_0;
		};

		A = pow(10.0, G/40.0);

		g = tan(F * ma.PI/(ma.SR*2)) : case {
			(7) => /(sqrt(A));
			(8) => *(sqrt(A));
			(t) => _;
		} (T);

		k = case {
			(6) => 1/(Q*A);
			(t) => 1/Q;
		} (T);

		mix = case {
			(0) => 0, 0, 1;
			(1) => 0, 1, 0;
			(2) => 1, -k, -1;
			(3) => 1, -k, 0;
			(4) => 1, -k, -2;
			(5) => 1, -2*k, 0;
			(6) => 1, k*(A*A-1), 0;
			(7) => 1, k*(A-1), A*A-1;
			(8) => A*A, k*(1-A)*A, 1-A*A;
		} (T);
	};

	// External API 
	lp(f,q)     = svf(0, f, q, 0);
	bp(f,q)     = svf(1, f, q, 0);
	hp(f,q)     = svf(2, f, q, 0);
	notch(f,q)  = svf(3, f, q, 0);
	peak(f,q)   = svf(4, f, q, 0);
	ap(f,q)     = svf(5, f, q, 0);
	bell(f,q,g) = svf(6, f, q, g);
	ls(f,q,g)   = svf(7, f, q, g);
	hs(f,q,g)   = svf(8, f, q, g);
};


smooth_bypass(bpc, e) = _,_ : ba.bypass_fade(ma.BS * 8, bpc, e) : _,_;
//smooth_bypass(bpc, e) = e;


// Calculate a denormalized frequency in hz from offset to max_fq from a (normalized) 0.0-1.0 float value
denormalize_fq(fq, offset, max_fq) = (fq * fq)  : * (max_fq - offset) + offset : int;

eq(hpf, lpf, sculpt_fq, sculpt_gain) =
    _,_ : sculpt(sculpt_fq, sculpt_gain) :  lp(lpf) : hp(hpf) 
with {

    sculpt_mono(fq, gain) =
		_ : fi.svf.bell(denormalized_fq, q, denormalized_gain)
	with {
        denormalized_fq = denormalize_fq(fq, 70,7000);
        centered_gain = (gain - 0.5) * 2;
		denormalized_gain = select2(centered_gain <= 0, centered_gain * 8, centered_gain * 30);
        q = octave_to_q(2);
        octave_to_q(n) = sqrt(pow(2, n)) / (pow(2, n) - 1);
	};

	sculpt(fq, gain) = _,_ : par(i, 2, sculpt_mono(fq,  gain));       
    highpass(cf) =  _ <: 
                        seq(i, 2, svf.hp(cf, 1.0 / sqrt(2.0)));
    lowpass(cf) =  _ <: 
                        seq(i, 2, svf.lp(cf, 1.0 / sqrt(2.0)));


	lp(fq) = _,_ : smooth_bypass((fq >= 0.999), par(i, 2, lowpass(denormalized_fq))) 
	with {
        denormalized_fq = denormalize_fq(fq : si.smoo, 30, ma.SR/2-1) <: attach(_, hbargraph("lp fq", 0, 20000)) ;

	};

    	hp(fq) = _,_ : smooth_bypass((fq <= 0.001), par(i, 2, highpass(denormalized_fq)))
	with {
      		denormalized_fq = denormalize_fq(fq,  1, ma.SR/2-1) <: attach(_, hbargraph("hp fq", 0, 20000));
            //hp(cf) =  (Bessel2.hp(cf), Bessel4.hp(cf), Bessel6.hp(cf)) : ba.selectn(3, filter_type);
	};
};


smoother(bs) =  tick ~ (_)
with {
        tick(z) = _ * b : + (z * a);
        a = exp(-1 * ma.PI / bs);
        b = 1.0 - a;
        z = 0.0;
};

// Uncomment to test in https://faustide.grame.fr
declare options "[midi:on]";
low_cut = button("low_cut");
param_eq_hpf =hslider("hpf[midi:ctrl 16 1]", 0.0, 0, 1, 0.001) + low_cut * 0.33 : smoother(128);
param_eq_lpf = hslider("lpf[midi:ctrl 19 1] ", 1.0, 0, 1, 0.001) : smoother(128);
param_eq_sculpt_fq = hslider("sculpt_fq[midi:ctrl 18 1]", 0.0, 0, 1, 0.001) : smoother(128);
param_eq_sculpt_gain = hslider("sculpt_gain[midi:ctrl 17 1]", 0.5, 0, 1, 0.001) : smoother(128);

process = eq(param_eq_hpf, param_eq_lpf, param_eq_sculpt_fq, param_eq_sculpt_gain) : _,_;    

Post

I don't really know Faust, so can only really guess what the code does... but at least the filter itself looks like a modern SVF which should be free of artifacts even under extreme modulation and I suspect the problem is probably somewhere else. Double sampling this kind of filters is IMHO kinda pointless (ie. doesn't necessarily improve anything), if you want to oversample then really it should be done with proper oversampling filters... but the whole thing should work fine without as well.

Is it possible that the artifacts are just clipping somewhere? Even though Butterworth is flat in frequency domain, the poles are still complex (ie. Q > .5) so they do cause some ringing in the time-domain causing the output to overshoot the input levels somewhat. If that's the problem, leaving some headroom should solve the problem.

Post

mystran wrote: Sat Sep 09, 2023 2:41 pm I don't really know Faust, so can only really guess what the code does... but at least the filter itself looks like a modern SVF which should be free of artifacts even under extreme modulation and I suspect the problem is probably somewhere else. Double sampling this kind of filters is IMHO kinda pointless (ie. doesn't necessarily improve anything), if you want to oversample then really it should be done with proper oversampling filters... but the whole thing should work fine without as well.

Is it possible that the artifacts are just clipping somewhere? Even though Butterworth is flat in frequency domain, the poles are still complex (ie. Q > .5) so they do cause some ringing in the time-domain causing the output to overshoot the input levels somewhat. If that's the problem, leaving some headroom should solve the problem.
Thanks! This sounds reasonable. Will leave some headroom and see what it does. But from a quick test this seems to help.

Post Reply

Return to “DSP and Plugin Development”