There are many scenarios where a delay line is needed in signal processing. Scenarios ranging from Digital Filters, Convolution, Reverberation, Echo and others. Delay lines have a fixed length of N samples. When delay lines take in a new sample they discard the oldest sample. In this post we discuss how delay lines work and implement a delay line in the C programming language.
A Theoretical Delay Line
For our purposes we will store primitive data types in an array. We’ll spare for this example both non-primitive data types, as well as, non-contiguous memory. The primitive data type in this array would be any of the following types: int, char, complex, double, or float. The memory for the delay line is monotonically increasing and continuous, meaning all memory addresses for samples are next to one another. Essentially, we’ll have a
int delay[N] to declare our delay line. The delay line will store N samples. With each new sample we’ll throw out the oldest sample. Below is a figure to depict the state of the delay line on each new sample received.
Implementation Rules of the Delay Line
- We’ll always store N latest samples
- Each new sample will overwrite the oldest sample
- Once the delay line is full all memory locations will have a valid time sample
- The delay line allows for random access access where samples are ordered from newest to oldest. For example
delay_linegets the previous time sample and
delay_linegets the latest time sample.
Is a Delay Line a Circular Buffer?
There are data structures namely a circular buffer, ring buffer, cyclic buffer, or circular queue that are very similar to a delay line. The delay line is circular and has a fixed size but there are the following distinctions. In many cases using a Circular Buffer would work for a Delay Lines. Here are some differences though in the usage between delay lines and circular buffers.
- Delay Lines are typically only written a sample at a time. Circular Buffers can be written with N bytes.
- Delay lines are accessed by time sample. E.g. the access will be time sample zero
- We don’t have a read and write pointer. Only a current pointer that points to
- Every memory address in our delay line is always used once we’ve collected N samples. The delay line will be full of time samples at all time. The next time sample it will throw out the oldest sample and put in the newest.
Using the Delay Line
To use the delay line we have the following process:
- Create a
delay_line.sizeto the number of samples required for the delay line
- Allocate the memory and initialize the delay line with
- Add time samples with
- Access time samples with
- De-allocate the memory and de-initialize the delay line with
In the code below we create two delay lines of size 3 and of size 10. We then insert time samples into the delay line and access them back. All the
asserts will evaluate to true.
C Header for a Delay Line
The C header file will define the API to use for the delay line. We have the ability to create and destroy the delay line. We can also add samples and fetch the samples. This delay line implementation has a
head pointer which points to where memory starts. A
tail pointer which points to the last sample, and a
curr pointer which points to the latest sample.
C Implementation of a Delay Line
Below is the implementation of a delay line in C.
Where to go from here?
This delay line has type
int which may not be suitable for your application. It would need to be modified to the data type for the application. Once that is sorted out it can be used for reverb, digital filters, echo, and all sorts of applications that use delay lines.
Downloading and Running the Code
Download the delayline-1.0.tar.gz code and run the following commands. Note, use of the
CFLAGS with the
-g flag so that the
assert statements can run.