/* Algorithmic Tools */
/* Functions can be used to automate repetitive processes */
(
var noteObject, score;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
score.add(
noteObject.new(1.0, 2.0).freq_(440).amp_(0.1).dur_(2.0),
noteObject.new(1.1, 2.0).freq_(440 * 2).amp_(0.1).dur_(2.0),
noteObject.new(1.2, 2.0).freq_(440 * 3).amp_(0.1).dur_(2.0),
noteObject.new(1.3, 2.0).freq_(440 * 4).amp_(0.1).dur_(2.0),
noteObject.new(1.4, 2.0).freq_(440 * 5).amp_(0.1).dur_(2.0),
noteObject.new(1.5, 2.0).freq_(440 * 6).amp_(0.1).dur_(2.0),
noteObject.new(1.6, 2.0).freq_(440 * 7).amp_(0.1).dur_(2.0),
noteObject.new(1.7, 2.0).freq_(440 * 8).amp_(0.1).dur_(2.0));
score.play;
)
// now, with a function that does some of the work for you
(
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
myFunc = {arg starttime, freq;
noteObject.new(starttime, 2.0).freq_(freq).amp_(0.1).dur_(2.0);
};
score.add(
myFunc.value(1.0, 440),
myFunc.value(1.1, 440 * 2),
myFunc.value(1.2, 440 * 3),
myFunc.value(1.3, 440 * 4),
myFunc.value(1.4, 440 * 5),
myFunc.value(1.5, 440 * 6),
myFunc.value(1.6, 440 * 7),
myFunc.value(1.7, 440 * 8));
score.play;
)
// iteration
// 'do' has really two different forms... here, do something a number of times
z = 10.do({
"Hello!".postln;
});
z; // 10 was returned from the do function, so it can be stored in a var
10.do({arg i; // i is a counter .. starts at 0. You can give this any name really.
i.postln;
});
// it is important to remember, you can call the arg WHATEVER you want... it will always do the
// same thing
10.do({arg thisIsMyCounter; // i is a counter .. starts at 0. You can give this any name really.
thisIsMyCounter.postln;
});
// or, iterate over a collection (or an Array) of values, and pass the values in as the argument
// on each turn of the loop. An incrementer is also available as an arg:
// notice that the array is returned this time.
z = [0.1, 1.2, 2.3, 3.4, 4.5, 5.6].do({arg thisVal, inc;
[thisVal, inc].postln;
});
// with this method, the above example can become even more compact:
(
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
myFunc = {arg starttime, freq;
noteObject.new(starttime, 2.0).freq_(freq).amp_(0.1).dur_(2.0);
};
[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7].do({arg starttime, inc;
score.add(myFunc.value(starttime, 440 * (inc + 1)))
});
score.play;
)
// or... because of the way we are dealing with time:
(
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
myFunc = {arg starttime, freq;
noteObject.new(starttime, 2.0).freq_(freq).amp_(0.1).dur_(2.0);
};
8.do({arg inc;
score.add(myFunc.value(1.0 + (inc * 0.1), 440 * (inc + 1)))
});
score.play;
)
/* It is often very useful to think of creating functions that do a specific thing (possibly a musical gesture). If we wanted to create many different instances of the gesture above, the following would be more appropriate */
(
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
myFunc = {arg starttime, basefreq;
8.do({arg inc;
score.add(
noteObject.new(starttime + (inc * 0.1), 2.0)
.freq_(basefreq * (inc + 1)).amp_(0.1).dur_(2.0));
})
};
myFunc.value(1.0, 440.0);
score.play;
)
/* NOW - it is very easy to think of this gesture as a single musical event... and you can start to control its creation algorithmically */
(
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
myFunc = {arg starttime, basefreq;
8.do({arg inc;
score.add(
noteObject.new(starttime + (inc * 0.1), 2.0)
.freq_(basefreq * (inc + 1)).amp_(0.1).dur_(2.0);
)
})
};
[ 440, 415.3, 466.2, 392, 493.9, 370, 523.3 ].do({arg basefreq, inc;
myFunc.value(1.0 + inc, basefreq + (inc * 20));
});
score.play;
)
// other control structures
// for(start, end, func)
for(2, 12, {arg curVal, inc; [curVal, inc].postln});
for(2, -12, {arg curVal, inc; [curVal, inc].postln});
// forBy(start, end, step, func) - step MUST be an integer
forBy(2, 12, 2, {arg curVal, inc; [curVal, inc].postln});
forBy(2, 12, 3, {arg curVal, inc; [curVal, inc].postln});
// reverseDo
10.reverseDo({arg i; i.postln});
// while - VERY easy to cause this to get caught in an infinite loop!
// takes two functions, a test, and something to execute
// the test MUST return a boolean (true or false) value. Here are some common tests;
1 < 10;
1 > 10;
1.isNil;
nil.isNil;
nil.notNil;
1.notNil;
1 == 10;
10 == 10;
1 <= 1;
1 >= 1;
1 < 1;
1 > 1;
(
var inc;
inc = 0;
while({
inc < 10
}, {
inc.postln;
// if the next line was missing - you have to kill SC!
inc = inc + 1;
});
)
// a while shortcut - you can actually do everything in one function, AS LONG AS your function ends
// in the test
(
var inc;
inc = 0;
while({
inc.postln;
inc = inc + 1;
inc < 10
})
)
// multiple value bind
// this can be handy for passing in data that you want to have linked (say - freq and duration):
(
var data, freq, dur;
data = [440, 1.0];
// tell the interpreter that you want to parse values from an array into individual vars with
// the pound sign
#freq, dur = data;
"Freq is: ".post;
freq.postln;
"Dur is: ".post;
dur.postln;
)

