20 -Variables, Arguments, Expressions, and User Defined Functions 

Variables 

Variables are names for storage locations in the computer. They are called variables because 
they change as the program runs. I think of them as containers like mail slots where you can 
place and retrieve numbers, arrays, strings, characters, entire functions, etc. All you have to 
do is declare them at the beginning of the program. Declaration uses the syntax var followed 
by the names, each separated by a comma and ending with a semicolon. A semicolon 
punctuates the end of an expression. You can name them anything you want. You can't start 
with a number or upper case letters and they have to be a contiguous. Many programmers run 
several words together and use a capital letter at the beginning of each new word such as 
nextPitchChoice, intervalCount, or originalSeries. 

SC reads and evaluates lines of code in descending order. In the case of the second to last 
line, it evaluates the parentheses first, then the rest of the line. Select the entire example and 
hit return. 

20.1. Variables 
(
var myFirstVariable, secondVariable, anyNameIWant;

myFirstVariable = 20;
secnodVariable = 50;
anyNameIWant = myFirstVariable * secondVariable;
anyNameIWant = anyNameIWant + 20;//replace anyNameIWant itself added to 20
anyNameIWant.postln; //post with a line return the results so far
myFirstVariable = 5; //put a new values in the first two
secondVariable = 6;
anyNameIWant = anyNameIWant + (a * b)
;
anyNameIWant.postln; //post anyNameIWant with a new line
)

You can assign values to variables at declaration. 

20.2. Variable declaration 
( 
var a = 50, b = 100, c = 3; //declare variables and assign values 

a = (a * b + (c * a))%55; //assign var "a" the result of this expression 
a.postln; //post or print the results, now stored in "a" 
) 

176

Functions 

A function is a series of expressions enclosed in two braces. The entire function is usually 
(but not always) assigned to a variable. The lines of code are executed in order and the results 
of the last line is returned. When the function is called or run anywhere in the program it is as 
if all the lines were inserted in place of the function. A function is evaluated by using the 
value message. 

A simple function with no variables and arguments, with its call: 

20.3. Function 
( 
var myFunc; 

myFunc = {100 * 20}; 

myFunc.value.postln; 
) 

The first line declares the variable name that will be used for the function. The second line is 
the function assignment ("make myfunc equal to the line {100 * 20}, or store the expression 
100 * 20 in myFunc). Every place you put myFunc in your code the value 2000 will be used. 

Arguments are like variables but they can be passed to the function when it is called. If you 
think of the function as a little echo unit you've just built, then a variable might be wires 
inside connecting several components together. An argument would be a wire connected to 
the outside; a dial on the unit that you can use to change parameters. Declare arguments right 
after the opening brace. Next you declare variables if you want to use them. 

Here is a function with arguments. 

20.4. Function with arguments 
(
var func;
func = { arg a, b;

var r; 

r = (b * 20)%a; 

r.postln; 

}; 
func.value(15, 5); 
) 

Now the function makes a little more sense because we can run it with different arguments. 
Here is the same example with a variable. 

20.5. Function with arguments and variables 
177

(
var func, outside;
func = { arg first, second; var inside;

inside = (first * 20)%second;
outside = second/first;
outside.postln; //works
inside.postln;
}
;

inside.postln; // will not work 
func.value(15, 5); 
) 

Variables work only inside the function where they are declared. They are local. Variables 
declared outside a function will work inside, provided they are part of an enclosing function 
or body of code. 

You can enter default values for the arguments as shown in the example below. 

Can you predict the values of the three myFunc calls before running this code? The first has 
no arguments passed to the function and will use the defaults (10 and 2). The next two use 
defaults. 

20.6. Function calls 
(//line 
1
var myFunc;
myFunc = { arg a = 10, b = 2;

b = (b * 100)%a;
b.postln;
}
;

myFunc.value; //line 7 
myFunc.value(15); //line 8 
myFunc.value(11, 30); //line 9 
) 

You can also use keywords with your own functions. 

20.7. Keywords 
(
var myFunc;
myFunc = { arg firstValue = 10, secondValue = 2;

firstValue = (firstValue * 100)%secondValue;
firstValue.postln;
}
;

myFunc.value;
myFunc.value(firstValue: 15)
;
myFunc.value(firstValue: 30, secondValue: 11)
;
myFunc.value(secondValue: 30, firstValue: 11)
;

178

myFunc.value(secondValue: 23); 
) 

In the previous examples the last line prints the final result. But in most functions there is a 
value returned to the place where the function is called. The last line of the function is 
returned. Here is an example that makes a little more musical sense. 

20.8. Return 
(
var octaveAndScale;
octaveAndScale = { arg oct = 4, scale = 0;

var scales, choice; 

oct = (oct + 1)*12; //translate "4" (as in C4) to MIDI octave (60) 

scales = [ 

[0, 2, 4, 5, 7, 9, 11], //major 

[0, 2, 3, 5, 6, 8, 9, 11], //octatonic 

[0, 2, 4, 6, 8, 10] //whole tone 

]; 

scale = scales.at(scale); //more on the "at" message below 

choice = scale.choose; //choose a pitch 

choice = choice + oct; //add the octave 

choice //return the final result 

}; 

octaveAndScale.value; //choose from major scale, C4 octave 
octaveAndScale.value(3); //choose from C3 octave, major scale 
octaveAndScale.value(7, 2); //choose from C7 octave, whole tone scale 
octaveAndScale.value(scale: 1); //choose from C4 octave, octatonic scale 
) 

