UA-69169170-1 SuperCollider | Daniel Hensel

Daniel Hensel

Die offzielle Webseite des Komponisten

I transcribed a few useful things from Heinrich Taube Common Music's book "Notes from the Metalevel" to Supercollider for teaching and composition purposes. Above all, this affects the functions from Chapter 6 onwards. The transcriptions are to be expanded by the end of the book. The code block is always posted first, followed by the evaluation result in the console. In Supercollider you need to select the code block and evaluate it with CMD-Enter on Mac, on Windows and Linux with ctrl-Enter.

Functions, alias "Chapter 6"

"Example 6-1. "

We can define functions in Supercollider like in Lisp, but we can also define global variables, which is useful for things that stay the same, like notes. The tilde in front of the letter defines a global variable, but also a function.
The "keyhole C" has the midi number 60. So let's define it globally:~middlec = 60;

*** Welcome to SuperCollider 3.13.0-dev. *** For help press Cmd-D.
SCDoc: Indexing help-files...
SCDoc: Indexed 1363 documents in 0.37 seconds
-> 60

But now we can also define a series. Taube takes the series from Berg's Violin Concerto for this, lists in Supercollider are nothing more than arrays, which should be imagined as a kind of egg cartons, the counting of the indices starts at 0!

~albansrow = [0, 3, 7, 11, 2, 5, 9, 1, 4, 6, 8, 10];
-> [ 0, 3, 7, 11, 2, 5, 9, 1, 4, 6, 8, 10 ]

"Interaction 6-1. Using the program"

Taube now goes on to develop a function for transposition. But it would be much faster in Supercollider if you simply added to or subtracted from the midi number. But we follow the Taube example. Functions in Supercollider are always enclosed in curly brackets.

(
~transposerow = {arg row, offset;
var i = 0, transposedrow;
while ({i < row.size},
{i= i + 1;
transposedrow = (row + offset)};
);
transposedrow.postln;
};
)
-> a Function

-> [ 60, 63, 67, 71, 62, 65, 69, 61, 64, 66, 68, 70 ]

~transposerow.value([0, 2, 5], 90).postln;

-> [ 90, 92, 95 ]

~transposerow.value(~albansrow, rand(80)).postln;

-> [ 72, 75, 79, 83, 74, 77, 81, 73, 76, 78, 80, 82 ]


~albansrow = [0, 3, 7, 11, 2, 5, 9, 1, 4, 6, 8, 10]+60;
-> [ 60, 63, 67, 71, 62, 65, 69, 61, 64, 66, 68, 70 ]

This would have been a faster result and more "Sc-like."

Now Taube comes to variables that we define in Supercollider like this:
(
var a = 10, b = 20;
(a * b).postln;
)

-> 200

"Interaction 6-2. Examples of numerical predicates."

1.isNumber

-> true

[1].isNumber

-> false

0.even
-> true


-2.odd

-> false

"Interaction 6-3. Examples of arithmetic relations."

1==1.0;

-> true

1<1;

-> false

(3>2); (3>1); (2 >1);

-> true


(var a = 3, b = 2, c = 1;
if ( a > b && a > c )
{a.postln+"is the greatest".postln}{a.postln+"of variable a is not the greatest"};
)

-> 3 is the greatest

(-1 >= 0) && (-1 >= 1) && (0 >= 1) && (1 >=1) && (1 >= 100);
-> false

(-1 <= 0) && (-1 <= 100) && (100 <= 0.3);
-> false

"Interaction 6-4. Example arithmetic operators."
Calculations are best summarized unmathematically in brackets to form a unit.

(1 + 2 + 3 + 4)
-> 10

(10 * 10)
-> 100

(- 1)
-> -1

(1 - 0.5)
-> 0.5

(1/1)
-> 1.0

(1/0.25)
-> 4.0

(30 / 5 / -2)
-> -3.0

Since there are no equivalents to the Lisp functions in Supercollider that Taube presents in his book, I'll skip a few. But most of them can be reprogrammed in other ways.

List[1,2,3]
-> List[ 1, 2, 3 ]

~mynotes= ["a", "b", "c", "d", "e"];
-> [ a, b, c, d, e ]

~mynotes.first
-> a

~mynotes.last
-> e

~mynotes.removeAt(0)
-> a

~mynotes;
-> [ b, c, d, e ]

These notes are now pure strings, character strings without assignment, now we assign MIDI notes:

