Sound Processing Techniques - part 2 of 2: Effects

by Nicky Hind

List of Topics


Introduction

Digital effects processors are widely used in audio work today, and, along with synthesizers and samplers, form one of the most popular types of music technology device on the market. Many of these devices provide excellent results, and use quite sophisticated algorithms to provide the user with a high degree of control over the output sound. But some would perhaps argue that for greater flexibility and sheer quality of sound, software implementations are still the best. This document looks at some of the more commonly used `effects' techniques (reverberation, flange etc.), and their implementation in CLM.


Reverberation Basics

Reverberation is probably one of our most important aural cues: it conveys information about the distance to the sound source, and the nature of the environment in which it appears. Reverberation consists of many thousands of very densely packed echos, as a sound emanates from its source in all directions, bouncing and re-bouncing off all possible surfaces untill finally its energy is absorbed. The reflectivity of surfaces in a given space (whether hard or soft, smooth or uneven), as well as the shape and volume, play a key role in determining the acoustic characteristics of that space, and are of fundamental importance to the study of room acoustics. Reverberation is generally measured by the amount of time taken for a signal to decay by 60 dB, and this is known as the reverberation time. Other important aspects are the time lag between the direct sound and its first reflections (greater than 50msec are usually perceived as distinct echoes, and the shorter this time, the smaller the room appears to be), the duration of these so-called early reflections - ie. before the onset of dense reflections (the ear cannot distinguish between densities greater than 1 reflection per msec) - and the relative attenuation of higher frequencies as compared with lower frequencies.

In order to simulate the necessary density of reflections for natural sounding reverberation, a system which combines all-pass and comb filters, along with delay lines (for the early reflections) and a low pass filter (for high frequency attenuation) is generally employed. An alternative method in which the input sound source is convolved with noise is far more computationally expensive, and thus less frequently employed. As a prelude to examining a reverberation instrument, it will be useful to take some of the building blocks in isolation, to find out what they do.


The Comb Filter

The comb filter is a simple recirculating delay line, in which the signal is attenuated (ie. multiplied by some amount less than one) on each cycle.

The characteristic of the comb filter is to emphasize the frequency (and its harmonics) of the reciprocal of the length (in time) of the delay line - ie. 1 / tau (where tau is the delay time in seconds). This is known as the natural frequency of the filter, and with respect to the sampling rate can be found with the equation:

                                 sampling-rate
                           -------------------------
                           delay length (in samples)

Here is a simple implementation of a comb-filter which takes an input soundfile.
This and the following series of instruments are all based on the READIN instrument in Example 1: A Comb Filter Instrument

(definstrument comb-filter (start-time file gain length
                            &key (duration -1.0) 
                                 (onset 0.0) 
                                 (ampscl 1.0))
     (let ((f (open-input file)))
	   (unwind-protect
 	     (let* ((beg (floor (* start-time sampling-rate)))
                (real-duration (if (plusp duration)
                                  (- duration onset)
                                  (- (clm-get-duration f) onset))) 
                (end (+ beg (floor (* sampling-rate real-duration))))
                (read-input (make-readin :file f :start-time onset))
		;; the comb filter is a CLM `unit generator' and takes a GAIN factor
		;; and delay length (in samples) as arguments
                (comb-filt (make-comb gain length))
                (cnt 1))

	     (Run
		   (loop for i from beg to end do
		;; the input soundfile is fed into the comb filter
		      (outa i (* ampscl (comb comb-filt (readin read-input))))
			  (when (= i (* sampling-rate cnt)) (print cnt) (incf cnt))))
	     (close-input f)))))

