Music 680 (Spring 2006): Special Topics in Music Theory - Programming for Sound Synthesis
Assignment 2: due Wednesday, February 15, 2006

Consider the following CLM additive synthesis instrument, where "frequencies" and "amplitudes" parameters are understood to be arrays of envelope data. (This instrument is a good solution to the first assignment -- the number of oscillators is calculated on the fly based on the length of the "frequencies" and "amplitudes" arrays).

(definstrument additive (start-time duration frequencies amplitudes)
  (let* ((start-sample (* start-time *srate*))
         (end-sample (+ start-sample (* duration *srate*)))
         (number-oscillators (min (length frequencies) (length amplitudes)))
         (oscillators (make-array number-oscillators))
         (freq-envs (make-array number-oscillators))
         (amp-envs (make-array number-oscillators))
         (output-sample 0.0))
    (loop for counter from 0 below number-oscillators do
         (setf (aref oscillators counter)
               (make-oscil :frequency 0.0))
         (setf (aref freq-envs counter)
               (make-env :duration duration
                         :scaler (hz->radians 1)
                         :envelope (aref frequencies counter)))
         (setf (aref amp-envs counter)
               (make-env :duration duration
                         :envelope (aref amplitudes counter))))
     (loop for sample-number from start-sample to end-sample do
          (setf output-sample 0.0)
          (loop for counter from 0 below number-oscillators do
               (setf output-sample (+ output-sample
                                      (* (env (aref amp-envs counter))
                                         (oscil (aref oscillators counter) 
                                                (env (aref freq-envs counter)))))))
          (outa sample-number output-sample)))))

This instrument works well, and can potentially synthesize any sound -- after all, we've got a (more or less) unlimited number of potential oscillators, and complete frequency and amplitude control on a per-oscillator basis.

The problem with this instrument is the enormous amount of data it requires -- it's difficult to imagine a compositional process involving typing thousands of envelope points for hundreds of oscillators. Later in the course we'll look at ways to derive additive synthesis data from the mathematical analysis of recorded sound, but even before we get to that we can start to imagine automated or algorithmic techniques for generating the data we need.

For instance, we can create the envelopes with code like:

(defparameter frequency-envelopes (make-array 20))
(defparameter amplitude-envelopes (make-array 20))

(loop for counter from 0 to 19 do
 (setf (aref frequency-envelopes counter) 
       (list 0 (* (+ counter 1) 110)
             1 (* (+ counter 1) 330)))
 (setf (aref amplitude-envelopes counter)
       (list 0 0 
             0.5 (/ 0.5 (+ counter 1))
             1 0)))

which derives some of the envelope points from the current value of the loop counter. Your challenge for this week is to come up with an interesting way to generate additive synthesis data (where "interesting" could be defined as elegant to the eye, pleasing to the ear, or ideally both). This could either be done by creating more and more complex envelope structures (along the lines that we're pursuing here), or it could involve modifications to the instrument itself -- for instance, what if all partials followed a single frequency envelope, and then just scaled that value by a variable starting frequency for each partial? What if odd partials followed an inverted envelope from even? Let your imagination run wild here -- any mathematical function or trick you can come up with is fair game.

Once you've got a satisfying way of generating data, be sure to use it -- please include with your code a short composition (in the form of a (with-sound) call) that shows off your data-generating technique....