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
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.
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