Share your delay/reverb designs!

Hello everyone

Something I have always struggled with in any DSP work is the design of nice sounding delay effects.

I would like to see and hear your delay / echo designs for inspiration - so please share it here if you feel like it!

Below is one of mine which is a stereo (actually multichannel, but with “stereo” effects) delay with feedback, lpf in the feedback loop and modulation among other things. All parameters are normalized to 0.0-1.0.

(
~numchans = 2;

~delay = {|in, delay=0.1, delayfb=0.33, decaytime=0.5, delaywidth=0.1, modFreq=0.1, modAmount=0.25, fblowcut=0.5|
	var localIn = LocalIn.ar(numChannels: ~numchans,  default: 0.0);
	var output = Array.fill(~numchans, {|cNum|
		var sig, fb;

		// Random scalar to add variation
		var randScale = Rand(0.95,1.0);

		// Every other channel, scale the delay value according to the delaywidth arg
		var delayScale = if(cNum.even, { delaywidth.linlin(0.0,1.0,0.5,1.5) }, { 1.0 });

		// The final, scaled delayVal to be fed to the modulator 
		var delayVal = lag3(delayScale * randScale * delay.linlin(0.0,1.0,0.0001,2.0));

		// Delaytime modulator
		var phase = cNum.linlin(0,~numchans-1,-8pi, 8pi);
		var lfofreq = (randScale * modFreq.linexp(0.0,1.0,0.0001,10.0)).lag3;
		var minModamount = modAmount.linlin(0.0,1.0,1.0,0.001).lag * delayVal;
		var maxModamount = delayVal;

		delayVal = LFTri.kr(
			Rand(0.99,1.0) * lfofreq, 
			phase
		).linlin(-1.0,1.0, minModamount, maxModamount);

		// Feedback 
		// goes from 0 to 110%
		fb = (delayfb * 1.1 * localIn[cNum]).tanh; // tanh for "limiting"
		fb = DelayL.ar(fb, 0.2, delaytime: Rand(0.1,0.2)); // Avoid phase problems that sound "flat"
		fb = LPF.ar(fb, Rand(0.99,1.0)*fblowcut.linexp(0.0,1.0,40.0,12000.0));

		// The final delay
		sig = AllpassC.ar(
			in[cNum] + fb, 
			2, 
			delayVal,
			decaytime.linlin(0.0,1.0, 0.1,3.0).lag3
		);

		// Filter out potential dc
		LeakDC.ar(sig)
	});

	LocalOut.ar(output);

	output
};

// Play some bullshit testsounds
Pdef(\testy, Pbind(\dur, 0.125, \pan, Pwhite(-1.0,1.0), \degree, Pwhite((-7),7), \amp, 0.75));
Ndef(\testoutput).source = Pdef(\testy);
Ndef(\testoutput).mold(2, 'audio').play;
Ndef(\testoutput)[1] = \filter -> ~delay;
// Normalize all parameters in gui (a hack)
Ndef(\testoutput).controlKeys.do{|k|
	Spec.add(k, [0.0,1.0]);
};
Ndef(\testoutput).gui;

Ndef(\testoutput).play;
)
8 Likes

Here is some inspiration:

Valhalla documentation:
https://valhalladsp.com/category/documentation/

Sean Costello of Valhalla talking about reverbs:

2 Likes

Hey Mads, thanks for sharing!
I have some questions about your code, but i’ll stick with 2:
-why is phase going from -8 to 8pi?(i assumed it was -2 to 2pi)
-why are you not wrapping the functions in a SynthDef?
Thanks a lot

Bumping this thread with some further inspiration:

Tom Erbe describing how he designed his Erbe-Verb eurorack module, with his design goals, constraints, and various examples other historical reverb designs.

The sound isn’t the best but a very good history and practical explanation of reverb algorithms. Good counterpoint to Sean Costello’s I think.

1 Like

An interesting creative idea about reverberation is proposed by Miller Puckette on Pd’s examples (I.08 pvoc reverb).