(
~mynotes2 = {var a4 = 69, b4 = 71, c5 = 72, d5 = 74, e5 = 76, fis5 = 78, gis5 = 79, notes = Array.new;
notes = [a4,b4,c5,d5,e5,fis5,gis5].postln;
}
)
-> a Function

~mynotes2.value.postln;
[ 69, 71, 72, 74, 76, 78, 79 ]

~mynotes2.value[0] == 69
-> true

We can now pass this on to a synth in SuperCollider, but then we need the conversion Midi-to-Cycles-Per-Second = midicps:

(SynthDef(\test,{
arg freq = 440, amp = 0.5;
var sig, env;
env = EnvGen.kr(Env.perc);
sig = VarSaw.ar(freq, mul: env);
Out.ar(0, sig);

};
).add;
)

(
Pdef(
\rhythm,
Pbind(
\instrument, \test,
\freq, Pseq(~mynotes2.value.midicps, inf),
\dur, 1.1,

).play;
)
)
Ein Äquivalent zu "list-ref":


(
~picklist = {arg lst;
var len, pos;
len = lst.size;
pos = len.rand;
lst.wrapAt(pos).postln;
};
)
-> a Function


~picklist.value(~mynotes2.value(rand(12))).postln;
-> 78

~mynotes2.value.size;
-> 7

~mynotes2.value.removeAt(0).postln;
-> 69

~mynotes2.value.reverse;
-> [ 79, 78, 76, 74, 72, 71, 69 ]

~mynotes2.value ++ ~mynotes2.value.reverse;
-> [ 69, 71, 72, 74, 76, 78, 79, 79, 78, 76, 74, 72, 71, 69 ]

~mynotes2.value.dupEach(2).postln;
-> [ 69, 69, 71, 71, 72, 72, 74, 74, 76, 76, 78, 78, 79, 79 ]

~mynotes2.value.rotate(1).postln;
-> [ 79, 69, 71, 72, 74, 76, 78 ]


Ist das Element in dem Array/der Liste enthalten?

(
x = {arg note;
var i= -1, mynotes3 = [69, 71, 72, 74, 76, 78, 79 ];

while({i<(mynotes3.size)},
{i=i+1; if (note == mynotes3[i], { "true".postln },{"false".postln});
};
)})
-> a Function

x.value(69);
true
false
false
false
false
false
false
false
-> nil


x.value(50);
false
false
false
false
false
false
false
false
-> nil



Oder:


~mynotes2.value[0] == 69

-> true

"Interaction 6-10. Example of boolean operators."
Boolean operators distinguish between "true" and "false". The doubled ampersand && allows us to concatenate the statements. || stands for "or":


true && true && true
-> true

false && true && true
-> false

(1 == 1 ) && (10 < 11)
-> true


false || false || true || false
-> true

false || false
-> false

As in Lisp, the statement can be reversed with the "not" method. So if something is not wrong, then it is true.

false.not
-> false

With conditional statements, SuperCollider proceeds in that the first function block calls the "true" state, the second stands for else (and-next), and thus for the "wrong" state:

(if (true) {1} {2})
-> 1

(if (false) {1} {2})
-> 2

Unfortunately, SuperCollider doesn't behave like other programming languages when you want to concatenate if statements, there is no else-if and manual concatenations don't bring the usual results like e.g. in C.

Sollte pi größer 4 sein, soll "yes" aufgerufen werden, ansonsten "false". Da aber pi = 3.1415926535898 ist, ist es kleiner als 4, weshalb "false" erscheint:
(if (pi > 4) {"yes".postln}{"false".postln})
-> false

(if (1 < 3) {"winner".postln}{"loser".postln})
-> winner


"Interaction 6-11. Sequential evaluation."
For a sequential evaluation we can use a routine in Supercollider.

c = TempoClock.new;
-> a TempoClock


(
t = Routine {
var go = "go";
loop {
3.0.wait;
go.postln;
}
}.play;
)
-> a Routine
go
go


t.reschedule(c);

t.stop;
-> a Routine
(
var go = "go", winner = "winner", looser = "looser", payday = "payday", gotojail = "go to jail";
(if (rand(3)<2) {winner.postln+payday.postln}
{looser.postln+gotojail});
)

winner
payday
-> winner payday


Let's define global variables again:

~twopi = {2*pi};
-> a Function

~twopi.value.postln;
-> 6.2831853071796

(2 * ~twopi.value).postln
-> 12.566370614359

~lotsapi = {pi*pi*pi};
-> a Function