;; Since it might be desirable to `tune' the comb filter to given 
;; pithces, here is a macro that converts a pitch (note name) into
;; the corresponding number of samples with respect to the sampling-rate

(defmacro natural-freq (pitch)
  `(floor (/ sampling-rate (pitch ',pitch))))


Some Calls to the comb-filter Instrument

1. Emphasizing the harmonics of the input soundfile by tuning the comb-filter to its pitch (the first two calls use a flute sound playing the note D4):

	(with-sound () 
	  (comb-filter 0 "<pathname of D4 flute soundfile>" 0.75 (natural-freq d4) 
                 :ampscl 0.3))

2. Same idea, only de-emphasizing the harmonics by tuning the comb-filter `away' from d4:

	(with-sound () 
	   (comb-filter 0 "<pathname of D4 flute soundfile>" 0.75 (natural-freq ef4) 
                 :ampscl 0.3))

3.Emphasizing a certain frequency from an input speech soundfile.

	(with-sound () 
	   (comb-filter 0 "<pathname of voice soundfile>" 0.75 (natural-freq c3) 
                 :ampscl 0.6))

4. Attempting to play a tune on the comb-filter:

	(with-sound () 
	   (comb-filter 0 "<pathname of input soundfile>" 0.9 (natural-freq c3)
		 :ampscl 0.6)
	   (comb-filter 2.25 "<pathname of input soundfile>" 0.9 (natural-freq e3)
		 :ampscl 0.6)
	   (comb-filter 4.5 "<pathname of input soundfile>" 0.9 (natural-freq g3)
		 :ampscl 0.6)
	   (comb-filter 6.75 "<pathname of input soundfile>" 0.9 (natural-freq bf3)
		 :ampscl 0.6))


The All-Pass Filter

For reasons which will be apparent from the following diagram, the all-pass filter is sometimes known as a feedback/feedforward network. It can be thought of as an extension to the comb-filter.

All-Pass filters are used for a variety of purposes, but on their own, the auditory results are not that obvious - save, that is, for the case of short bursts of sound, where the delay line system has an audible result.


Example 2: An All-Pass Filter Instrument

(definstrument all-pass-filter (start-time file fb-scl ff-scl length
				&key (duration -1.0) (onset 0.0) (ampscl 1.0))
     (let ((f (open-input file)))
	    (unwind-protect
	     (let* ((beg (floor (* start-time sampling-rate)))
                (real-duration (if (plusp duration) 
                                  (- duration onset)
				                   (- (clm-get-duration f) onset)))
                (end (+ beg (floor (* sampling-rate real-duration))))
                (read-input (make-readin :file f :start-time onset))
		;; MAKE-ALL-PASS takes values for the feedback and feedforward
		;; attenuaters (the latter usually being the negation of the former)
		;; and the length of the delay line (in samples)
                (all-pass-filt (make-all-pass fb-scl ff-scl length))
                (cnt 1))

	     (Run
		   (loop for i from beg to end do
              (outa i (* ampscl (all-pass all-pass-filt (readin read-input))))
              (when (= i (* sampling-rate cnt)) (print cnt) (incf cnt))))
         (close-input f)))))


An Example Call to the all-pass-filter Instrument

1. Attempting to illustrate the impulse response by feeding in a short sound burst:

	(with-sound () 
	   (all-pass-filter 0 "<pathname of click soundfile>" 1.0 -1.0 200 :duration 3))


Effects Using Variable-Length Delay Lines (1):
The Movable Comb Filter

With a variable length delay line, the emphasised frequencies of the comb filter can change during the course of a computed soundfile, causing frequency `sweeps'. The following instrument is identical to the previous comb-filter instrument except that the delay line length is controlled by the output of an envelope.


Example 3: A Movable Comb-Filter Instrument

(definstrument movable-comb-filter (start-time file gain min-length max-length
				    &key (duration -1.0) (onset 0.0) (ampscl 1.0))
  (let ((f (open-input file)))
    (unwind-protect
	(let* ((beg (floor (* start-time sampling-rate)))
	       (real-duration (if (plusp duration) 
				  (- duration onset)
				(- (clm-get-duration f) onset)))
	       (end (+ beg (floor (* sampling-rate real-duration))))
	       (read-input (make-readin :file f :start-time onset))
	       ;; ZDELAY is CLM's variable length delay line. It takes an initial
	       ;; length as first argument (in samples), and a maximum possible
	       ;; as the value of TRUE-LENGTH. ZDELAY allows the delay increment
	       ;; value to be a non-integer, and interpolates bwtween the values on
	       ;; either side. Its capabilites are not unlimited: too much variation
	       ;; will result in glitches!
	       (delay-line (make-zdelay min-length :true-length max-length))
	       ;; The envelope structure by which the frequency sweep is attained.
	       ;; This is used to provide an offset from the otherwise current
	       ;; read position of the delay line. The envelope shape is set to a
	       ;; simple ramp.
	       (sweep-env (make-env :envelope '(0 0  100 1)
				    ;; scaler is the difference between the max-length
				    ;; and min-length -- ie. when the envelope
				    ;; is at zero, the output value will be zero
				    ;; and normal read position will pertain;
				    ;; when the envelope is at 1, the envelope
				    ;; output will give the maximum possible value
				    ;; to OFFSET (see RUN loop)
				    :scaler (- max-length min-length) 
				    :start-time start-time 
				    :duration real-duration))
	       (cnt 1))

	  (Run
	   (loop for i from beg to end do
		 ;; a value for OFFSET is obtained from SWEEP-ENV,
		 (let* ((offset (env sweep-env))
			;; which is used to `TAP' the delay line at that position
			(del-output (ztap delay-line offset)))
		   (outa i (* ampscl (zdelay delay-line
				     ;; Input to the delay line is the sum of attenuated
				     ;; previous output, with incoming READIN samples
					     (+ (* del-output gain) 
						(readin read-input))
				     ;; the length of the delay line is reset with the
				     ;; value of OFFSET
					     offset)))
		   (when (= i (* sampling-rate cnt)) (print cnt) (incf cnt)))))
	  (close-input f)))))


Some Calls to the movable-comb-filter Instrument

1. A frequency sweep from middle C to the octave below on an input speech sound:

	(with-sound () (movable-comb-filter 0 "<pathname of input soundfile>" 0.95 
		                            (natural-freq c4) (natural-freq c3)))


Effects Using Variable-Length Delay Lines (2): Flange

The well known flange effect is obtained when a signal is added to a slightly delayed version of itself, and the amount of delay is modulated (usually quite slowly) over time.

The DEPTH paramter controls the proportion of flanged- to dry-signal


Example 4: A Flange Instrument

(definstrument flanger (start-time file length length-off flange-freq depth
		        &key (duration -1.0) (onset 0.0) (ampscl 1.0))
  (let ((f (open-input file)))
    (unwind-protect
	(let* ((beg (floor (* start-time sampling-rate)))
	       (real-duration (if (plusp duration) 
				  (- duration onset)
				(- (clm-get-duration f) onset)))
	       (end (+ beg (floor (* sampling-rate real-duration))))
	       (read-input (make-readin :file f :start-time onset))
	       (delay-line (make-zdelay length 
					:true-length (+ length length-off)))
	       ;; Here, the `sweep' factor is controlled not by
	       ;; an envelope, but by an oscillator
	       (sweep-oscil (make-oscil :frequency flange-freq))
	       (cnt 1))

	  (Run
	   (loop for i from beg to end do
		 ;; the output from the sweep oscillator is
		 ;; multiplied by the the value of LENGTH-OFF.
		 ;; Thus the delay line length will modulate
		 ;; plus and minus the basic length (LENGTH) with
		 ;; LENGTH-OFF.
		 (let* ((offset (* length-off (oscil sweep-oscil)))
			(direct-sig (readin read-input))
			(flange-sig (zdelay delay-line direct-sig offset)))
		   (outa i (* ampscl (+ direct-sig (* depth flange-sig))))
		   (when (= i (* sampling-rate cnt)) (print cnt) (incf cnt)))))
	  (close-input f)))))


Some Calls to the flange Instrument

1. With the speech sound:

	(with-sound () 
	   (flanger 0 "<pathname of input soundfile>" 20 10 0.2 1))

2. Using a harp sound:

	(with-sound () 
           (flanger 0 "<pathname of input soundfile>" 10 10 0.1 1 :ampscl 0.5))


More On Reverberation

The implementation of reverberation is essentially very simple, and consists of a bank comb-filters in parallel, feeding into a bank of all-pass filters in series (or, as its often called, in cascade). It was first suggested in this form in the early 1960s by M.R. Schroeder.

Great care is taken to ensure a smooth output by setting the delay line lengths of the various filters to prime numbers, so that mutual periodicity between them is minimized.

Here is a simplified version of John Chownings reverb instrument - the unabridged version can be found in the CLM directory (yes, along with all other example instruments, documentation, and source code etc.!). This is actually the reverse of the above diagram in that the incoming source sound first passes through the bank of in-series all-pass filters, and then through the in-parallel comb filters.


Example 5: John Chowning's Reverb Instrument

(definstrument JCreverb (start-time duration)
  (let*
	((beg (floor (* start-time sampling-rate)))
	 (end (+ beg (floor (* duration sampling-rate))))
 ;; Structures for 3 (in this case) all-pass filters
 ;; notice the prime number values of the delay lengths, 3rd argument
	 (allpass1 (make-all-pass 0.700 -0.700 1051))
	 (allpass2 (make-all-pass 0.700 -0.700  337))
	 (allpass3 (make-all-pass 0.700 -0.700  113))
 ;; Structures for 4  comb filters
 ;; again notice the value of the delay lengths, 2nd argument
	 (comb1 (make-comb 0.742 4799))
	 (comb2 (make-comb 0.733 4999))
	 (comb3 (make-comb 0.715 5399))
	 (comb4 (make-comb 0.697 5801))
 ;; Delay lines to implement `early reflection'
	 (outdel1 (make-delay (* 0.013 sampling-rate)))
	 (outdel2 (if (stereo) (make-delay (* 0.011 sampling-rate))))
	 (allpass-sum 0.0)
	 (comb-sum 0.0)
	 (chan2 (stereo)))
   (Run
     (loop for i from beg to end do
 ;; REVIN is the input reverb stream and is passed successively to
 ;; the 3 all-pass filters
       (setf allpass-sum 
			 (all-pass allpass3
			   (all-pass allpass2
			     (all-pass allpass1 (revin i)))))
 ;; the resulting ALL-PASS-SUM is then fed into eahc of the 4 comb
 ;; filters in parallel, and collected into the variable, COMB-SUM
       (setf comb-sum 
		  (+ (comb comb1 allpass-sum)
			 (comb comb2 allpass-sum)
			 (comb comb3 allpass-sum)
			 (comb comb4 allpass-sum)))
       (outa i (delay outdel1 comb-sum))
       (if chan2 (outb i (delay outdel2 comb-sum)))))
    (end-run)))

Reverb instruments in CLM are usually not called directly, but are referenced within the first parens after WITH-SOUND 1 eg. (with-sound (:reverb JCreverb) (...)) . Of course, the appropriate reverb instrument must also have been compiled and loaded before it can be called in WITH-SOUND. There are two ways of adding reverb to a soundfile: one using LOCSIG (which is probably the preferred method), and the other uses the function REVIN in conjunction with the straight OUTA OUTB... structures - see ins.lisp for an example of the latter. Also, Fernando Lopez-Lezcano's DLOCSIG is an extension to LOCSIG into 4 channels, where sound can be dynamically moved an reverberated.

Although once again, the simple READIN instrument is used, the same principles would apply to any instrument at all 1 be it a synthesis or sound-processing instrument.

(definstrument reverb-readin (start-time file 
			      &key (duration -1.0) (onset 0.0) (ampscl 1.0)
				   (amp-env-shape '(0 1  100 1))
				   (degree 0.0) (distance 1.0) (reverb-amount 0.005))
  (let ((f (open-input file)))
    (unwind-protect
	(let* ((beg (floor (* start-time sampling-rate)))
	       (real-duration (if (plusp duration) 
				  (- duration onset)
				(- (clm-get-duration f) onset)))
	       (end (+ beg (floor (* sampling-rate real-duration))))
	       (readin-sig (make-readin :file f :start-time onset :channel :A))
	       (amp-env (make-env :envelope amp-env-shape
				  :scaler ampscl 
				  :start-time start-time
				  :duration real-duration))
	       (loc (make-locsig :degree degree 
				 :distance distance 
				 :revscale reverb-amount))
	       (cnt 1))

	  (Run
	   (loop for i from beg to end do
		 (locsig loc i (* (env amp-env) (readin readin-sig)))
		 (when (= i (* sampling-rate cnt)) (print cnt) (incf cnt))))
	  (close-input f)))))


Some Reverberated Soundfiles

1. With default values:

	(with-sound (:reverb jcreverb) 
	    (reverb-readin 0 "<pathname of input soundfile>"))

2. In order to be able to use the DEGREE argument, channels must be set to 2, and in this case values range from 0-90:

	(with-sound (:reverb jcreverb :channels 2) 
	   (reverb-readin 0 "<pathname of input soundfile>"
			   :degree 90
			   :distance 3
			   :reverb-amount 0.05)) 

3. With lots of reverberation:

	(with-sound (:reverb jcreverb :channels 2) 
	   (reverb-readin 0 "<pathname of input soundfile>"
			   :degree 45
			   :distance 2
			   :reverb-amount 0.5))


Some interesting Papers on Reverberation

James A. Moorer, "About This Reverberation Business" Computer Music Journal (from late 1970s ?)

John Chowning, "The Simulation of Moving Sound Sources" Journal of the A.E.S., 1970.

Both of these papers are available from CCRMA.


Table of Contents
The Basics | Additive Synthesis (1/2) | Additive Synthesis (2/2) | Frequency Modulation Synthesis (1/2)
Frequency Modulation Synthesis (2/2) | Sound Processing (1/2) | Sound Processing (2/2) | Physical Modelling