/* SuperCollider language basics */
// parens enclose a single block
(
// inside the parent code block, you can have variables. These must be declared first with the
// 'var' keyword
var me, aFunction, return;
me = 10;
// Functions are created with curly braces. Functions can have arguments given to them,
// and their own local variables. It is usually good form to ONLY use variables local to the
// function. Anything needed from outside the function should be passed in as an argument.
// This makes your functions more portable. Also - strive for smaller, modular functions
// 'aFunction' takes two arguments
aFunction = {arg val1, val2;
// declare a var
var result;
result = val1 * val2; // multiply the two arguments together, store the result
// whatever is in the last line of a function is the 'return' value of the Function
result;
};
// you then send the function the 'value' message to evaluate it.
// Following .value you can pass in values for the args inside parens
return = aFunction.value(me, 123);
// show the value of return in the post window
return.postln;
)
(
var mulAdd;
// this function assigns default values to the arguments. Also, notice that there are MNAY more
// args. Args can be named just about anything meaningful to you... so take advantage of that and
// give them names that help you remember what they will be doing.
mulAdd = {arg startVal = 0, mulVal = 1, addVal = 0;
// especially with math, use parens to tell SC what should be done first
// since only this line is evaluated, it will be returned from the function.
(startVal * mulVal) + addVal;
};
// posting messages to the windows, in addition to strings, can be a handy debugging tool. Here,
// we'll run 'mulAdd' a number of times to see what it does, posting the result, and a little
// message each time. The messages is a String, which has an opening and closing double quote.
"First result of mulAdd is: ".post; // no 'ln', 'ln' means new line
mulAdd.value.postln; // use the defaults in the arguments, and postln the result
"Second result of mulAdd is: ".post;
mulAdd.value(10, 1, 20).postln; // give values to all three arguments
"Third result of mulAdd is: ".post;
// you can also use the names of the arguments as keywords. To do this, type the arg name and add :
// After you do this, the interpreter will only accept keywords in THIS function call
mulAdd.value(mulVal: 30, startVal: 14).postln;
// not just numbers can be multiplied and added! You can also do arrays:
"Here is an example using an array: ".post;
mulAdd.value([0, 1, 2, 3, 4], 10); // will use 'addVals' default
)
(
var number;
number = 10;
"Started".postln;
(number = 8 * number).postln;
(5 * number).postln;
"End".postln;
)
// Arrays are one of the most important and basic ways to deal with
// large amounts of similar data in almost any programming language.
// Arrays *usually* contains similar kinds of data, and are stored
// with a single variable. Accessing the data is then done through
// index (starting with 0!!!).
(
var myArray;
// Arrays can be created with initialized data
// here, myArray is filled with numbers
myArray = [0, 2, 4, 5, 7, 9, 11];
// you can access the data with the 'at' method:
myArray.at(1).postln;
// or - you can use square brackets as a shortcut for 'at'
myArray[3].postln;
// the 'do' method lets you iterate over the elements of an array
myArray.do({arg valueFromArray, index;
("At index" + index + "the value is:"+valueFromArray).postln;
});
)
// Certain operations can be done to all elements in an Array:
(
var myArray, addResult, mulResult, sum;
myArray = [0, 2, 4, 5, 7, 9, 11];
// add to all elements, and store the result in a new var
addResult = myArray + 2;
// multiply all elements, and store the result in a new var
mulResult = myArray * 10;
// quickly get the sum of all elements in the Array
sum = myArray.sum;
// finally - post all vars... notice that 'myArray' is still intact!
myArray.postln;
addResult.postln;
mulResult.postln;
sum.postln;
)
// if you want to change the Array, you can reassign the return value to
// the same var
(
var myArray;
myArray = [0, 2, 4, 5, 7, 9, 11];
"Original: ".post;
myArray.postln;
// perform addition on the elements of the Array
myArray + 2;
// myArray is still the same value
"Changed? ".post;
myArray.postln;
// re-assign to the same var
myArray = myArray + 2;
"Changed! ".post;
myArray.postln;
)
// the values of an Array can be bound individually with the #
(
var myArray, val1, val2, val3;
myArray = [0, 2, 4];
#val1, val2, val3 = myArray;
"val1 is: ".post;
val1.postln;
"val2 is: ".post;
val2.postln;
"val3 is: ".post;
val3.postln;
)
// multi-dimensional Arrays (or, an Array of Arrays)
(
var myBigArray, tmp;
myBigArray = [
[0, 440, 0.1],
[0.5, 880, 0.05],
[1.0, 440, 0.1]
];
myBigArray.postln;
// access the 0th slot - this returns the 0th Array
myBigArray.at(0).postln;
// or using []
myBigArray[0].postln;
// accessing the contents of the inner Array - since
// the first 'at' returns an Array, you can then operate on
// that value just like another Array
tmp = myBigArray[0];
tmp[1].postln;
// or as a shortcut
myBigArray[0][1].postln;
)
// iteration of an Array of Arrays with value binding
(
var myBigArray;
myBigArray = [
[0, 440, 0.1],
[0.5, 880, 0.05],
[1.0, 440, 0.1]
];
myBigArray.do({arg thisDataSet, i;
var start, freq, amp;
"The current data set is: ".post;
thisDataSet.postln;
#start, freq, amp = thisDataSet;
("Turn" + i + "of the do loop... values are:").postln;
// use the \t formatting string to create a tab in
// the printed output
("\tstart:"+start+"freq:"+freq+"amp:"+amp).postln;
});
// return nil to make the post output cleaner
nil;
)
// now, a practical sound example
s.boot;
(
var note, score;
// envDur should be the same as the duration of the note
// to give the note a clean cutoff
note = CtkSynthDef(\note, {arg freq, amp, envDur = 1;
var src, env;
src = SinOsc.ar(freq, 0, amp) * Line.kr(1, 0, envDur);
Out.ar(0, src)
});
score = CtkScore.new;
// now, add some notes for a melody
score.add(note.new(0.0, 0.4).freq_(PC(\c, 4).freq).amp_(0.2).envDur_(0.4));
score.add(note.new(0.5, 0.25).freq_(PC(\c, 4).freq).amp_(0.15).envDur_(0.25));
score.add(note.new(1.0, 0.35).freq_(PC(\g, 4).freq).amp_(0.1).envDur_(0.35));
score.add(note.new(1.5, 0.25).freq_(PC(\g, 4).freq).amp_(0.1).envDur_(0.4));
score.add(note.new(2.0, 0.35).freq_(PC(\a, 4).freq).amp_(0.1).envDur_(0.35));
score.add(note.new(2.5, 0.25).freq_(PC(\a, 4).freq).amp_(0.1).envDur_(0.25));
score.add(note.new(3.0, 0.75).freq_(PC(\g, 4).freq).amp_(0.1).envDur_(1.0));
score.add(note.new(4.0, 0.35).freq_(PC(\f, 4).freq).amp_(0.15).envDur_(0.35));
score.add(note.new(4.5, 0.25).freq_(PC(\f, 4).freq).amp_(0.1).envDur_(0.24));
score.add(note.new(5.0, 0.35).freq_(PC(\e, 4).freq).amp_(0.15).envDur_(0.35));
score.add(note.new(5.5, 0.25).freq_(PC(\e, 4).freq).amp_(0.1).envDur_(0.25));
score.add(note.new(6.0, 0.35).freq_(PC(\d, 4).freq).amp_(0.15).envDur_(0.35));
score.add(note.new(6.5, 0.25).freq_(PC(\d, 4).freq).amp_(0.1).envDur_(0.25));
score.add(note.new(7.0, 0.75).freq_(PC(\c, 4).freq).amp_(0.2).envDur_(0.75));
score.play;
)
// two disadvantages to the above... one - lots of typing of the same
// thing over and over again! two - if the same values should be used
// in two places (note duration and envDur in this case) there is more
// room for mistakes... so, store the data in an Array and parse it to
// create the melody
(
var note, score, dataArray;
// envDur should be the same as the duration of the note
// to give the note a clean cutoff
note = CtkSynthDef(\note, {arg freq, amp, envDur = 1;
var src, env;
src = SinOsc.ar(freq, 0, amp) * Line.kr(1, 0, envDur);
Out.ar(0, src)
});
score = CtkScore.new;
dataArray = [
// [startTime, duration, PC, amp]
[0.0, 0.4, PC(\c, 4), 0.2],
[0.5, 0.25, PC(\c, 4), 0.15],
[1.0, 0.35, PC(\g, 4), 0.1],
[1.5, 0.25, PC(\g, 4), 0.1],
[2.0, 0.35, PC(\a, 4), 0.1],
[2.5, 0.25, PC(\a, 4), 0.1],
[3.0, 0.75, PC(\g, 4), 0.1],
[4.0, 0.35, PC(\f, 4), 0.15],
[4.5, 0.25, PC(\f, 4), 0.1],
[5.0, 0.35, PC(\e, 4), 0.15],
[5.5, 0.25, PC(\e, 4), 0.1],
[6.0, 0.35, PC(\d, 4), 0.15],
[6.5, 0.25, PC(\d, 4), 0.1],
[7.0, 0.75, PC(\c, 4), 0.2]
];
// now, add some notes for a melody by iterating the big array,
// and parsing the inner one
dataArray.do({arg thisData;
var startTime, duration, pc, amp;
#startTime, duration, pc, amp = thisData;
pc = pc.freq; // change the PC to a hertz value
score.add(note.new(startTime, duration).freq_(pc).amp_(amp).envDur_(duration))
});
score.play;
)