~lotsapi.value.postln;
-> 31.0062766803

~favoritepie = "Apple-Crunsh";
-> Apple-Crunsh

~favoritepie.value.postln;
Apple-Crunsh
-> Apple-Crunsh


"Interaction 6-13. Redefining and assigning variables."

Global variables can also be assigned new values:

~favoriterow = Array[0, 7, 8, 3, 4, 11, 10, 5, 6, 1, 2, 9];
-> [ 0, 7, 8, 3, 4, 11, 10, 5, 6, 1, 2, 9 ]

~favoriterow.value.postln;
-> [ 0, 7, 8, 3, 4, 11, 10, 5, 6, 1, 2, 9 ]

~albansrow = ~favoriterow.value.reverse.postln;
-> [ 9, 2, 1, 6, 5, 10, 11, 4, 3, 8, 7, 0 ]

~albansrow.value.postln;
-> [ 9, 2, 1, 6, 5, 10, 11, 4, 3, 8, 7, 0 ]

~favoriterow = ~albansrow;
-> [ 9, 2, 1, 6, 5, 10, 11, 4, 3, 8, 7, 0 ]

~favoriterow.value.postln;
-> [ 9, 2, 1, 6, 5, 10, 11, 4, 3, 8, 7, 0 ]

"Example 6-2. Convert beats per minute to time in seconds."

(
~bpmtoseconds = {arg bpm;
(60.0 / bpm);
})

-> a Function

~bpmtoseconds.value(60).postln;
-> 1.0

~bpmtoseconds.value(120).postln;
-> 0.5

~bpmtoseconds.value(90).postln;
-> 0.66666666666667

~bpmtoseconds.value(40).postln;
-> 1.5

"Example 6-3. Three example let statements => as there is no "let statement", we use local variables "var""


(
var a = 10, b = 20;
(a * b).postln;
)
-> 200

(var a = rand(10);
(a*a).postln;)
-> 49

(var a = 10, b = rand(10);
a.postln;
b.postln;
List[a, b, (a+b)];
)
-> List[ 10, 3, 13 ]


(var key = rand(116), oct = key + 12;
List[key, oct];)

(
~randomoctave = {var key = rand(116), oct = key + 12;
List[key, oct];
};
)
-> a Function

~randomoctave.value;

-> List[ 114, 126 ]
Chapter 7

//bpm->seconds
(
~b = {arg bpm;
(60.0 / bpm);
})

~b.value(120).postln;

//rhythm->seconds

(

~c = {arg rhy, tempo;
(rhy * 4.0) * (~b.value(tempo).postln);

}
)
-> a Function

~c.value(1/4, 60).postln;
-> 1.0

~c.value(1/8, 76).postln;
-> 0.39473684210526

Octaves as power of 2

2.pow(-2);
-> 0.25

2.pow(-1);
-> 0.5

2.pow(0);
-> 1.0

2.pow(1/2);
-> 1.4142135623731

2.pow(1/12);
-> 1.0594630943593

2.pow(-3/24);
-> 0.91700404320467


Hertz-scaled frequency values with equal tempered magnitudes:

(
~d = {arg hz, mult;
(hz * (2.pow(mult)));

};
)
-> a Function

~d.value(440, 1);
-> 880.0

~d.value(440, -2);
-> 110.0

MIDI nach Hz
~lowestfreq = 8.175
-> 8.175

(
~e = {arg key;
(~lowestfreq * (2.pow(key /12)));
};

)

-> a Function

~e.value(60).postln;
-> 261.6

Version based on A

~lowestfreqa = 6.875;
-> 6.875

(
~f = {arg key;
(~lowestfreqa * (2.pow(key /12)));
};

)
-> a Function

~f.value(69).postln;
-> 369.99442271163

(
~pickrange = {arg low, high;
var rng, random;
rng = (high - low);
if(rng > 0,{
low + rng.rand},
{rng = 0};
);
};
)

-> a Function

~pickrange.value(60, 100).postln;
-> 92

~pickrange.value(-1.0, 1.0).postln;
-> 0.48808193206787

~pickrange.value(440, 880).postln;
-> 648

~pickrange.value(0, -1).postln;
-> 0

(
~picklist = {arg lst;
var len, pos;
len = lst.size;
pos = len.rand;
lst.wrapAt(pos).postln;
};
)
-> a Function

(
~aeolianmode = Array[57, 59, 60, 62, 64, 65, 67,
69, 71, 72, 74, 3, 77, 79, 81];
)
-> [ 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 3, 77, 79, 81 ]


