Further Soundmaking

1 defsynth

We've been using the proxy macro to define and redefine sounds, but this limits us to sounds that play continuously. Most music is made from sounds that are temporal and periodic, so we need a way to define a sound that can be re-triggered.

proxy is good for testing out sounds, but once we've made something that we want to use, we define it with defsynth. Here's a basic example:

(defsynth drum ((freq 3000))
  (let* ((env (env-gen.ar (perc 0.001 0.1) :act :free))
         (sig (lpf.ar (white-noise.ar) (* freq env))))
    (out.ar 0 (pan2.ar sig 0 0.2))))

It's not the most exciting drum sound of all time, but now at least you can re-trigger it. To do so, just evaluate (synth 'drum :freq 3000).

Notice that defsynth is very similar to defun. With defsynth you provide the name of the synth (drum), its parameters ((freq 3000)), and the body, which describes how it generates its sound.

In the case of this synth, we create two variables, env and sig. env is bound to an envelope generator UGen, env-gen.ar, which takes an envelope specification and generates an audio signal from that specification. (perc 0.001 0.1) is the function generating the specification; its first parameter is the time in seconds to rise from 0 to 1, and the second parameter is the time to fall back to 0. The :act :free keywords for env-gen specify that once the envelope has reached its end, the synth will be removed from the server, freeing up CPU resources.

The signal (sig) being generated is white noise (white-noise.ar) with its high frequencies dampened by the lpf low pass filter. The filter's frequency (lpf's second argument) is the envelope generator multiplied by the synth's freq argument. Since the perc function generates an envelope that goes from 0 to 1 and then to 0 again, multiplying it by frequency is a simple way to change the brightness of the sound. Try the synth function with different values for its :freq key, from 20 to 10000 to get a feel for how it affects the sound.

The last line of the synth definition contains the out.ar UGen. When we use the proxy function, this UGen is automatically inserted for us for convenience, but when we define a synth manually, we have to specify it. out's first argument is the index of the bus that you want to write to–in this case we just use 0 since we want the sound to go to our speakers. out's second argument is a UGen or list of UGens that are generating the sound. Up to this point, we've only been generating mono (single channel) sounds. The pan2 UGen accepts a mono signal as input (sig in our case) and converts it into a stereo signal based on its second argument, which specifies the panning position (here we use 0 to position it centrally). Finally, pan2's third argument is an amplitude input, so we multiply the overall volume of the synth by 0.2.

2 Best Practices

If you're going to make music with cl-collider, you're likely to end up writing a lot of ~defsynth~s. Because of this, it may be a good idea to use a template system for your editor. For example, Emacs users can use skeleton or yasnippet. Both of these allow you to create a template and easily insert it into a file so you don't have to do as much manual typing.

You may want to use something like the following as your basic template synth:

(defsynth newsynth ((gate 1) (freq 440) (amp 0.5) (pan 0) (out 0))
  (let* ((env (env-gen.kr (adsr 0.001 0.1 0.5 0.1) :gate gate :act :free))
         (sig (saw.ar freq)))
    (out.ar out (pan2.ar sig pan (* amp env)))))

This is a good synthdef to start from because it includes most of what you'll need to get started: standard arguments, an envelope that frees the synth when complete, and of course, UGens to generate and output sound.

gate, freq, amp, pan, and out are all fairly common and standard arguments for synthdefs. gate is used to tell the synth whether it should play or release. If you were playing on a piano, gate would be 1 when you press a key, and would become 0 when you release the key. Because we provide gate to the env-gen, the envelope will start when the gate is 1 and will proceed to the release section when the gate becomes 0. Then, when the release section ends, the envelope will free itself.

freq is the standard argument name for sending pitch information to a synthdef. If you want to control the pitch of a synth using a midi note number instead, you can use the midicps function to convert the midi note number to a frequency. Like so:

(defparameter *ns* (synth 'newsynth :freq (midicps 40)))

We don't have to specify all of the arguments to newsynth since they all have default values.

Since the synth will sustain until we specifically tell it to release, we save the result of calling the synth function to the *ns* variable. When we want to release it, we can just call release on *ns*, like so:

(release *ns*)

3 More UGens

Last updated: 2019-03-12 Tue 19:47