Karplus–Strong string synthesis with InFeedback

Here is the most canonical Karplus-Strong I can come up with, without using extensions – basically a 1:1 implementation of the block chart.

s.options.blockSize = 16;  // extend the upper frequency limit
s.boot;

(
SynthDef(\basicks, { |out, gate = 1, amp = 0.1, freq = 440, decay = 1,
	excAtk = 0.001, excDcy = 0.005,
	ffreq = 8000|
	var feedback = LocalIn.ar(1);  // don't need InFeedback
	// n repetitions = freq * decay
	// we want coeff ** n = 0.001, so coeff = 0.001 ** (1/n)
	var coeff = 0.001 ** (freq * decay).reciprocal;
	var excEnv = EnvGen.ar(Env.perc(excAtk, excDcy));
	var mainEnv = EnvGen.ar(Env.asr(0, 1, 0.03), gate + 0.001, doneAction: 2);
	var exc = PinkNoise.ar * excEnv;
	var sig;
	
	// filter must be inside the feedback-delay loop
	feedback = LPF.ar(feedback, ffreq) * coeff;
	sig = DelayC.ar(exc + feedback, 0.5, freq.reciprocal - ControlDur.ir);
	
	LocalOut.ar(sig);  // --> back to LocalIn
	
	DetectSilence.ar(sig, doneAction: 2);
	Out.ar(out, (sig * (mainEnv * amp)).dup);
}).add;
)

(
p = Pbind(
	\instrument, \basicks,
	\degree, Pwhite(-7, 7, inf),
	\dur, Pexprand(0.08, 0.5, inf),
	\decay, Pexprand(0.3, 9.0, inf),
	\ffreq, Pexprand(5000, 16000, inf)
).play;
)

p.stop;

Not necessarily. Without single-sample feedback, there is an upper limit on note frequency = sr / blocksize, e.g. my example is running on my system at 48 kHz, divided by blockSize = 16, so I can play up to 3000 Hz. Without changing the block size, that limit would go down to 750 Hz.

Within the frequency limit, KS works perfectly well. There is no strict requirement for single-sample feedback.

Using an extension for single-sample feedback, e.g. Feedback quark, would push the upper frequency limit arbitrarily high (within Nyquist).

hjh

3 Likes