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

Consider the following CLM additive synthesis instrument:

(definstrument additive3 (start-time duration freq1 freq2 freq3 amp1 amp2 amp3)
    (let* ((start-sample (* start-time *srate*))
           (end-sample (+ start-sample (* duration *srate*)))
           (oscil-array (make-array 3))
           (amp-array (make-array 3))
           (freq-array (make-array 3)))
      (setf (aref oscil-array 0) (make-oscil :frequency 0))
      (setf (aref oscil-array 1) (make-oscil :frequency 0))
      (setf (aref oscil-array 2) (make-oscil :frequency 0))
      (setf (aref amp-array 0) (make-env :envelope amp1 :duration duration))
      (setf (aref amp-array 1) (make-env :envelope amp2 :duration duration))
      (setf (aref amp-array 2) (make-env :envelope amp3 :duration duration))
      (setf (aref freq-array 0) (make-env :envelope freq1 :duration duration :scaler (hz->radians 1)))
      (setf (aref freq-array 1) (make-env :envelope freq2 :duration duration :scaler (hz->radians 1)))
      (setf (aref freq-array 2) (make-env :envelope freq3 :duration duration :scaler (hz->radians 1)))
       (loop for index from start-sample to end-sample do
            (outa index (+ (* (env (aref amp-array 0))
                              (oscil (aref oscil-array 0) (env (aref freq-array 0))))
                           (* (env (aref amp-array 1))
                              (oscil (aref oscil-array 1) (env (aref freq-array 1))))
                           (* (env (aref amp-array 0))
                              (oscil (aref oscil-array 2) (env (aref freq-array 2))))))))))

This works nicely, and provides a fair amount of flexibility (independent amplitude and frequency envelopes for each partial), but the limitation to only three partials is a drag. Robust additive synthesis almost always requires more oscillators -- thousands, for some applications.

There's an easy way to scale this instrument up to a thousand oscillators -- but who wants to do that much typing? Aren't computers supposed to do repetitive tasks for us? Your challenge for this week is to rewrite this code to create an instrument which can perform additive synthesis with an arbitrary number of oscillators -- specified by the user, at run-time (that is, inside of a (with-sound) call).

The very particular structure of this instrument -- with all the unit generators created inside of arrays -- will work to your advantage here. Remember that you can use the (make-array) function to create an array of any size, if you call the function with a user-specified argument rather than a specific number. Once you've got your arrays you'll need to fill them with unit generators -- think about using a (loop) to make that possible. Another (loop) nested inside the run loop will probably be useful to access the unit generators and get sample values from them.

This is a challenging first assignment -- good luck, and please email me if you need assistance!