The Basics of CLM

by Nicky Hind

List of Topics


A Template for DEFINSTRUMENT

In CLM, instruments are defined by using the macro DEFINSTRUMENT, a template for which is as follows.
The contents of brackets "< > " indicate sections to be defined by the user.
Comments are preceded with a semi-colon, ";".

(definstrument <name> (<arg1 arg2 arg3...>) 
                          ; a name, followed by a list of args to
                          ; be called by the user

    ; initialization variables and values
  (let ((< init-variable1 value1> )
	(< init-variable2 value2> )
	(< init-variable3 value3> ))

     ; the RUN loop computes each sample, one at a time
    (Run
     (loop for i from < first sample>  to < last sample>  do
     ; the "body" defines exactly how each sample is to be computed
	   < body> ))))


Example 1: A Simple Sine-Wave Instrument

This example demonstrates the RUN loop; the variables (commonly named) BEG and END; and the functions MAKE-OSCIL and OSCIL.

(definstrument examp1 (start-time duration frequency amplitude)

  (let* ((beg (floor (* start-time sampling-rate)))
	 (end (+ beg (floor (* duration sampling-rate))))
	 (sine-wave (make-oscil :frequency frequency)))

    (Run   ; Run ensures that the DSP (not RAM) will be used
	   ; to compile the samples -- it's much faster!
     (loop for i from beg to end do 
	   (outa i (* amplitude (oscil sine-wave)))))))


Compiling Instrument Definitions

Once defined, a CLM instrument must be compiled before it will run. This is done by converting the file which contains the code into binary (where it acquires the ".fasl" extension). There are several ways to do this, but perhaps the simplest way is to use LISP's top-level command, :cl, for compile and load. This command takes one argument, the file name to be compiled and loaded, so for example, to compile the file, "test.lisp" which is in your home directory, enter the following.

   :cl ~/test.lisp

Remember that every time you modify your instrument, you will have to first save the file, and then re- compile and load, as described above.


Calling Instruments with WITH-SOUND

To call the instrument defined above, use the function WITH-SOUND.

With these arguments, it will sound a bit like a tuning fork.

      (with-sound () (examp1    0    1    440.0    0.75))
                        |       |    |      |       |
corresponding with:   name   start  dur   freq     amp

The empty parens following with-sound are where certain global values can be specified (such as the number of channels, sampling rate, name of resulting soundfile etc.), all of which have defaults. More on this later.

WITH-SOUND automatically plays the sound once it is computed. This is done by (invisibly) invoking the DAC function. To hear the sound again, you will need to do this explicitly. Just type:

(dac)

DAC, like this, with no arguments plays the last soundfile computed. To hear some other soundfile, you will need to give the pathname as an argument. Eg.

(dac "/zap/my-sound.snd")


Example 2: Sine-Wave Instrument with Envelope Control

(definstrument examp2 (start-time duration frequency amplitude 
		       &optional (amp-env '(0 0 50 1 100 0)))
  (let* ((beg (floor (* start-time sampling-rate)))
	 (end (+ beg (floor (* duration sampling-rate))))
	 (sine-wave (make-oscil :frequency frequency))
	 (amp (make-env :envelope amp-env :scaler amplitude 
			:start-time start-time :duration duration)))
    (Run 
     (loop for i from beg to end do
	   (outa i (* (env amp) (oscil sine-wave)))))))

This is basically the same as example 1. A new argument have been added to the argument list: amp-env. This has been made an optional argument, and if not specified when the instrument is called, it will take as a default value the shape given by the list (0 0 50 1 100 0) -- a simple triangle. Observe the syntax carefully.

A new initialization variable, amp is also used. This creates an envelope structure with MAKE-ENV, using the values of amplitude (as a scaler) and amp-env (as an envelope shape). Having set up the envelope in this way, it is called in the Run loop with ENV, and it's value multiplied by the value from sine-wave, to produce a sample value. In CLM, structures that are set up using MAKE-* (where * is the name of a structure) are called in the RUN loop with the name of the structure itself. Eg.

Initialization Run loop Structure
MAKE-OSCIL OSCIL a sine wave oscillator
MAKE-ENV ENV an envelope function
MAKE-DELAY DELAY a delay line


Error Messages and Debugging

CLM carries out a certain amount of error-checking for you, and reports back with error messages if something seems wrong. A lot of the time, errors are caused by typos: for example, supposing in example 2, the envelope variable amp is misspelled as "imp", this would produce the following errors.

Warning: Symbol AMP declared special
Warning: variable IMP is never used

In general, something is "declared special" if it is called in the Run loop without having been defined elsewhere. A warning is also given if a variable defined in the let, is not called in the Run loop. These are handy, and make debugging a lot easier.


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