Music 220a, Handout #3

15-Oct-97

I. Music at the signal level

definstrument code must be compiled and loaded before use. Copying and pasting the definition into the LISP interpreter will result in errors.

Compiling and loading can be done either with the LISP functions (compile-file "<filename>"), (load-file "<filename>") or using the ACL shortcut :cl <filename>.. Note that the corresponding Stella command does not start with a colon! At the Stella prompt, type cl <filename> instead.

Instrument definitions should be kept in discrete files. It is common to include sample (with-sound () ... ) calls in the same file but these should be commented out.

As an example consider the instrument in the following section:

Proving the Nyquist Theorem

The Nyquist Theorem: to represent digitally a signal containing frequency components up to f Hz it is necessary to use a sampling rate of more than 2f samples per second. The result of disobeying this theorem is the introduction of aliasing. To test the effects of aliasing let us create an instrument that generates a sine wave with a ramping envelope applied to the frequency parameter.

(definstrument swoon (start-time duration minfreq maxfreq amplitude)      
  (multiple-value-bind (beg end) (get-beg-end start-time duration)     
    (let ((s (make-oscil :frequency 0))           
	  (f (make-env :base 3 :envelope '(0 0 1 1) :duration duration
		       :offset minfreq :scaler (- maxfreq minfreq))))
      (run        
       (loop for i from beg to end do
	     (setf (frequency s) (env f))         
	     (outa i (* amplitude (oscil s))))))))
Remember: This instrument must be in a separate file and compiled and loaded into LISP!

A blow by blow account of swoon. Swoon has 5 parameters, start-time, duration, the beginning freq, the target frequency, and amplitude. There are two unit generators called, an oscilator (s) and a line segment envelope generator (f). As each sample is written within the loop, the effect of the envelope on the frequency is computed and scaled by the amplitude. Now, recall that the with-sound function has those mysterious all-too-often empty parentheses between the function name and the instrument call. This is the place to make global changes (such as channel numbers, and sampling rate settings. Well... let's swoon - from 20 KHz (that's 20,000 Hz) down to 200 Hz in 3 seconds at a sampling rate of 44.1 KHz (that's 44,100 samples per second). Eat your heart out Celine...

;;; a 20,000 Hz - 200 Hz  swoon... ;;;
(with-sound(:srate 44100)(swoon 0 3 20000  200 .3))

;;; the same swoon at a lower sampling rate... ;;; 
(with-sound(:srate 22050)(swoon 0 3 20000 200 .3))

;;; and at a very low sampling rate... ;;; 
(with-sound(:srate 8192)(swoon 0 3 20000 200 .3))
For a good time, look at the resulting sound files in a spectrogram application such as "snd" on Linux/SGI (enable spectrogram display) or "Spectro" on NeXT's.

II. Music at The Score Level

OK... Now we go to Common Music. If you haven't done this already, type (stella) to the LISP interpreter. This brings up Common Music's interactive environment. The biggest word of warning here is that whereas CLM is pure LISP, Stella is not. Stella departs from pure LISP in a number of important ways. They will ultimately be useful but for now they are bound to confuse. Before we specify these differences let's meet the Item Stream, Common Music's way of encapsulating any kind of sequential data.

An item stream enumerates data according to a specified pattern. The behavior of an item stream is controlled by two characteristics, a stream type and a pattern type. Stream types include: notes, rhythms, amplitude, and others. Pattern types include: cycle, accumulation, palindrome, random, heap, and others.

In the following examples we will continue to use our swooner. Remember - if you are starting from here - after entering the LISP interpreter type (stella) at the prompt compile and load the "swoon" instrument using the cl command. Open a sound file to write to, if you haven't done so already. Do this by typing: open /tmp/test.snd.

In the following example we create an algorithm in which the arguments to minfreq and maxfreq are set using item streams. An algorithm generates musical events. The template for an algorithm using item streams is:

(algorithm <name> <even-class> (<initializations>)
  (setf <parameter-name-1> (item <item-stream-1>)
	...
	<parameter-name-n> (item <item-stream-n>)))

Thus:

(algorithm item-stream-example1 swoon (length 10  duration .2 rhythm .4 
 				       amplitude .05)
  (setf minfreq (item (pitches c4 e g bf c5)) 	
	maxfreq (item (pitches c3 c5 c6 c2 c1))))

So what is going on in the following example?

(algorithm item-stream-example2 swoon (length 30  duration .2 rhythm .4
				       amplitude .05)
  (setf minfreq (item (pitches c4 e g bf c5)) 	
	maxfreq (item (pitches c3 e g bf c4 e5))))

Note that the period lengths are different.

And here?

(algorithm item-stream-example3  swoon (length 30  duration .2 rhythm .4
					amplitude .05) 
  (setf minfreq (item (pitches c4 e g bf c5 
			       in palindrome elided t)) 
	maxfreq (item (pitches c3 e g bf c4 e5))))

Recursive nestings of item streams are possible...

(algorithm item-stream-example4  swoon (length 30 duration .1 rhythm .1
					amplitude .05) 
  (setf minfreq (item (pitches c4 e g bf
			       (notes c5 e g c6
				      for 8 
				      in palindrome
				      elided t)
			       g2))
	maxfreq (item (pitches c3 e g bf c4 e5 in heap))))

And the period lengths may change dynamically:

(algorithm item-stream-example5 swoon (length 30 duration .1 rhythm .1
				       amplitude .05) 
  (setf minfreq (item (pitches c4 e g bf (notes c5 e g c6
						for 8  
						in palindrome
						elided t)
			       g2)) 	
	maxfreq (item (pitches c3 e g bf c4 e5  
			       (pitches c4 d ef f g
					for (items 1 2 3 4 in random))))))