Go to:
Title Page               Chapter 1           Appendix A   Appendix J
Copyright                Chapter 2           Appendix B   Appendix K
Abstract                 Chapter 3           Appendix C   Appendix L
Acknowledgements         Chapter 4           Appendix D   References
Table of Contents        Chapter 5           Appendix E
List of Figure           Conclusions/        Appendix F
List of Tables           Future Directions   Appendix G
List of Audio Examples                       Appendix H
List of Programs                             Appendix I


Appendix J

class Delayline, code for a C++ class which models an interpolating and

non-interpolating delay line



I. Motivation and background

Digital delay lines form the basic building blocks from which many electro-acoustic musical tools are made: simple chorusing and flanging effects, for example, use delay lines exclusively for sound processing. Specifically, digital delay lines are closely related to digital filters, which usually employ delay lines for time-domain convolution. In preparation for further studies with digital filters, therefore, class Delayline, an object-oriented, templated C++ class, was written to explore the rudimentary operations of a linear interpolating delay line.

class Delayline does not rely on any audio libraries for proper functioning; the class declaration and definition found in delayline.h can be used with or without audio code. In this respect, code can be written without audio first to test proper operation of the Delayline, while audio support can be added in a later stage.

This code was written during Fall 1995, for Music 103 (Analysis, Synthesis, and Perception of Timbre, taught by Charles Dodge) at Dartmouth College.

II. Specific design issues

A. Use of a circular buffer.

A circular buffer, is a block of memory which has two associated pieces of data: a start index and a end index, both of which refer to certain index in the block of memory. Typically, circular buffers are used to implement queues, or FIFO (first-in, first-out) buffers. When a sample is enqueued in the circular buffer, it is stored at the location of the start index, and the start index is increased by one. When a sample is dequeued in the circular buffer, the value to which the end index points is returned, and the end index is increased by one. The buffer is called circular because when the start and end indices reach the logical end of the buffer, each index is simply reset to point to the first location in the buffer.

Circular buffers are used in place of other queuing data structures, such as linked lists, for speed reasons, as well as their ease of implementation. The code written for class Delayline was altered from a standard Queue data structure (Weiss 108-109).

B. Templating of class Delayline for different sample types

class Delayline is also a templated class; the template argument tells the Delayline what type of samples (e.g. floating point, integer, etc.) it will hold; in this respect, class Delayline is a flexible alternative to writing delay code for each possible type of audio sample.

C. Allowing for non-integral samples of delay via linear interpolation

Formally, a delay line is a FIFO queue which holds a certain number of audio samples in a circular buffer. From this buffer, a user can "tap," or extract a certain sample at a certain position in the queue. If the queue's input is real-time audio, therefore, monitoring the tap at sample number 456 would produce an output delayed by 456 samples.

Even though discrete samples are enqueued in a delay line, it is often desirable to have a tap location that is not an integer. For example, a tap location of 3.5 samples would guess the value of the delayline between samples 3 and 4 of the delayline. class Delayline uses linear interpolation to deal with such non-integral tap locations. If a non-integral tap location is requested, the two sample values from the two tap locations closest to the requested tap location are used as endpoints in the construction of a line segment. The point on the line corresponding to the desired non-integral tap location is then returned to the user.

III. General usage

class Delayline is designed to be as simple as possible; the best way to learn its usage, therefore, is to refer to the included examples. The user must #include "ac.h"; "ac2.h"; and "delayline.h" to declare and define class Delayline, its member functions and data, and other supporting macro definitions. All example programs in this section can be compiled with the following command. The audio libraries libaudio.a and libaudiofile.a, available in standard releases of the Irix operating system, are required for compilation:

g++ -o output_binary <filename.cc> ac.cc ac.error.cc -laudio -laudiofile

ac.cc provides definitions for class Aiff_file's member functions, and ac.error.cc provides definitions for the error codes referenced in Appendix G, part II.C. The class definition for class Delayline has been completely written in the delayline.h file. The output will be output_binary, which can be executed directly at the command line.

