Lecture 5
Substractive Synthesis and Filters (with CLM examples)

Lecture Slides

A series of gif images of the lecture slides... (sorry, only accesible from within Stanford University)

Substractive Synthesis

Basic concepts.


input -> operation -> output; anything is a filter... usually applied to devices that boost or attenuate regions of the spectrum.
amplitude versus frequency response curve
Cutoff Frequency
half power point (0.707 or -3dB)
Center Frequency
[maximum|minimum] amplitude in a [bandpass|bandreject]
Stopband vs Passband
Q and Gain
Comb and Allpass Filters


Here is a bunch of very simple instruments that use the stock filters that come with the clm distribution ("/Lisp/clm").


A simple One Pole filter (filtering white noise)...

(definstrument onepole(start-time duration amplitude
				  (b1 '(0 0.5 1 0.5)))
  (multiple-value-bind (beg end) (get-beg-end start-time duration)
    (let* ((noise (make-randh :frequency (* 0.49 sampling-rate) 
			      :amplitude amplitude))
	   (b1-env (make-env :envelope b1))
	   (opfilt (make-one-pole :a0 1.0 :b1 0.5)))
       (loop for i from beg to end do
	     (setf (smpflt-b1 opfilt) (env b1-env))
	     (outa i (one-pole opfilt (randh noise))))))))


A simple One Zero filter (filtering white noise)...

(definstrument onezero(start-time duration amplitude
				  (a1 '(0 0.5 1 0.5)))
  (multiple-value-bind (beg end) (get-beg-end start-time duration)
    (let* ((noise (make-randh :frequency (* 0.49 sampling-rate) 
			      :amplitude amplitude))
	   (a1-env (make-env :envelope a1))
	   (ozfilt (make-one-zero :a0 1.0 :a1 0.5)))
       (loop for i from beg to end do
	     (setf (smpflt-a1 ozfilt) (env a1-env))
	     (outa i (one-zero ozfilt (randh noise))))))))


A simple Two Pole filter with resonance based on the ppolar clm ug (filtering white noise)...

(defmacro b1-from-r-freq (r freq) `(- (* 2.0 ,r (cos (in-hz ,freq)))))
(defmacro b2-from-r (r) `(* ,r ,r))

(definstrument twopole(start-time duration amplitude
				  (freq '(0 20 1 10000))
				  (r '(0 0.5 1 0.5)))
  (multiple-value-bind (beg end) (get-beg-end start-time duration)
    (let* ((noise (make-randh :frequency (* 0.49 sampling-rate) 
			      :amplitude amplitude))
	   (freq-env (make-env :envelope freq))
	   (r-env (make-env :envelope r))
	   (ppfilt (make-ppolar :r 0.5 :frequency 440.0)))
       (loop for i from beg to end do
	     (let* ((freq0 (env freq-env))
		    (r0 (env r-env)))
	       (setf (smpflt-b1 ppfilt) (b1-from-r-freq r0 freq0))
	       (setf (smpflt-b2 ppfilt) (b2-from-r r0))
	       (outa i (ppolar ppfilt (randh noise)))))))))


A simple Two Pole / Two Zero formant filter (filtering white noise)...

(defmacro set-formnt(filter freq r)
  `(let* ((freq ,freq)
	  (r ,r))
     (setf (smpflt-a2 (frmnt-tz ,filter)) (- r)
	   (smpflt-b1 (frmnt-tp ,filter)) (- (* 2.0 r (cos (in-hz freq))))
	   (smpflt-b2 (frmnt-tp ,filter)) (* r r))))

(definstrument simp-formnt(start-time duration amplitude
				  (freq '(0 20 1 10000))
				  (r '(0 0.707 1 0.707)))
  (multiple-value-bind (beg end) (get-beg-end start-time duration)
    (let* ((noise (make-randh :frequency (* 0.5 sampling-rate) :amplitude amplitude))
	   (freq-env (make-env :envelope freq
			       :start-time start-time
			       :duration duration))
	   (r-env (make-env :envelope r
			    :start-time start-time
			    :duration duration))
	   (fmfilt (make-formnt :frequency 440 :r 0.99)))
       (loop for i from beg to end do
	     (set-formnt fmfilt (env freq-env) (env r-env))
	     (outa i (formnt fmfilt (randh noise))))))))
All of these example instrument do some internal contortions to move through envelopes the center frequency, resonance and or assorted internal coefficients. Hope you can figure things out. Believe it or not everything that's being done is documented in the clm manual... :-)


Create an instrument that can filter white noise with three formant filters. Design the instrument so that it can read and interpret formant frequencies in order to emulate a "whispering voice". In the following code segment you'll find all the elements you need (a table of formant frequencies, a macro to set the coefficients of the filter and an instrument template). Once the instrument is running try it out, and have it whisper something...

(defvar *formants* 
    '((I . (390 1990 2550))  
      (E . (530 1840 2480)) 
      (AE . (660 1720 2410))
      (UH . (520 1190 2390))
      (A . (730 1090 2440))
      (OW . (570 840 2410))
      (U . (440 1020 2240))
      (OO . (300 870 2240))
      (ER . (490 1350 1690))
      (EE . (260 3500 3800))))

;;; macro to set the coefficients of the filters
;;; using frequency and r (Q) values

(defmacro set-formnt (filter freq r)
  `(let* ((freq ,freq)
	  (r ,r))
     (setf (smpflt-a2 (frmnt-tz ,filter)) (- r)
	   (smpflt-b1 (frmnt-tp ,filter)) (- (* 2.0 r (cos (in-hz freq))))
	   (smpflt-b2 (frmnt-tp ,filter)) (* r r))))

(definstrument <choose a name!> (start-time duration amplitude phoneme 
                              &key (amp-env '(0 0 0.5 1 1 0))
        			   (q-env '(0 0.99 1 0.99))
    			           (dictionary *formants*))
   (multiple-value-bind (beg end) (get-beg-end start-time duration)
    (let* (
           ;;; create a white noise generator to use as excitacion for the formants
           ;;; create amplitude and r (Q) envelopes
           ;;; extract the three frequencies from the formant dictionary...
           ;;; ...and create three formant filters centered on those frequencies
       (loop for i from beg to end do
          (let (
                ;;; as you will be using the noise generator as input to the three
                ;;; filters you'll probably need a local variable to hold its value
                ;;; (same for envelopes that will be used more than once)
             ;;; use the set-formnt macro to set the three frequencies and r's
             ;;; add up the three outputs from the filters and scale by the global
             ;;; amplitude envelope, send to outa or locsig and you are done!
             ;;; To get a slightly better rendition scale the amplitudes of the 
             ;;; second and third formants by 1/2 and 1/3...

;;; An example of calling the instrument

(with-sound()(<choose a name> 0 2 0.1 'E))

To look up keys in the formant dictionary use the "assoc" lisp function. More details can be found in the Lisp Workshop page for this week.

For extra credit try using the pulse-train clm unit generator instead of the white noise source, and also add periodic and random vibrato to the instrument. Using a pulse train as an excitacion will produce voiced sounds instead of whispers (it will sound more like a singing voice, specially when you add the right amount of vibrato).

©1996 Fernando Lopez-Lezcano. All Rights Reserved.