~picklist.value(~aeolianmode).postln;
-> 3

~picklist.value([1/16, 1/8, 1/8, 1/4]).postln;
-> 0.125

"Example 7-7. Random selection from a list of values."

(
~chance = {arg prob;
(rand(1.0) < prob)};
)

~chance.value(0.75);

(if (~chance == 0.8) {"retrograd".postln} {"prime".postln});



"Chapter 8"

~mykey = 60;
-> 60

~myrow = Array[0, 3, 7, 11, 2, 5, 9, 1, 4, 6, 8, 10];
-> [ 0, 3, 7, 11, 2, 5, 9, 1, 4, 6, 8, 10 ]

Transmission of the transposition loop:

(
~transposition = {arg row, offset;
var transposed;
row.size.do{
transposed = (row + offset);
};
transposed.postln;
})
-> a Function

~transposition.value(~myrow, ~mykey).postln;
-> [ 60, 63, 67, 71, 62, 65, 69, 61, 64, 66, 68, 70 ]

Transposition um eine große Sekunde aufwärts:

~transposition.value(~myrow, ~mykey+2).postln;
-> [ 62, 65, 69, 73, 64, 67, 71, 63, 66, 68, 70, 72 ]

"Example 8-2. Defining the fundamental and bounds for a harmonic series."
Here we define a fundamental note for a harmonic series, i.e. an overtone series.

~fundamental = 220;
-> 220

~startharm = 1;
-> 1

~endharm = 8;
-> 8

(
var h = 0;
(~startharm..~endharm).do({h = h + 1;
(h * ~fundamental).postln});
)

220
440
660
880
1100
1320
1540
1760
-> 1


(var i = 0, sum = 0;
while({i < 10},{i = i+1; sum = i+sum; i.postln});
sum.postln;
)
1
2
3
4
5
6
7
8
9
10
55
-> 55


10.do(rand(100).postln);
35
-> 10

(
x = Routine({10.do({ var i = rand(100); i.yield })});
x.nextN(10);
)
-> [ 54, 49, 66, 49, 8, 70, 68, 4, 71, 13 ]


(var i = 1, sum = 0;
while({i < 5},{i = i + 1; sum = rand(5) + sum + 10;
});
sum.postln;
)
-> 49

(var x = 0, i = 1;
while({i < 5}, {i = i + 1; x = rand(100); x % 5 });
x.postln;
)
-> 50


(var i = 0;
while({i < 10},
{i = i + 1; i.postln});
)
1
2
3
4
5
6
7
8
9
10
-> nil


(var i = -1.5;
while({i < 2 },
{i = i + 0.5; i.postln});
)
-1.0
-0.5
0.0
0.5
1.0
1.5
2.0
-> nil


(var i = -2;
while({i < 8 },
{i = i + 2; i.postln});
)
0
2
4
6
8
-> nil


(var i = 2;
while({i > -10 },
{i = i - 2; i.postln});
)
0
-2
-4
-6
-8
-10
-> nil


(var i = 11;
while({i > 2 },
{i = i - 1; i.postln});
)
10
9
8
7
6
5
4
3
2
-> nil

(
~test = List["a", "b", "c"];
)
-> List[ a, b, c ]

(
~test = List["a", "b", "c"].reverse;
)
-> List[ c, b, a ]

(
~test = List["a", "b", "c"].scramble;
)
-> List[ b, c, a ]

(
var y, z;
z = ["a", "b", "c"];
y = [z[0],z[1], z[1], z[2], z[2]];
y.postln;
)
-> [ a, b, b, c, c ]

Besser:

List["a", "b", "c"].stutter(2).postln;
-> List[ a, a, b, b, c, c ]

List["a", "b", "c"].dupEach(2).postln;
-> List[ a, a, b, b, c, c ]


(var i = 11;
while({i > 2 },
{i = i - 1; i.postln});
)

10
9
8
7
6
5
4
3
2
-> nil


(
var w, x, y, z;
x = ["a", "b", "c"];
w = x.copyRange(1,3);
x.postln;
w.postln;
w = x.copyRange(2,3);
w.postln;
)
[ a, b, c ]
[ b, c ]
[ c ]
-> [ c ]


"Interaction 8-11. Example action clauses."

2.do{"Hi".postln};
Hi
Hi
-> 2

10.do{rand(50).postln};
49
35
30
18
4
22
20
10
37
8
-> 10


