Audio Code Library: Four-Tap Delay


This code implements a four-tap delay, which is simply an extension of the basic delay. In the basic delay, we have a single read/write pointer to a buffer, where you read the value, and write a new one. In the multi-tap delay, we keep separate write and (multiple) read pointers. This way, you can have multiple output taps on the delay line, each producing a copy of the input at different time intervals.

One reason multi-tap delays are interesting is that they can create a rhythmic feel that can synchronize with the tempo of a song. When the feedback larger than zero, the signal that appears at a tap repeats at an interval equal to the total delay line length, and this interval is the same for ALL taps. A multi-tap delay is NOT equivalent to multiple basic delay units in parallel.

The code allows for the total delay time, feedback multiplier, delay times for each of the four taps, the mix level for the four taps, and the level of the non-delayed input signal in the constructor.

See also:

Please do not redistribute this code. In the event that it contains a bug, this will ensure that it can be fixed without the buggy copies floating around indefinitely.

Last Modified: 6/28/98

FourTapDelay.h

/*********************************************************

FourTapDelay.h - A 4-tap delay unit

Copyright (c) 1998, Scott Lehman, slehman@harmony-central.com
This code may be used and modified freely provided that credit
is given to the author in any public release. Any derivative
programs must be distributed freely and/or the modified source
code made publicly available.  All code is provided AS IS and
without warranty of any kind.
*********************************************************/

#ifndef FOUR_TAP_DELAY_H
#define FOUR_TAP_DELAY_H

#include "Processor.h"

class FourTapDelay : public Processor {
public:
  FourTapDelay(float totalTime, float feedback, 
               float tap1Time, float tap1Mix, 
               float tap2Time, float tap2Mix,
               float tap3Time, float tap3Mix,
               float tap4Time, float tap4Mix,
               float dryMix);
  void Initialize(void);
  void Process(void);
  void Cleanup(void);
  ~FourTapDelay(){;}

private:
  float totalDelayTime, feedbackGain;  // delayTime is in milliseconds
  float * outputSignal, * inputSignal;
  float tap1DelayTime, tap1Level, tap2DelayTime, tap2Level;
  float tap3DelayTime, tap3Level, tap4DelayTime, tap4Level;
  float delayLineOutput, dryLevel;
  float * delayLineStart, * delayLineEnd, * writePtr;
  float * tap1Ptr, * tap2Ptr, * tap3Ptr, * tap4Ptr;
  int  i;
  FourTapDelay(void){};
  FourTapDelay(FourTapDelay&){};
};


#endif

FourTapDelay.cpp

/*********************************************************

FourTapDelay.cpp - A 4-tap delay unit

Copyright (c) 1998, Scott Lehman, slehman@harmony-central.com
This code may be used and modified freely provided that credit
is given to the author in any public release. Any derivative
programs must be distributed freely and/or the modified source
code made publicly available.  All code is provided AS IS and
without warranty of any kind.
*********************************************************/


#include "FourTapDelay.h"


// ************  Delay(float, float, float, float)  ***********

// time - total length of delayline, in milliseconds
// feedback - feedback gain, from 0 to 1 (or .9999...)
// tap#Time - the delay time for the specified tap, in milliseconds
// tap#Mix - the level of the tap that is passed to the output
// dryMix - level of input signal passed directly to output, from 0 to 1

FourTapDelay :: FourTapDelay (float time, float feedback, 
                              float tap1Time, float tap1Mix,
                              float tap2Time, float tap2Mix,
                              float tap3Time, float tap3Mix,
                              float tap4Time, float tap4Mix,
                              float dryMix) {

  SetNumInputs(1);
  SetNumOutputs(1);

  totalDelayTime = time;
  feedbackGain = feedback;
  tap1DelayTime = tap1Time;
  tap1Level = tap1Mix;
  tap2DelayTime = tap2Time;
  tap2Level = tap2Mix;
  tap3DelayTime = tap3Time;
  tap3Level = tap3Mix;
  tap4DelayTime = tap4Time;
  tap4Level = tap4Mix;
  dryLevel = dryMix;


  return;
}


// ******************  Initialize(void)  *******************

void FourTapDelay :: Initialize(void)
{
	//Double check that input/output buffers are there
  if (inputs[0] == NULL)
    ModuleError("Buffer in Delay input not assigned");
  if (outputs[0] == NULL)
    ModuleError("Buffer in Delay output not assigned");

  //compute required buffer size for desired delay and allocate for it
  int bufferLength = (int)(totalDelayTime*samplingRate/1000);
  if(bufferLength <= 0)
    ModuleError("Delay buffer length is non-positive");
  delayLineStart = new float[bufferLength];
  if (delayLineStart == NULL)
    ModuleError("Couldn't allocate buffer in Delay");

  //set up pointers for delay line
  delayLineEnd = delayLineStart + bufferLength;
  writePtr = delayLineStart;

  //zero out the buffer (silence)
  do {
    *writePtr = (float)0.0;
  }
  while (++writePtr < delayLineEnd);

  //reset read pointer to start of delayline
  writePtr = delayLineStart;

  //set pointers for each of the taps
  tap1Ptr = delayLineStart + bufferLength - (int)(tap1DelayTime*samplingRate/1000);
  tap2Ptr = delayLineStart + bufferLength - (int)(tap2DelayTime*samplingRate/1000);
  tap3Ptr = delayLineStart + bufferLength - (int)(tap3DelayTime*samplingRate/1000);
  tap4Ptr = delayLineStart + bufferLength - (int)(tap4DelayTime*samplingRate/1000);  

  //only one in and out.  Assign to new pointers for simplicity
  outputSignal = outputs[0];
  inputSignal = inputs[0];

  return;
}

// ********************  Process(void)  ******************

void FourTapDelay:: Process()
{

  for (i=0; i<frameLength; i++) { //for each sample...

    //get delayed sample - doesn't go to output, but may be written back
    delayLineOutput = *writePtr;

    //weight the delayed sample and the current input to create the output
    outputSignal[i] = dryLevel * inputSignal[i] + 
		                  tap1Level * (*tap1Ptr) +
		                  tap2Level * (*tap2Ptr) +
		                  tap3Level * (*tap3Ptr) +
		                  tap4Level * (*tap4Ptr);

    //write the input sample and any feedback to delayline
    *writePtr = inputSignal[i] + 
			          feedbackGain * delayLineOutput;
	
    //increment buffer index and wrap if necesary
    if (++writePtr >= delayLineEnd)
      writePtr = delayLineStart;
    if (++tap1Ptr >= delayLineEnd)
      tap1Ptr = delayLineStart;
    if (++tap2Ptr >= delayLineEnd)
      tap2Ptr = delayLineStart;
    if (++tap3Ptr >= delayLineEnd)
      tap3Ptr = delayLineStart;
    if (++tap4Ptr >= delayLineEnd)
      tap4Ptr = delayLineStart;

  }

  return;
}


// **********************  Cleanup(void)  *******************

void FourTapDelay :: Cleanup(void)
{
  //Free memory allocated during initialization
  delete [] delayLineStart;
}


Back to the Audio Programming Page

Back to Harmony Central® Home Page

Email: webmaster@harmony-central.com
Copyright © 1995-98 Harmony Central, Inc. All rights reserved.