GenerateAndTest

hey, i have created a repository for developing and testing out new stuff.
Currently you find my current state of a warpable filterbank in there, where i will refine the current frequency transformer and add amplitude transformation as well for generating formants.
You will also find the current state of my disperser in there, where im currently trying to find a sweet spot between disperser twang and a feedback phaser. Will add a frequency shifter with feedback as well.
Will generate and test and if im happy with the results these will go into my main repo.

You can check out the latest release here: Releases · dietcv/GenerateAndTest · GitHub

// Basic Disperser
(
{
    var sig = Impulse.ar(2);
    Disperser.ar(
        input: sig,
        freq: 500,
        resonance: 0.5,
        mix: 1.0,
        feedback: 0.0
    )!2;
}.play;
)

// Phaser with Feedback
(
{
    var sig = Saw.ar(110);
    Disperser.ar(
        input: sig,
        freq: SinOsc.kr(0.2).linlin(-1, 1, 200, 2000),
        resonance: MouseY.kr(0, 1),
        mix: 0.5,
        feedback: MouseX.kr(0, 1)
    )!2 * 0.1;
}.play;
)

// WhiteNoise into FilterBank
(
{
    var sig = WhiteNoise.ar(0.5);
    var spread = MouseX.kr(0, 2);
    var warp = MouseY.kr(-1, 1);
    sig = FilterBank.ar(sig, 220, spread, warp, 0.8);
    sig!2 * 0.1;
}.play;
)

// VPS into FilterBank
(
var vps = { |freq, skew, harm|

    var phase = Phasor.ar(DC.ar(0), freq * SampleDur.ir);

    var harm_even = harm.round(2);
    var harm_odd = ((harm + 1).round(2) - 1);

    var pmod = UnitTriangle.ar(phase, skew);

    var sig_even = cos(phase + (pmod * (harm_even - skew)) * 2pi).neg;
    var sig_odd = cos(phase + (pmod * (harm_odd - skew)) * 2pi).neg;

    LinXFade2.ar(sig_even, sig_odd, harm.fold(0, 1) * 2 - 1);
};

SynthDef(\test, {
    var sig;

    var grainFreqMod = LFDNoise3.ar(0.1!2);
    var skewMod = LFDNoise3.ar(0.1!2);

    var harmonics = [
        \harmA.kr(2) * (2 ** (grainFreqMod[1] * 3)),
        \harmB.kr(4) * (2 ** (grainFreqMod[0] * 1))
    ];

    sig = vps.(\freq.kr(440), skewMod.linlin(-1, 1, 0.15, 0.35), harmonics);

    [1.5, 2.0].do{ |ratio, i|
        sig[i] = PitchShift.ar(sig[i], 0.2, ratio, 0.003, 0.001);
    };

    sig = Splay.ar(sig);

    sig = sig * \amp.kr(-15.dbamp);
    sig = sig * Env.asr(0.001, 1, 0.001).ar(Done.freeSelf, \gate.kr(1));
    sig = Limiter.ar(sig);
    Out.ar(\out.kr(0), sig);
}).add;

SynthDef(\filterbank, {

    var inSig, sig;
    var cuttoff, spread, warp;

    inSig = In.ar(\in.kr(0), 2);

    spread = LFDNoise3.kr(0.1).linlin(-1, 1, 0, 2);
    warp = LFDNoise3.kr(0.1).linlin(-1, 1, -1, 1);
    cuttoff = 220;

    sig = FilterBank.ar(inSig, cuttoff, spread, warp, 0.1);

    ReplaceOut.ar(\out.kr(0), sig);
}).add;
)

(
Routine({

    var freqs = [57, 60, 64, 65, 70].midicps;

    s.bind {
        freqs.collect{ |freq|

            Synth(\test, [
                \freq, freq,
                \amp, -35.dbamp,
            ], addAction: \addToHead);

        };
    };

    s.bind {

        Synth(\filterbank, [
        ], addAction: \addToTail);

    };

}).play;
)
1 Like

