SuperCollider sound generator

Piling 'Em Up

Until now, the examples have been pretty boring, but they are the basic building blocks of much more complex and interesting sounds. Because all functions and commands in SuperCollider return a value, you can use the output of one oscillator (e.g., sine) as a parameter for another oscillator. The following

{LFSaw.ar(200, 0, 1 * SinOsc.kr(1, 0, 1))}.scope;

modifies the multiplier (volume) parameter of the sawtooth wave (Figure 2, middle row, far left; a twangy, guitar-like sound), with the sine oscillator.

The scope at the end lets you see what's happening. Note the kr method appended to SinOsc, which is a control function (i.e, it is modulating, not generating, a sound) instead of ar, which is much more CPU intensive.

The sine oscillator frequency is set to 1Hz (1 cycle per second), so the saw oscillator's volume varies between 1 and -1 every second, rendering a siren-like sound.

Piling on as many functions as you like allows you to create really sophisticated effects. The following, for example, pans the sound from one speaker to the other:

{Pan2.ar( LFSaw.ar(200, 0, 1 * SinOsc.kr(1, 0, 1).abs), SinOsc.kr(0.5))}.play;

The Pan2 function is used to pan between two channels – the Pan4 function handles four-channel output – and takes as arguments the audio signal and a number between -1 (left) and +1 (right).

The default multiplier of the sine oscillator is 1, so in the second argument of Pan2 (SinOsc.kr(0.5)), you only have to specify the frequency. Now you will hear the siren on the right panning over to the left and back again every half second.

Mix and Match

Next, I'll look closely at the following line from the inside out:

{ Pan2.ar( Mix.fill( 8, { LFSaw.ar(200 + 200.0.rand, 0, 1/8)}), SinOsc.kr( 0.5 ) ) }.play;

Among the confusing array of fences, the Pan2.ar function has two parameters:

<C>Mix.fill( <n>, { LFSaw.ar(200 + <C><C>  200.0.rand, 0, 1/<n>) })<C>

and

<C>SinOsc.kr( <n> )<C>
  • LFSaw.ar(200 + 200.0.rand, 0, 1/8), the innermost function of the first parameter, looks similar to an earlier example: the sawtooth wave generator. The rand method affixed to 200.0 (a float number) returns a random number between 0.0 and 200.0, thus generating a sawtooth wave with a frequency between 200 and 400. The pulse starts at 0 with an amplitude (volume) of 1/8.
  • SinOsc.kr( 0.5 ), the second parameter of the Pan2.ar function, is also familiar. This function pans the sound wave between the right and left speaker every half a second.
  • Mix.fill( 8, { ... }) is new. The Mix function, as advertised, mixes two (or more) sound waves. In this case, its fill method mixes as many waves as indicated by the first parameter (8). The second parameter to Mix is the wave generator (i.e., the sawtooth oscillator). Here, eight slightly different sawtooth waves (i.e., with frequencies that vary randomly between 200 and 400Hz) are mixed to achieve something that sounds like a truck's horn in a traffic jam. The volumes of mixed waves are additive, so each wave is assigned a volume of 1/8; with eight waves, the total mixed signal has volume of 1.
  • Pan2.ar( ... ), finally, pans the effect from one speaker to the other every half second.

Although piling up functions gives you very sophisticated results, it can also get confusing quickly, which is why grouping is useful.

Listing 1 produces exactly the same effect as the one-line instruction, but it is much easier to read. Additionally, you can use a multicharacter variable to define how many waves (line 2) you want to mix.

Listing 1

Truck Horn

 

SynthDefs

Once you have built your own building blocks, you will want to save them and turn them into a shortcut for creating more sophisticated soundblocks by using SynthDefs. For those familiar with object-oriented programming (OOP), think of SynthDefs as classes that you can then use to generate "objects" by instantiating the Synths.

Listing 2 is an example of building on a simple sine wave oscillator. Line 2 defines a SynthDef named ("sinewave") and declares some arguments that can be used later as parameters when called. The new function in line 4, Out, writes a signal to a bus (channel).

Listing 2

Sine SynthDef

 

Here, the first parameter is an array ([0, 1]) that indicates it will be writing to channel 0 (left) and 1 (right). The second parameter is the function that will be generating the wave (i.e., a simple audio (ar) sine wave).

Finally, on line 5, the SynthDef gets added to the server with add so that it can be called from anywhere. If your SynthDef is really useful, you can save it for future sessions by changing add to writeDefFile and executing the block. Your SynthDef will then be loaded into the server every time it boots.

Next, call the SynthDef (line 1) and pass it some parameters (line 2) or change the tone and volume as it plays:

x=Synth("sinewave");
x=Synth("sinewave", [myfreq:400]);
x.set("myfreq", 200);

Although the SynthDef structures are very useful, they do have certain limitations compared with functions. For starters, a SynthDef is compiled, then the server runs the compiled, static version every time it is called. Therefore, if the SynthDef contains a random number generator, the random number will be calculated at run time, and the result is stored statically within the Synth, so it will not change any of the times the Synth is called.

You also cannot define dynamically the number of channels (buses) or waves you are going to use, again, because this has to be compiled into the Synth.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News