Audio Code Library: Variable Delay


The delay can easily be extended to produce the flanging and chorus effect by allowing the delay time to change over time. This requires what is called a variable delay line. This variable delay is implemented with the basic circular buffer, but there is not a single pointer for reading and writing since we need to change the delay time. The size of the buffer is determined by the maximum delay time you want to have. This module has two inputs - the input audio signal and another signal whose value is the delay time (in milliseconds) - and one output.

Unlike the plain delay, this code doesn't keep a read pointer. Instead, the pointer is calculated from the desired delay time and the write pointer. In general, the value we want would lie between two sample values so we take the largest valid pointer, and computer the fractional offset. The interpolated value is calculated by connecting the two known values with a straight line, multiplying the line's slope times the fractional offset, and adding that to the sample on the lower bound. To simplify the interpolation process a bit, the delayline is made one sample longer than necesary where the first point is a copy of the last.

The write step is pretty much the same as the basic delay - after getting the read value, write the incoming sample plus the read value weighted by the desired feedback level. When the write pointer reaches the end of the delay line, copy the last point to the first, and wrap the pointer to the second location of the delay line.

The code allows for the maximum delay time, feedback multiplier, and mix levels for the input and delayed signal to be specified 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: 9/5/98

Vdelay.h

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

Vdelay.h - A variable delay unit with linear interpolation

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 VDELAY_H
#define VDELAY_H

#include "Processor.h"

class Vdelay : public Processor {
public:
  Vdelay(float maxDelay, float feedback, float wetMix, float dryMix);
  void Initialize(void);
  void Process(void);
  void Cleanup(void);

private:
  float * bufferStart, * bufferEnd, feedbackGain;
  float wetLevel, dryLevel, * outputSignal, * inputSignal, *LFO;
  float delayLineOutput, delayTime; //delay time in milliseconds
  float * delayLineStart, * delayLineEnd, * writePtr;
  int  i, delayLineLength;
  Vdelay(Vdelay&){};
};

#endif

Vdelay.cpp

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

Vdelay.cpp - A variable delay unit with linear interpolation

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 "Vdelay.h"
#include 


// **********  Vdelay(float, float, float, float)  ************

Vdelay :: Vdelay(float maxDelayTime, float feedback, float wetMix, float dryMix)
{
  SetNumInputs(2);
  SetNumOutputs(1);

  feedbackGain = feedback;
  delayTime = maxDelayTime;
  wetLevel = wetMix;
  dryLevel = dryMix;

  return;
}

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

void Vdelay :: Initialize(void)
{
	//Double check that input/output buffers are there
  if (inputs[0] == NULL)
    ModuleError("Buffer in Delay input not assigned");
  if (inputs[1] == 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
  //add extra point to aid in interpolation later
  delayLineLength = (int)(delayTime*samplingRate*.001) + 1;
  if(delayLineLength <= 0)
    ModuleError("Delay buffer length is non-positive");
  delayLineStart = new float[delayLineLength];
  if (delayLineStart == NULL)
    ModuleError("Couldn't allocate buffer in Delay");

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

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

  //set read pointer to end of delayline. Setting it to the end
  //ensures the interpolation below works correctly to produce
  //the first non-zero sample.
  writePtr = delayLineStart + delayLineLength -1;

  //Assign to new pointers for simplicity
  outputSignal = outputs[0];
  inputSignal = inputs[0];
  LFO = inputs[1];

  return;
}


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

void Vdelay :: Process (void)
{

float offset, *ptr, diff, frac;

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

    //Compute number of samples necesary for desired delay time
    offset = LFO[i]*samplingRate*0.001;

    //compute the largest read pointer based on the offset.  If ptr
    //is before the first delayline location, wrap around end point
    ptr = writePtr - (int)(ceil(offset));
    if (ptr < delayLineStart)
      ptr += delayLineLength - 1;

    //compute distance from ptr and slope to the next sample
    diff = *(ptr+1) - *ptr;
    frac =  1 - (offset - (int)offset);

    //calculate the delayline output value
    delayLineOutput = *ptr + diff*frac;


    //weight the delayed sample and the current input to create the output
    outputSignal[i] = dryLevel * inputSignal[i] + 
		                  wetLevel * delayLineOutput;

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

  return;
}


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

void Vdelay :: 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.