I have created a new release you can get it here: Releases · dietcv/GenerateAndTest · GitHub, both the Disperser and the Filterbank had problems when modulating the resonance before, resulting in NaNs.
In the former release both of the Ugens have been based on a SVF by Andrew Simper (Bandpass and Allpass tick).

I have now based the Disperser Ugen on a simple Biquad topology, where the coefficients are based on the RBJ EQ cookbook and the filter topology on a Biquad with TDF-II form. These are then running in a cascade. Thats really stable for modulating the resonance:

(
{
	var sig = Saw.ar(32.midicps);
	Disperser.ar(
		input: sig,
		freq: 1000,
		resonance: SinOsc.kr(0.3).linlin(-1, 1, 0, 1),
		mix: 0.5,
		feedback: 0,
	)!2 * 0.5
}.play;
)

I have now based the FilterBank Ugen on a SVF topology by Émilie Gillet which is capable of high resonance and is also really stable in that parallel setting (it doesnt have the 2*v - state operation like the simper svf, which i guess is getting unstable for high Q values in that parallel setting).

(
{
    var sig = WhiteNoise.ar(0.5);
    var spread = MouseX.kr(0, 2);
    var warp = MouseY.kr(-1, 1);
	sig = FilterBank.ar(sig, 220, spread, warp, SinOsc.kr(0.3).linlin(-1, 1, 0, 1));
    sig!2 * 0.1;
}.play;
)

Question: What’a disperser? My searches mostly brings up one specific plugin by that name which seems to be some allpass network that does transient shaping. I’ve just never heard the term before in an audio context…

Its referencing the Kilohertz disperser plugin which is just 2nd order allpass filters in a cascade with control over filter q to change the steepness of the phase response and the center freq.
The frequency depended delay is decorating your input with a chirp.

Its similiar to a phaser but you dont have that feedback path and you typically dont use the dry and wet signal and mix them together. Often times phasers use 1st order allpasses so they dont have control over the q (the resonance of phasers is the feedback control).

The plugin can do both, i will probably rename it to “Twang” haha

This paper is related: https://www.researchgate.net/publication/286994878_Spectral_Delay_Filters
also used for spring reverb emulation https://www.researchgate.net/publication/220057482_Efficient_Dispersion_Generation_Structures_for_Spring_Reverb_Emulation

1 Like

nice, thanks! I’ll give it a try…

the initial examples are illustrating that difference:

Disperser: with mix:1.0 and feedback 0.0 and resonance 0.5 (resonance 0.5 is identical to cascading Allpass1, if you cascade Allpass2 and modulate the q you get NaNs).
Phaser: with mix 0.5 and feedback modulation

(
var disperser = { |in, fltFreq|
	var wet;
	wet = in;
	16.do {
		wet = Allpass1.ar(wet, fltFreq);
	};
	wet;
};

{
	var sig = Saw.ar(100);
	sig = disperser.(sig, 400);
}.plot(0.02);
)

(
{
	var sig = Saw.ar(100);
	Disperser.ar(sig, \freq.kr(400), \res.kr(0.5), \mix.kr(1), \feedback.kr(0));
}.plot(0.02);
)

But yeah you can just moudulate all the params and see what sounds nice :slight_smile:

// Basic Disperser
(
{
    var sig = Impulse.ar(2);
    Disperser.ar(
        input: sig,
        freq: 500,
        resonance: 0.5,
        mix: 1.0,
        feedback: 0.0
    )!2;
}.play;
)

// Phaser with Feedback
(
{
    var sig = Saw.ar(110);
    Disperser.ar(
        input: sig,
        freq: SinOsc.kr(0.2).linlin(-1, 1, 200, 2000),
        resonance: MouseY.kr(0, 1),
        mix: 0.5,
        feedback: MouseX.kr(0, 1)
    )!2 * 0.1;
}.play;
)

here a quick test sequence where im adjusting the parameters:

You can create all sorts of percussive tones adjusting the freq, resonance and feedback:

(
{
    var sig = Impulse.ar(2);
    Disperser.ar(
        input: sig,
        freq: 1000,
        resonance: 0.8,
        mix: 1.0,
        feedback: 0.8
    )!2;
}.play;
)