(Array.fill(10,{[rand(50)];
}).postln;
)
-> [ [ 15 ], [ 1 ], [ 24 ], [ 6 ], [ 18 ], [ 3 ], [ 5 ], [ 28 ], [ 3 ], [ 44 ] ]

{50.rand.postln}.sum(10).postln;
41
27
46
1
21
30
6
6
28
32
238
-> 238


(var i = 11;
while({i > 1 },
{i = i - 1; if(i.even){i.postln}});
)
10
8
6
4
2
-> nil


(var i = 1;
while({i < 10 },
{i = i + 1; if(i.even){i.postln}});
)
2
4
6
8
10
-> nil

(var i = 11;
while({i > 1 },
{i = i - 1; if(i.odd){i.postln}});
)
9
7
5
3
1
-> nil


(var i = 1;
while({i < 10 },
{i = i + 1; if(i.odd){i.postln}});
)
3
5
7
9
-> nil

(var i = 11, x;
while({i > 1 },
{i = i - 1; if(i == -1){"true".postln}{"false".postln}
});
)
false
false
false
false
false
false
false
false
false
false
-> nil

(var i=0, n = 0;
while({i < 11 },
{i = i + 1; n = rand(10); if(n%3==0){n.postln}});
)
9
9
3
-> nil

(var i=0, n = 0;
while({i < 11 },
{i = i + 1; n = rand(10); if(n.even){n.postln}});
)
4
2
4
-> nil


(var a,b, i=11, j=0;
while({i > 1 },
{i = i - 1; j=rand(10); a = ("i=" + i); b = ("j=" + j); if(j.even){(a.postln+b.postln)}})
)
i= 9
j= 0
i= 7
j= 8
i= 6
j= 8
i= 5
j= 2
i= 4
j= 0
i= 3
j= 0
i= 2
j= 0
-> nil

"Interaction 8-13. Example initialization clause."

(var hz=220, h=0;
while({h < 8}, {h = h + 1; (hz * h).postln});
)
220
440
660
880
1100
1320
1540
1760
-> nil


(var i = 1, sum = 0;
while({i < 10}, { i = i + 1; sum = sum + rand(100); });
(sum / 10.0).postln;
)
-> 46.2


(var i = 0, j = 100, k = 0;
while({i < 4}, {i = i + 1;
while({j > 0}, {j = j - 10});
k = rand(100);
Array[i,j,k].postln});
)
[ 1, 0, 61 ]
[ 2, 0, 62 ]
[ 3, 0, 99 ]
[ 4, 0, 25 ]
-> nil


(var a, b, c, i = 0, j = 100, k;
while({i < 4; j > 0}, {i = i + 1; j = j - 10;
a = ("i=" + i); b= ("j=" + j); c = ("k=" + k = rand(100));
List[a,b,c].postln;
};
);
)
List[ i= 1, j= 90, k= 89 ]
List[ i= 2, j= 80, k= 56 ]
List[ i= 3, j= 70, k= 0 ]
List[ i= 4, j= 60, k= 22 ]
List[ i= 5, j= 50, k= 59 ]
List[ i= 6, j= 40, k= 33 ]
List[ i= 7, j= 30, k= 67 ]
List[ i= 8, j= 20, k= 83 ]
List[ i= 9, j= 10, k= 22 ]
List[ i= 10, j= 0, k= 99 ]
-> nil


(var i,j,k;
forBy(0, 4, 1){i; i.postln};
forBy(100, 0, -10){j; j.postln};
100.do{arg k=rand(100); k.postln};
)
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
nil
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
-> 100


(var i = -1, j = 0;
while({i < 3}, {i = i + 1;
i.postln;
j = 2.pow(i).postln}
);
)

0
1.0
1
2.0
2
4.0
3
8.0
-> nil


(var i = 0,j=0, result = Array2D.new(3,4);
forBy(0, 2, 1) {j=0;
forBy(0, 3, 1){
result.put(i,j, (i*10+j));j=j+1;
}; i=i+1;
};result.postln;
)
-> Array2D[ [ 0, 1, 2, 3 ][ 10, 11, 12, 13 ][ 20, 21, 22, 23 ] ]


Chapter 9
"Etudes, Op. 3: Iteration, Rows and Sets"

"Interaction 9-1. Using loop to collect numbers mod 12."



(
~loopup = {arg x, y;
var i = -1;
while ({i < (y - x)}, {i = i + 1;
(i%12).postln;

};
)
}

)

~loopup.value(60, 72).postln;