Piano Reverb
This is a phase vocoder acting as a reverberator. The sound is more coherent (less “whispered”) than a real room or a standard delay-based reverberator.
The technique is to “punch” the incoming sound into channels where (1) there’s a peak, and (2) the incoming sound drowns out whatever might already be there. If the sound already in any channel is louder than the input the input for that channel is ignored.
For each window, the amplitude in each channel is propagated by a constant phase increment and multiplied downward by a gain that determines the “reverb time”.

This is more tricky (and also less efficient) to implement on the language side because you will need to re-window the FFT stream in order to access and manipulate the previous window bins. I hope someday someone implement this as an UGen!

PV_SpectralMap → into a reverb can do this (and I do it a lot) - It takes the spectrum of one sound (either continuous or frozen) and uses it to filter the second input. I often capture a soloist or group playing a chord (for instance), then use that sound as a filter that the performers continue to play through. Feed it into a GVerb with a long decay and it’s magic.

10 Likes

Delicious idea Josh !! I have to try this…

The “reverb” episodes of Reflectives are great, especially this one:

https://youtu.be/AnmY5LFHSVk

Sam

6 Likes

My (rough) implementation of Dattorro’s reverb (mentioned in the videos linked above) in SuperCollider is here:

If I remember correctly, it is NOT true to the paper in terms of where the taps are (and I never bothered to figure out). The code is 3 years old and I don’t think I’ve ever had an example of it as a stand-alone thing. But you might be able to get something out of how i implemented it :slight_smile:

3 Likes

I came across this video recently, clear explanation with nice animations.

My biggest issue with digital delay is not the so much the sound itself but the sound of the delay with moderate to high feedback when you sweep the rate ( or delay time, however you want to express it). To my ears, most (almost all) digital delays sound very unpleasant when sweeping the rate, unlike analog delays (tape or bucket brigade) which produce a lovely sound when you sweep the rate.

So I made this emulation of an analog delay which suffers from the same limitations as analog delays - degrading/downsampling of the audio at longer delay times. In the case of tape delays this is due to the slowing of the tape speed, for bucket brigade chips it is due to the lowering of the clock frequency. In my example the max delay time is 0.5 seconds, a factor 5 slower than the buffer being recorded into which has a duration of 0.1 second at rate = 1; Maybe this design could allow for longer delay times without intolerable downsampling by running several buffers at different lengths and crossfading between them.

Try this and sweep the ratio in gui to get some of that analog feel. I use the same sound source as in @madskjeldgaard’s example. The det and detRate parameters can produces responses simulating flutter in an analog delay.

(
s.waitForBoot{
	Ndef(\dly, {
		var sr = SampleRate.ir;
		var in = In.ar(\bus.kr(0), 2);
		var bufDur = 0.1;
		var buf = LocalBuf(bufDur * sr, 2).clear;
		var mod = SinOsc.ar(\detRate.kr(1)).bipolar(\det.kr(0)).midiratio;
		var rate = K2A.ar(bufDur / (\rate.kr(0.1)));
		var phasor = Phasor.ar(0, rate * mod, 0, bufDur * sr);
		var bufRd = BufRd.ar(2, buf, phasor);
		var recSig = bufRd * \feed.kr(0.5)  + in;
		var lpfreq = rate.linlin(0.1, 0.5, 10000, 2000);
		var rec = BufWr.ar(IIRFilter.ar(recSig, lpfreq), buf, phasor);
		var sig = XFade2.ar(in, LPF.ar(bufRd, lpfreq), \mix.kr(1) * 2 - 1);
		ReplaceOut.ar(\bus.kr(0), sig)
	});

	Spec.add(\feed, [0.0, 1.0]);
	Spec.add(\rate, [0.1, 0.5]);
	Spec.add(\det, [0.0, 1.0]);
	Spec.add(\detRate, [0.0, 10.0]);
	Spec.add(\mix, [0, 1.0]);

	Ndef(\dly).gui;
	Ndef(\dly).set(\feed, 0.8, \rate, 0.1, \det, 0, \detRate, 1, \mix, 0.5);
	Pdef(\testy, Pbind(\dur, 0.125, \pan, Pwhite(-1.0,1.0), \degree, Pwhite(-7, 7), \amp, 0.75)).play;
}
)
1 Like

Oh just realized this thread is old and was just revived. Watching the video you posted @fmiramar - nice stuff:)