When should you use a function? We have used them in just about every example so far. 
Messages such as max, choose, midicps, are functions (in receiver notation) that were put 
together by the authors of SC. When do you write your own functions? Convenience or 
clarity, for example when you use code over and over. An example might be a section of 
code that generates a twelve-tone matrix. It might be useful several places in the patch, or 
even in different patches. Rather than repeat the code each place it would be clearer and more 
efficient to write a function (e.g. matrixGenerator) and use that single function each time you 
need a matrix. The other situation is when there is no existing message or function that does 
exactly what you need, so you have to tailor your own35 . There is rand, rand2, rrand, 
bilinrand, exprand, etc., but maybe you need a random number generator that always returns 
pitches from a major scale in the bass clef. 

35 Check carefully, there probably is one that does what you want. 

179

Practice, just flashing 

In this patch the function returns the next frequency to be used. Each new pitch has to be 
calculated based on the previous pitch. This is because I'm using pure ratios, not MIDI 
numbers. Because of the Pythagorean comma the pitch will drift into the cracks of keyboards 
or MIDI pitches (which are equal tempered). I've added a short printout that shows what the 
actual pitch is, and what the nearest equal tempered equivalent would be36 . 

It begins with a patch from a previous chapter in a SynthDef with added arguments; 
fundamental, decay, and filter. This instrument is played successively using a task and loop. 
Before entering the loop I define the freqFunc, which picks a new pitch based on the 
previous pitch and a new pure interval ratio. This is called free just intonation (very difficult 
to do on natural instruments, very easy for computers). I add a wrap to keep the frequency 
within a certain range. The nextEvent not only controls when the next event will be, but that 
value is passed to the Flash instrument as a decay, ensuring one sound will decay shortly 
after the next sounds. I'm not convinced the filter works the way it should. 

20.9. Function practice, free, just tempered flashing 
(
//run this first
SynthDef("Flash"
,
{
arg fund = 400, decay = 4, filter = 1;
var out, harm;

out = Mix.ar( 
Array.fill(7, 
{ 

arg counter;
var partial;
partial = counter + 1;
SinOsc.ar(fund*partial) 
*

EnvGen.kr(Env.linen(0, 0, decay + 2)
,
levelScale: 1/(partial*filter)
) * max(0, LFNoise1.kr(rrand(5.0, 12.0))
)

}) 

)*0.3; //overall volume 
out = Pan2.ar(out, Rand(-1.0, 1.0)); 
DetectSilence.ar(out, doneAction:2); 
Out.ar(0, out) 
} 
).play(s); 
) 

( 

36 If rounded to the nearest MIDI value. The actual difference will probably be greater if it strays more than a 
half step. I couldn't figure out how to do that (without an if statement). You do it and send me the code. 

180

//then this
r = Task(
{
var freqFunc, pitch = 440, nextEvent;

freqFunc = {arg previousPitch; 
var nextPitch, nextInterval; 
nextInterval = [3/2, 2/3, 4/3, 3/4, 5/4, 4/5, 6/5, 5/6].choose; 
nextPitch = (previousPitch*nextInterval).wrap(100, 1000); 
nextPitch.round(0.01).post; " != ".post; 
nextPitch.cpsmidi.round(1).midicps.round(0.01).postln; 
nextPitch 

}; 

{ 
nextEvent = [0.5, 0.25, 5, 4, 1].choose; 
pitch = freqFunc.value(pitch); 
Synth("Flash", 

[\fund, pitch, \decay, nextEvent, \filter, rrand(1.0, 4.0)])
;
//Choose a wait time before next event
nextEvent.wait;

}.loop; 
}).play 
) 

Practice: Example Functions 

Here are some other functions you can try. They should all be inserted into the patch above 
(replacing the existing var list and freqFunc). 

20.10. Pitch functions 
var freqFunc, pitches, pitch = 440, count, midiNote, nextEvent; 
pitches = [60, 61, 62, 63, 64]; //declare an array of pitches 
freqFunc = { 

midiNote = pitches.choose; //pick a pitch from the array 
midiNote.midicps; // return the cps for that pitch 
}; 

var freqFunc, pitches, pitch = 440, count, midiNote, nextEvent;
pitches = [60, 62, 64, 67, 69, 72, 74, 76]; //declare an array of pitches
count = 0; //initialize count
freqFunc = 
{

midiNote = pitches.wrapAt(count); // wrapped index of count
if(count%30 == 29, //every ninth time
{pitches = pitches.scramble} //reset "pitches" to a scrambled

//verion of itself
)
;
count = count + 1; //increment count
midiNote.midicps; //return cps

}; 

181

// My favorite:
var freqFunc, pitches, pitch = 440, count, midiNote, nextEvent;
pitches = [60, 62, 64, 67, 69, 72, 74, 76].scramble;
freqFunc = 
{

midiNote = pitches.wrapAt(count); // wrap index of count
if(count%10 == 9, //every tenth time

{pitches.put(5.rand, (rrand(60, 76)))}//put a new pitch between 
//65 and 75 into the array pitches 
//at a random index 

)
;
count = count + 1; //increment count
midiNote.midicps; //return cps

}; 

182

20. Exercises 
20.1. Write a function with two arguments (including default values); low and high midi 
numbers. The function chooses a MIDI number within that range and returns the 
frequency of the number chosen. 
20.2. Write a function with one argument; root. The function picks between minor, 
major, or augmented chords and returns that chord built on the supplied root. Call 
the function using keywords. 
183