The real fun starts when you use some UGens to control the parameters of other UGens. The theremin example did just that. Now you have all the tools to understand exactly what was going on in one of the examples of section \ref{sec:first-sine}:
{SinOsc.ar(freq: LFNoise0.kr(12).range(500, 1500), mul: 0.1)}.play;
// Breaking it down:
{LFNoise0.kr(1).poll}.play; // watch a simple LFNoise0 in action
{LFNoise0.kr(1).range(500, 1500).poll}.play; // now with .range
{LFNoise0.kr(12).range(500, 1500).poll}.play; // now faster
The three last lines of the example demonstrate step-by-step how the LFNoise0
is used to control frequency. The message range
simply rescales the output of a UGen. Remember, LFNoise0
produces numbers between -1 and +1 (it is a bipolar UGen). Those raw numbers would not be very useful to control frequency (we need sensible numbers in the human hearing range). The .range
takes the output betwen -1 and +1 and scales it to whatever low and high values you provide as arguments (in this case, 500 and 1500). The number 12, which is the argument to LFNoise0.kr
, specifies the frequency of the UGen: how many times per second it will pick a new random number.
In short: in order to use a UGen to control some parameter of another UGen, first you need to know what range of numbers you want. Are the numbers going to be frequencies? Do you want them between, say, 100 and 1000? Less? More? Or are they amplitudes? Perhaps you want them to be between 0.1 (soft) and 0.5 (half the maximum)? Or are you trying to control number of harmonics? Do you want it to be between 5 and 19?
Once you know the range you need, use the .range
method to make the controlling UGen do the right thing.
Exercise: write a simple line code that plays a sine wave, the frequency of which is controlled by a LFPulse.kr
. Provide appropriate arguments inside after the kr
. Then, use the .range
method to scale the output of LFPulse
into something that you want to hear.
mul
and add
You already know how to scale the output of UGens in the server using the method .range
. The same thing can be accomplished on a more fundamental level by using the arguments mul
and add
, which pretty much all UGens have. The code below shows the equivalence between range
and mul/add
approaches, both with a bipolar UGen and a unipolar UGen.
// This:
{SinOsc.kr(1).range(100, 200).poll}.play;
// ...is the same as this:
{SinOsc.kr(1, mul: 50, add: 150).poll}.play;
// This:
{LFPulse.kr(1).range(100, 200).poll}.play;
// ...is the same as this:
{LFPulse.kr(1, mul: 50, add: 100).poll}.play;
Figure \ref{fig:mul-add-scale} helps visualize how mul
and add
work in rescaling UGen outputs (a SinOsc
is used as demonstration).