/* 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;

)