Wednesday 2 March 2011

Arrays, Functions, SynthDefs


Super Collider Blog 4- Programming SuperCollider

This week we looked more closely at the syntax of SuperCollider as a programming language. I have already talked a bit about variable assignments and arrays in previous posts.

I looked at the use of encapsulation of code within brackets and the use of functions.
When writing a function:
arg= argument,inputs (first thing stated inside the function brackets). The output of the function is the last line of code within the curly function brackets.

Basically the recipe for writing a function in SuperCollider:
1. Say what inputs are
2. Do calculation
3. Get output

Functions are written inside function brackets which look like this:
{}

Below is a code example that we were shown in class:
(
f = {arg input1,input2,input3;
{SinOSc.ar*0.1}.play; input 1*input2*input3;
}
)

Shortened to:

f = {SinOsc.ar*0.1}

and called with:

f.play

Programmatic ways of writing lists of data:
Here are a few examples of how lists can be created and filled with numbers in different ways:

Array.fill(10,{1})
In the above example there are10 things in the array and every thing in the array is the number one.

Array.fill(100,{1})
Gives us 100 1's in the array.

Instead of a fixed thing in array could give a function:

Array.fill(100,{10.rand})
(The random number in this array will go from 0-9 NOT 1-10 as we start counting from 0 in SuperCollider.

Array.fill(100,10.rand)
Without the function bracket is just gives us loads of the same number, it needs to be called over and over to give a different number.

Array.fill(100,{arg count; count})
This creates an array of squares of numbers.

Array.fill(100,{arg count; count*count*82})
This is an example of how SuperCollider can be used to fill arrays with results from difficult equations.

We then looked at how arrays can be used to create scales using midi notes in SuperCollider.

This is an example of an array containing a midi note scale starting at middle C:
[60,62,64,65,67,69,71]


(
var scale, current;

current=60;
scale= Array.fill(8, {var old; old=current; current=current+rrand(1,3); old});
scale.postln;

current=60;
scale= Array.fill(8, {var old; old=current; current=current+rrand(1,3); old});
scale.postln;

current=60;
scale= Array.fill(8, {var old; old=current; current=current+rrand(1,3); old});
scale.postln;
)

The code above was written by my tutor Nick Collins as an example of code that can be used for generating a random scale. The scale starts on middle C as the current variable is set to 60. An array is then created and filled with 8 notes. For each note in the array the function gets called. It has been copied twice more, this gives us 3 different scales and echos the last line. This can take a long time and we were therefore taught to put one of these blocks of code into a function:
(
var makescale;

makescale= {var current;
current=60;
Array.fill(8, {var old; old=current; current=current+rrand(1,3); old;});
};

3.do({makescale.value.postln;});
)

To call this function we use:
makescale.value

If we want more of them we can write:
10.do(makescale)

The number 10 can be replaced with however many times we want to call the function.

When using rrand we can change the numbers in brackets after to give us random numbers within a specific range, for example:

rrand(45,80)

Will output random numbers between 45 and 80.

From browsing around the internet I could see that Arrays are a very valuable part or the SuperCollider language and used often. I found that they were used to create drum machines and load samples amongst many other things.

I found: http://sc3howto.blogspot.com/2010/05/arrays.html a blog written by Charles CĂ©leste Hutchins gave a useful introduction to different ways to write arrays and their uses.

SynthDefs
SynthDef is short for Synthesiser Definition. SynthDefs are used to define networks of Ugens. Many synths can be created from a single SynthDef.

Before using SynthDefs we have just been using .play:

{SinOsc.ar*0.1}.play

This is the basic construct of a SynthDef:

(
SynthDef("mysound", {

}).add
)
If we look at the SynthDef count of the localhost server we can see that it has gone up.

This is the basic construct of declaring a SynthDef:

(
SynthDef("mysound",{Out.ar(SinOsc.ar*0.1)

}).add
)

The Synth can now be assigned to variables:
a = Synth("mysound")
b = Synth("mysound")

Here is an example (again written by my tutor) of an example of creating a SynthDef using a Sawtooth wave with frequency 440hz and shaped with an envelope which is then cut off with the use of a doneAction:
(
SynthDef("mysound2", {arg freq = 440; var env, saw;

saw = Saw.ar(freq);
env = EnvGen.ar(Env([0,1,0],
[0.01,0.4]), doneAction:2);

Out.ar(0,saw*env*0.1)
}).add
)

Synth("mysound2")

Synth("mysound2", [\freq, 1000])

When text is written with “” around it it is a String, the syntax colour is grey:

"name"

When text is written with a \ in front of it then it is a symbol and the syntaxcolour is green:
\name

"name" as a String can individually access the characters. With symbol it is not an array of characters but is a globally defined value, cheaper in storage than a string.

I attempted to change:
[Line.kr(1,0,1.0)*Blip.ar(550,10)*0.1}.play
into a SynthDef. This was my attempt, i'm not sure if it is right:

(
SynthDef("RosaSound", {arg freq = 550;

Out.ar(0,Line.kr(1,0,1.0)*Blip.ar(freq,10)*0.1)
}).add
)

Synth("RosaSound")

We then worked in pairs to answer some exercises that we we were set:

1.Imagine you have to generate a rhythm for one 4/4 bar (i.e. 4 beats). Write a short program which selects random successive numbers from [1.0, 0.5, 0.25] to fill up one bar's worth of beats. How do you deal with going past the end of the bar? (hint: what does .choose do on an array?)

answer:
(
var beats=[1.0,0.5,0.25];
var count=0;
while({count<4},{
var beat=beats.choose;
min(4-count,beat).postln;
count=count+beat;

})

)

2.Rewrite the following code as a series of nested ifs

i.e. if(condition1, {}, {if (condition2, etc.)})
Answer:
(
var z;
z = 4.rand;
switch (z,
0, { \outcome1 },
1, { \outcome2 },
2, { \outcome3 },
3, { \outcome4 }
).postln;
)


3.Now also rewrite it as a choice amongst elements of an array.
Answer:
(
var z;
z = 4.rand;
if(z==0,{\outcome1},{
if(z==1,{\outcome2},{
if(z==2,{\outcome3},{
if(z==3,{\outcome4},{})
})
})
}).postln;

)


[\one, \two].choose

4. Compare each of these lines by running them one at a time:

2.rand

2.0.rand

2.rand2

2.0.rand2

rrand(2,4)

rrand(2.0,4.0)

exprand(1.0,10.0)


Write a program which plots ten outputs from any one of these lines in a row. Advanced: actually allow user selection (via a variable for instance) of which line gets used to generate the ten random numbers.
Answer:
(
f={arg line;
switch (line,
1, { for(1,10,{2.rand})},
2, { for(1,10,{2.0.rand})},
3, { for(1,10,{2.rand2})},
4, { for(1,10,{2.0.rand2}) },
5, { for(1,10,{rrand(2,4)}) },
6, { for(1,10,{rrand(2.0,4.0)}) },
7, { for(1,10,{exprand(1.0,10.0)}) },

)
}
f.value(3)
)

To be honest I was lucky to be working with someone who turned out to be really good at programming and would have struggled to get through working through the problems alone. I found the exercise really useful as it allowed me to see the best way to logically tackle a problem starting first with setting out the structure of the code for the answer and then breaking down the problems into steps and finding the most logical and efficient way of coding it in SuperCollider by seeing how someone else would work through it.

No comments:

Post a Comment