top of page

Coding a basic delay effect

The ability is to record and play back audio unlocks a wealth of options for simple but fundamental waveform manipulation.


The specific code to read ADCs (pots and your audio input) and write to DACs or PWM (your audio output) will vary across devices. I'll give examples in Arduino language since its popular.


Storing a long list of audio samples will always be done with an array, this is called a delay buffer. You can then use two indexes to iterate through the array, marking the position of your record header (the point you write samples to) and playback header (the point you play samples from). Both headers continuously loop through the delay buffer.


int record = 0;			// record header
int playback = 1;			// playback header
int delaybuffer[20000];	// buffer with 20000 samples
int sample;				// variable to store each individual sample as 
						// 			... it's processed

while(1)
{
	sample = analogRead(A1);		// grab a sample from your audio 
								// ...input (pin A1 in this example)
	delaybuffer[record] = sample;	// save sample to delay buffer
	sample = delaybuffer[playback];	// grab different sample from buffer
	analogWrite(A2) = sample;		// write this sample at the output
	
	if(record >= 20000)			// wrap around record header 
		record = 0;
	else record++;				// or increment

	if(playback >= 20000) 			// wrap around play header
		playback = 0;
	else playback++;				// or increment
}

Imagine the buffer as a tape loop if it helps!


Delay chips like analog BBDs and (I'm pretty sure) PT2399s have a fixed number of samples in their buffers. The delay time is changed by altering the overall speed of the chip, creating that pitch-warp transition we all know and love. To do this in code, just add a microsecond delay to your loop that is controllable with a pot. To get a decent range of delay times this way you'll need a chip that can process quickly, or settle for "bitcrushed" sounds on the longer delays.

delayTime = analogRead(A3));     // get pot reading for delay time
delayMicroseconds(delayTime);    // delay by that amount

a pot controlling the clock speed / delay time / sampling rate


Another way to change delay length is to abruptly move the play or record header position. This will cause a distinct clicking sound as the delay buffer is cut and shunted, which is cool to do intentionally for glitch delays. Electric Druid overcame the clicking by cross fading to the new position when the time knob is turned.


A third way that's similar to the last is to use a variable instead of "20000" and to modify the length of the delay buffer. The downside is that when you shorten the delay length, you end up with samples stored at the end of the array which will play out again later on when you lengthen the delay, which can be a bit perplexing.


It's very easy to move the play header backwards (decrement) for a reverse delay. However, this will effectively half your max delay time as the record and play headers cross over twice per cycle around the delay buffer, causing a click each time.

	if(playback == 0) 			// wrap around play header
		playback = 20000;
	else playback--;				// or decrement

going backwards


Coding a feedback network AFAIK requires a processor capable of float arithmetic, that is, you need numbers with decimal places to divide the output down before mixing it back in with the input. Multiplying a number by a fraction less than 1 will reduce that number. If instead you divide your sample by whole numbers you get a logarithmic curve that's crazy steep at one end, think how different dividing by 1 is to dividing by 2.


float feedback;						// declare a feedback variable
feedback = (float) analogRead(A4)/1023;	// get a reading between 0 and 1                                 
                                         //       ...from a 10 bit pot 
input += (output * feedback);			// add the attenuated output to 
                                         //       ...the input

Or there's always analog feedback of course!



Recent Posts
Search By Tags
Glowfly_Logo_RGB_inv.png
bottom of page