A. Construction and destruction

class Delayline is designed so that the length of the delay line can change dynamically. Therefore, in addition to the template declaration of class Delayline for the desired sample type, the constructor for a Delayline takes two arguments: the maximum possible delay in samples, the starting delay in samples. For example, the following code instantiates a Delayline with a 100-sample maximum delay, and a 50-sample starting delay. Floating point samples are used.

#include "delayline.h"

#include "ac.h" // defines the type float_sample

template class Delayline<float_sample>;

...

Delayline<float> the_delay_line(100,50);

B. Enqueing and dequeing samples

The user must enqueue and dequeue samples manually in a fashion that keeps the Delayline "moving"; typically, a user should dequeue and enqueue samples on a 1:1 basis, unless the user wishes to dynamically change the size of the delayline.

Enqueue an element at the front of the Delayline.

void enqueue(const ElementType& X)

Dequeue an element from the rear of the Delayline.

ElementType dequeue()

C. Using the interpolating and non-interpolating tap member functions

Two member functions are included to tap the Delayline at interpolating or non-interpolating sample locations. If the user is sure that only integral tap times will be used, the tap() function should be used instead of the tapi() function, as tap() will execute much faster than tapi().

Tap the Delayline to return an interpolated sample value at the non-integral sample number num_elements_delay

ElementType tapi(float num_elements_delay)

Tap the Delayline to return a non-interpolated sample value at the integral sample number num_elements_delay

ElementType tap(int num_elements_delay)


IV. Examples

The following table lists some examples that make exclusive use of class Delayline. All audio examples can be found in the included compact disc.
CD Track # Audio examplefile description
N/AN/A delaytest2.cc non-audio testing of class Delayline for floating point values
N/AN/A delaytest3.cc test of class Delayline for stereo audio data
N/AN/Adelaytest2.cc non-audio testing of class Delayline for floating point values
N/AN/Adelaytest3.cc test of class Delayline for stereo audio data
85j.1delaytest4.cc four channel chorusing example, original file (Palestrina)
86j.2delaytest4.cc four channel chorusing example. stereo mixdown of deep quad chorusing of soundfile (Palestrina)
87j.3delaytest5.cc exhibition of high-frequency attenuation of noise due to non-integral sample delay tap. original file (white noise)
88j.4delaytest5.cc exhibition of high-frequency attenuation of noise due to non-integral sample delay tap. processed file (white noise)
89j.5ac_test4.cc implementation of Perry Cookís slide flute
90j.6ac_test4a.cc implementation of Perry Cookís slide flute with time-varying parameters
91j.7granular.cc application of granular synthesis for time-stretching purposes. original file (Beethoven)
92j.8granular.cc application of granular synthesis for time-stretching purposes. processed file (Beethoven)
93j.9granular.cc application of granular synthesis for time-compression purposes. original file (Palestrina)
94j.10granular.cc application of granular synthesis for time-compression purposes. processed file (Palestrina)

V. Future directions

A linear interpolating delay line, as seen in the example file delaytest5.cc, attenuates high frequencies. Other forms of interpolation, which use more than two adjacent sample values to guess at non-integral tap values between them would provide less high frequency roll-off for applications which require a larger dynamic range. In fact, the algorithm used to compute the fast lifted interpolating wavelet transform (Chapter 3, see Neville's algorithm), can be used to accomplish this task.


Go to:
Title Page               Chapter 1           Appendix A   Appendix J
Copyright                Chapter 2           Appendix B   Appendix K
Abstract                 Chapter 3           Appendix C   Appendix L
Acknowledgements         Chapter 4           Appendix D   References
Table of Contents        Chapter 5           Appendix E
List of Figure           Conclusions/        Appendix F
List of Tables           Future Directions   Appendix G
List of Audio Examples                       Appendix H
List of Programs                             Appendix I