// PitchClass (or its abreviation, PC) can hold pitch data in a convenient way:
(
var a4;
a4 = PitchClass(\a, 4); // PitchClass(\noteSymbol, octave)
// a4 = PC(\a, 4); // same thing! Just not as descriptive
a4.freq.postln;
)
(
var g5, fs5, e5, d5, c5, b4, a4, g4;
var mel1Data, now;
var e, x;
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
/* set up basic pitch vars */
g5 = PC(\g, 5);
fs5 = PC(\fs, 5);
e5 = PC(\e, 5);
d5 = PC(\d, 5);
c5 = PC(\c, 5);
b4 = PC(\b, 4);
a4 = PC(\a, 4);
g4 = PC(\g, 4);
/* set up durations */
e = 0.5;
x = 0.25;
mel1Data = [
/* m1 */ [g5, e], [g5, e], [fs5, e], [fs5, e],
/* m2 */ [e5, e], [e5, e], [d5, e], [d5, e],
/* m3 */ [c5, x], [b4, x], [c5, x], [d5, x], [b4, x], [c5, x], [d5, x], [e5, x],
/* m4 */ [d5, x], [c5, x], [b4, x], [a4, x], [g4, e], [b4, e]
/* eod */ ];
myFunc = {arg starttime, mel;
var now;
now = 0.0;
mel.do({arg data;
var pc, dur;
#pc, dur = data;
score.add(noteObject.new(now + starttime, dur).freq_(pc.freq).amp_(0.25).dur_(dur));
now = now + dur;
})
};
myFunc.value(1.0, mel1Data);
score.play;
)
(
var g5, fs5, e5, d5, c5, b4, a4, g4;
var mel1Data, now;
var e, x;
var noteObject, score, myFunc;
noteObject = CtkSynthDef(\test, {arg freq, amp, dur, out;
Out.ar(out, SinOsc.ar(freq, 0, Line.kr(amp, 0, dur)))
});
score = CtkScore.new;
/* set up basic pitch vars */
g5 = PC(\g, 5);
fs5 = PC(\fs, 5);
e5 = PC(\e, 5);
d5 = PC(\d, 5);
c5 = PC(\c, 5);
b4 = PC(\b, 4);
a4 = PC(\a, 4);
g4 = PC(\g, 4);
/* set up durations */
e = 0.5;
x = 0.25;
mel1Data = [
/* m1 */ [g5, e], [g5, e], [fs5, e], [fs5, e],
/* m2 */ [e5, e], [e5, e], [d5, e], [d5, e],
/* m3 */ [c5, x], [b4, x], [c5, x], [d5, x], [b4, x], [c5, x], [d5, x], [e5, x],
/* m4 */ [d5, x], [c5, x], [b4, x], [a4, x], [g4, e], [b4, e]
/* eod */ ];
// add a way to pass in amp and speaker assignment
myFunc = {arg starttime, mel, amp = 0.25, out = 0;
var now;
now = 0.0;
mel.do({arg data;
var pc, dur;
#pc, dur = data;
score.add(noteObject.new(now + starttime, dur).freq_(pc.freq).amp_(amp)
.dur_(dur).out_(out));
now = now + dur;
})
};
myFunc.value(1.0, mel1Data, -24.dbamp);
// create an louder 'echo' of the original
myFunc.value(1.25, mel1Data, -18.dbamp, 1);
score.play;
)
/* LAB ASSIGNMENT */
/*
PART 1
- Using the above example as a template, create variables for the pitches used in the second and
third parts of the melody from Assignment 1

- Make vars for any other durations
- make a var called mel2Data for the second part of the melody, and mel3Data for the third
For this part of the exercise, we will change myFunc slightly, to RETURN the current 'now' + 'startime' value:
myFunc = {arg starttime, mel, amp = 0.25, out = 0;
var now;
now = 0.0;
mel.do({arg data;
var pc, dur;
#pc, dur = data;
score.add(noteObject.new(now + starttime, dur).freq_(pc.freq).amp_(amp)
.dur_(dur).out_(out));
now = now + dur;
});
now + starttime;
};
This lets you grab where the melody left off, using it for new function calls:
(
var curtime;
... other vars ...
curtime = 0;
curtime = myFunc.value(curtime, mel1Data);
curTime = myFunc.value(curtime, mel2Data);
... etc ...
)
- using the new myFunc, create a single playing of the melody by calling myFunc with each melodic
part. Use the value of that is returned by 'myFunc' for setting the starttime. Store this value in a variable called 'curtime' (as above). Initialize 'curtime' to 1.0.
PART 2
- This melody is actually a canon, meaning it can be played against itself, offset by eight seconds.
- create a function called myMelody that takes a starttime, and will call myFunc three times, once with each block of data
- call myMelody 3 times, with time offsets of 1.0, 9.0 and 17.0 seconds. This should create the canon heard in myCanon.aif
*/