Stealing VCV Rack's analog "exciter" formula

Credit:

(
a = {
	var saw = Saw.ar(MouseX.kr(50, 8000, 1));
	var x = saw * 0.5 + 0.5;
	var saw2 = LeakDC.ar(((x * 5 - 13) * x + 3) / (x * 2 + 3)).neg;
	var trig = Impulse.kr(1);
	var index = ToggleFF.kr(trig);
	SendReply.kr(trig, '/index', index);
	(Select.ar(index, [saw, saw2]) * 0.1).dup
}.play;

if(v.tryPerform(\isClosed) ? true) {
	v = Button(nil, Rect(800, 200, 200, 50)).states_([
		["Digital", Color.white, Color.gray(0.1)],
		["Analog", Color.black, Color.gray(0.6)]
	]).font_(Font.default.size_(36)).front;
};

OSCdef(\index, { |msg|
	defer { v.value = msg[3] };
}, '/index', s.addr);
)

To my ear it adds some pleasing high-frequency content. Also works well with Pulse.ar.

Comments? Improvements?

EDIT: Rewrote the formula to suit SC’s operator order better.

EDIT: You could try two versions.

  • VCV’s exact formula LeakDC.ar(((x * 5 - 13) * x + 3) / (x * 2 + 3)).neg – mellower
  • Or my first typo version LeakDC.ar(((x * (5 - 13)) * x + 3) / (x * 2 + 3)).neg – adds a touch of distortion. TBH I like this sound better :smiling_imp: though it’s technically wrong. – -7 instead of -8 sounds slightly nicer.

hjh

(
{
	var f = { |x|
		x = x * 0.5 + 0.5;
		LeakDC.ar(((x * (-7)) * x + 3) / (x * 2 + 3)).neg
	};

	var sdef = { |name, oscFunc|
		SynthDef(name, { |out, gate = 1, freq = 440, amp = 0.1, detun = 1.008,
			ffreq = 8000, gain = 0.02,
			atk = 0.01, dcy = 0.1, sus = 0.6, rel = 0.1,
			fAtk = 0.01, fDcy = 0.1, fSus = 0, fRel = 20, fAmt = 3,
			pan = 0, width = 0.8,
			i_analog = 1|
			var n = 7;
			var detunes = Array.fill(n, { detun ** Rand(-1.0, 1.0) });
			var sig = oscFunc.value(freq * detunes);
			var eg = EnvGen.kr(Env.adsr(atk, dcy, sus, rel), gate, doneAction: 2);
			var filtEgAmt = min(fAmt, (19000 / ffreq) - 1);
			var feg = EnvGen.kr(Env.adsr(fAtk, fDcy, fSus, fRel), gate) * filtEgAmt + 1;
			var anasig;

			sig = Splay.ar(sig, width, amp, pan);
			anasig = f.(sig);
			sig = Select.ar(i_analog > 0, [sig, anasig]);

			sig = MoogFF.ar(sig, ffreq * feg, gain);

			Out.ar(out, sig * eg);
		})
	};

	sdef.(\anasaw, Saw.ar(_)).add;
	sdef.(\anapulse, { |freq|
		var pwidth = NamedControl.kr(\pwidth, 0.5);
		Pulse.ar(freq, pwidth)
	}).add;
	sdef.(\anatri, DPW3Tri.ar(_)).add;  // needs SC3-plugins, could use LFTri or VarSaw
}.value;
)

// then
(
p = PmonoArtic(
	\anapulse,
	\degree, Pn(Pser([0, 6, 4, 3, 9, 5, 8, 2, 3, 4, 6, -2], { rrand(5, 12) }), inf),
	\scale, Scale.dorian,
	\dur, 0.125,
	\legato, Pwrand([0.4, 1.1], #[0.8, 0.2], inf),
	\pwidth, Pwhite(0.1, 0.5, inf),
	\octave, #[3, 4, 5],
	\ffreq, Pexprand(800, 10000, inf),
	\gain, 1.5
).play;
)

(
a = {
	var sig = In.ar(0, 2);
	sig = sig + (0.7 * CombC.ar(sig, 0.5, [0.75, 1.25] * 0.5, 6));
	ReplaceOut.ar(0, sig)
}.play(target: s.defaultGroup, addAction: \addToTail);
)

p.stop;

a.release;

hjh