/* Dictionary */
a = Dictionary.new;
a.add(\entry1 -> 10); // symbolOrString -> value this is an Association
a.add(\entry2 -> 20);
a.put(\entry3, 40); // this will create an Association from the two values
a.put("entry4", 60);
// you can then access your data with the keyword
a[\entry1]; // returns the value 10
a["entry1"];
a[\entry4];
a
// the Associations inside the Dictionary are not ordered... you can only access
// them through the keyword
a.postcs; // postcs can be handy!
z = Env([0, 1, 0], [1, 1], \sin);
z.postln;
z.postcs;
// keys can be symbols (as above), strings...
a.add("string1" -> Score.new);
// ... and even numbers
a.add(0 -> 100);
// Dictionary iteration
a.do({arg me, i; [me, i].postln;}); // passes in value and inc
a.keysDo({arg me, i; [me, i].postln}); // passes in keys and inc
a.keysValuesDo({arg key, val, i; [key, val, i].postln}); // passes in key, val and inc
a.findKeyForValue(10); // return the key associated with this value
a;
// remove an item
a.removeAt(\entry1);
a.removeAt("string1");
a;
// of course... you can store Dictionaries of Dictionaries
a.add(\event1 -> Dictionary[\node -> 1000, \controlbus -> 12, \group -> 1002])
a
a.at(\event1).at(\node);
a[\event1][\node];
// The above will come in handy as a possible control structure for events!
/* Celia says 'spend less time programming, more time playing with me!" */

/* Classes
Classes can add functionality to SuperCollider. They can perform functions for you, or even wrap a number of UGens into a 'pseudo' UGen...
*/
/* RPlay */
s.boot;
//example code
SynthDef(\sines, {arg freq1, freq2;
var a;
a = SinOsc.ar([freq1, freq2], 0, 0.3) * Line.kr(1, 0, 1, doneAction: 2);
Out.ar(0, a);
}).load(s);
s.sendMsg(\s_new, \sines, 1000, 0, 1, \freq1, 440, \freq2, 660);
SynthDef(\sines2, {arg freq1, freq2, freq3;
var a;
a = Mix.ar(SinOsc.ar([freq1, freq2, freq3], 0, 0.3)) *
Line.kr(1, 0, 1, doneAction: 2);
Out.ar(0, a);
}).load(s);
s.sendMsg(\s_new, \sines2, 1000, 0, 1, \freq1, 440, \freq2, 660, \freq3, 880);
a = [1, 2, 3];
#b, c, d = a;
b
c
d
(
var event1, event2, score;
event1 = {arg args;
var arg1, arg2;
#arg1, arg2 = args;
s.sendMsg(\s_new, \sines, s.nextNodeID, 0, 1, \freq1, arg1, \freq2, arg2);
};
event2 = {arg args;
var arg1, arg2, arg3;
#arg1, arg2, arg3 = args;
s.sendMsg(\s_new, \sines2, s.nextNodeID, 0 , 1, \freq1, arg1, \freq2, arg2,
\freq3, arg3)
};
score = [
[1, event2, 400, 550, 660],
[3, event1, 440, 220],
[4.3, event2, 500, 550, 552],
[5, event2, 440, 880, 1320]
];
r = RPlay.new(score);
r.play;
)
r.stop;
r.play;
r.isPlaying;
r.pause;
r.resume;
r.stop;
/* Feedback */
SynthDef(\feed, {arg gate = 1, buffer, feedpct = 0.95;
var src, env, feedback;
env = EnvGen.kr(
Env([0, 1, 0], [0.1, 0.1], \sin, 1),
gate, doneAction: 2);
src = Ringz.ar(Dust2.ar(1, 0.1), 200, 3);
// the Feedback class (a 'pseudo' UGen)
feedback = Feedback.ar(src, feedpct, BufDur.kr(buffer), buffer);
Out.ar(0, [src, feedback] * env);
// Out.ar(0, src * env);
}).load(s);
// allocate 0.5 seconds of memory... introducing \b_alloc!
s.sendMsg(\b_alloc, b = s.bufferAllocator.alloc(1), 0.1 * s.sampleRate);
s.sendMsg(\s_new, \feed, a = s.nextNodeID, 0, 1, \buffer, b, \feedpct, 0.9);
s.sendMsg(\n_set, a, \gate, 0);
s.sendMsg(\b_free, b); // don't forget to free memory!
s.sendMsg(\b_alloc, b = s.bufferAllocator.alloc(1), 0.2 * s.sampleRate);
s.sendMsg(\s_new, \feed, a = s.nextNodeID, 0, 1, \buffer, b, \feedpct, 0.9);
s.sendMsg(\n_set, a, \gate, 0);
s.sendMsg(\b_free, b);
s = Server.internal.boot;
Server.default = s;
/*
Ctk - Composition Tool Kit
Main goals -
Create objects that can be used in real-time AND non-real-time
Create a framework for to use the SC language algorithmically for 461-3
Creation of note and other events based on prototypes
*/
/* synthesis node creation */
/*
There are two main ways to create notes in SC- through the use of server commands, and through the Node Objects (Synth)
*/
// a sample synthdef
SynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, XLine.kr(amp, 0.00001, dur, doneAction: 2)))
}).load(s);
// messaging style:
s.sendBundle(0.1, [\s_new, \test, s.nextNodeID, 0, 1, \freq, 300, \amp, 0.1, \dur, 0.2]);
// Node Object style
Synth(\test, [\freq, 300, \amp, 0.1, \dur, 0.2]);
/*
Synth objects are a little cleaner - take care of node IDs and building the bundle
Messaging style is the most bare bones - reflects what is actually sent to the server, and the format is closer to what is needed for Score and NRT rendering.
Synth objects ALWAYS create the node immediately, and can NOT be used for NRT. Also, for an object oriented style, the way arguments are passed to the synth that is created doesn't really reflect the look of the rest of the language.
Ctk tries to-
take advantage of the object-oriented style of SC
create notes that can be scheduled
Create objects that can be used in both real-time and non-real-time mode
(more advanced) - manage memory and buffers in a logical way
*/
/*
The basic classes for note creation are CtkNoteObject and CtkNote. CtkNoteObject is mostly a way to prototype a SynthDef that will create new instance of CtkNote for you. You will not actually create new CtkNotes by hand.
CtkNoteObject takes a SynthDef. It will call .load for you, allowing it to be used in RT and NRT.
Functions kind of like (defobject) in CM.
*/
a = CtkNoteObject(
SynthDef(\test, {arg freq, amp, dur;
Out.ar(0, SinOsc.ar(freq, 0, XLine.kr(amp, 0.00001, dur, doneAction: 2)))
})
);
// create a new CtkNote from the CtkNoteObject
b = a.new;
// set the arguments of the synth
b.freq_(440);
b.amp = 0.1;
b.dur_(1);
// play it!
b.play;
b.bundle;
// multiple instances of the note can be created from the CtkNoteObject
10.do({arg i;
var j;
j = i + 1;
a.new.freq_(300*j).amp_(j.reciprocal*0.1).dur_(j).play;
});
/*
no message is sent to the server until .play is called. Once .play is called, the object enters
'real-time' mode.
*/
Routine.run({
y = Array.fill(10, {arg i;
var j;
j = i + 1;
a.new.freq_(300*j).amp_(j.reciprocal*0.1).dur_(j);
}).scramble;
y.do({arg me;
me.play;
0.1.rrand(1.0).wait;
})
});
/*
values in a synth can be set in real-time.
*/
a = CtkNoteObject(
SynthDef(\test, {arg gate = 1, freq, amp, pan;
Out.ar(0, Pan2.ar(
SinOsc.ar(freq, 0,
EnvGen.kr(
Env([0, 1, Rand.new(0.6, 0.8), Rand.new(0.8, 1.0), 0],
[1, 1, 1, 1],
[10, -10, 10, -10],
3, 1),
gate, doneAction: 2) * amp
),
pan)
)
})
);
b = a.new.freq_(300).amp_(0.1).pan_(0);
b.play;
c = a.new.freq_(b.freq + 100).amp_(b.amp * 0.5).pan_(-1).play;
c.release;
b.release;
b.freq_(550);
b.amp_(0.01);
b.pan_(1.0);
b.pan_(-1.0);
// will set a 'gate' arg to 0. Other keywords can be passed in. Can schedule for the future
b.release(2);
b.play.release(5); // play and immediately schedule the release
// an argument list
b.args; // the current values of the CtkNote
a.args; // the defaults on the CtkNoteObject
// also, each instance of a CtkNote can have its arguments current value returned:
b.freq;
b.amp;
b.gate; // .release does not change the gate value. This way, you can replay the note
b.play;
b.release;
/*
CtkProtoNotes is a Dictionary of CtkNoteObjects. The CtkNoteObjects are created for you from SynthDefs that are added to an instance of CtkProtoNotes
*/
a = CtkProtoNotes(
SynthDef(\test, {arg gate = 1, freq, amp;
var env, envgen, src;
// Controls with arrays in a SynthDef are handled just like other args!
env = Control.names([\env]).kr(Env.newClear(20));
envgen = EnvGen.kr(env, gate, doneAction: 2);
src = SinOsc.ar(freq, 0, amp * envgen);
Out.ar(0, Pan2.ar(src, Rand(-1.0, 1.0)));
}),
SynthDef(\test2, {arg gate = 1, freq, amp;
var env, envgen, src;
env = Control.names([\env]).kr(Env.newClear);
envgen = EnvGen.kr(env, gate, doneAction: 2);
src = BPF.ar(WhiteNoise.ar(amp), freq, 0.01, amp * envgen);
Out.ar(0, Pan2.ar(src, Rand(-1.0, 1.0)));
})
);
// access a CtkNoteObject from the CtkProtoNotes dictionary, create a new instance
b = a[\test2].new;
// set its values and play it ... look! simply pass in the envelope!
b.freq_(440).amp_(1.0).env_(Env([0, 1, 0, 1, 0], [0.5, 0.3, 0.2, 0.5], \sin, 3)).play;
// same, all at once
c = a[\test].new.freq_(440).amp_(0.1).env_(Env([0, 1, 0], [0.5, 0.5], \sin, 1)).play;
c.release;
b.release;
/*
The CtkNote arguments check for a few things. The above treatment of Envelopes is part of that. Also, if a CtkControl is passed in as an argument, the values on the control bus are mapped to that parameter.
*/
(
a = CtkProtoNotes(
SynthDef(\test, {arg gate = 1, freq, amp;
var env, envgen, src;
env = Control.names([\env]).kr(Env.newClear(8));
envgen = EnvGen.kr(env, gate, doneAction: 2);
src = SinOsc.ar(freq, 0, amp * envgen);
Out.ar(0, Pan2.ar(src, Rand(-1.0, 1.0)));
}),
SynthDef(\test2, {arg gate = 1, freq, amp;
var env, envgen, src;
env = Control.names([\env]).kr(Env.newClear(8));
envgen = EnvGen.kr(env, gate, doneAction: 2);
src = BPF.ar(WhiteNoise.ar(amp), freq, 0.01, amp * envgen);
Out.ar(0, Pan2.ar(src, Rand(-1.0, 1.0)));
}, [0, 1, 0]),
SynthDef(\control, {arg outbus, rate, low, hi;
Out.kr(outbus, LFNoise2.kr(rate).range(low, hi))
})
);
)
b = a[\test2].new.freq_(440).amp_(1.0).env_(Env([0, 1, 0], [0.5, 0.5], \sin, 1)).play;
c = a[\test].new.freq_(440).amp_(0.1).env_(Env([0, 1, 0], [0.5, 0.5], \sin, 1)).play;
b.freq_(660);
b.release;
c.release(2); // release in 2 seconds
z = CtkControl.new; // allocates a control bus from the server
z.play;
z.set(440)
b = a[\control].new.outbus_(z.bus).rate_(2).low_(440).hi_(880).play;
// use the instance of CtkControl as an argument
c = a[\test2].new.freq_(z).amp_(3).env_(Env([0, 1, 0], [1, 1], \sin, 1)).play;
d = a[\test].new.freq_(z).amp_(0.2).env_(Env([0, 1, 0], [10, 0.1], \sin, 1)).play;
b.free;
z.set(660); // set the control bus value by hand
// arguments that are changed while a CtkNote is running will change that parameter in real-time
c.amp = 0.5;
d.amp = 0.05;
c.amp_(2.0);
d.amp_(0);
[c, d].do({arg me; me.release}); // these have release nodes
/*
CtkBuffer- depending on how you need to use the Buffer, you provide different information
through creation methods
*/
//for use with PlayBuf - just provide the path. The file will be loaded for you
a = CtkBuffer.playbuf("sounds/a11wlk01-44_1.aiff").load; // load and sync with the server
b = CtkNoteObject(
SynthDef(\test, {arg buffer;
var play;
play = PlayBuf.ar(1, buffer);
Out.ar(0, play)
})
);
c = b.new.buffer_(a).play; // Ctk tries to know about its own Objects!
c.free; // free the synth
a.free; // free the buffer
// for use with DiskIn - path and buffer size (should be power of two)
a = CtkBuffer("sounds/a11wlk01-44_1.aiff", 32768).load(sync: true);
b = CtkNoteObject(
SynthDef(\test, {arg buffer;
var play;
play = DiskIn.ar(1, buffer);
Out.ar(0, play)
})
);
c = b.new.buffer_(a).play;
c.free; // free the synth
a.free;
a = CtkBuffer.diskin("sounds/a11wlk01-44_1.aiff", 32768).load(sync: true);
// just allocate space... no need to load anything into it.
b = CtkBuffer.buffer(32768).load;
c = CtkNoteObject(
SynthDef(\test, {arg diskbuf, buffer;
var play, del;
play = DiskIn.ar(1, diskbuf);
del = BufDelayN.ar(buffer, play, 0.25);
Out.ar(0, [play, del])
})
);
d = c.new.diskbuf_(a).buffer_(b).play;
d.free;
a.free;
b.free;
// allocating and setting values
a = CtkBuffer.buffer(2048);
a.load;
// due to UDP packet limitations, only 1024 samples or less can be set
// loadCollection methods still need to be implemented
a.set(0.0, 0, Array.fill(1024, {-1.0.rrand(1.0)}));
b = {PlayBuf.ar(1, a.bufnum, loop: 1) * 0.1}.play(s);
b.release
// change the values in the buffer
a.set(0.0, 1024, Array.fill(1024, {-1.0.rrand(1.0)}));
// zero it out
a.zero;
// refill them
a.set(0.0, 0, Array.fill(1024, {-1.0.rrand(1.0)}));
a.set(0.0, 1024, Array.fill(1024, {0.0.rrand(1.0)}));
b.free;
a.free;
// with Osc, OscN and Shaper with the fill commands
a = CtkBuffer.buffer(32768).load;
a.sine1(0.0, 1, 1, 1, 0.3, 0.5, 0.7);
b = {Osc.ar(a.bufnum, 440, mul: 0.5)}.play(s);
a.sine1(0.0, 1, 1, 1, 0.3, 0.2, 0.5);
a.sine3(0.0, 1, 1, 1, 1, 0.3, 0.0, 4, 0.2, 0.2, 9, 0.4, 0.5);
b.free;
a.free;
a = CtkBuffer.buffer(32768).load;
a.sine1(0.0, 1, 0, 1, 0.3);
b = {OscN.ar(a.bufnum, 440, mul: 0.1)}.play(s);
a.sine1(0.0, 1, 0, 1, 0.3, 0.2, 0.5);
a.sine3(0.0, 1, 0, 1, 1, 0.3, 0.0, 4, 0.2, 0.2, 9, 0.4, 0.5);
b.free;
a.free;
a = CtkBuffer.buffer(32768).load;
a.cheby(0.0, 1, 1, 1, 0.3, 0.2, 0.5);
s.scope;
b = {Shaper.ar(a.bufnum, SinOsc.ar(440, 0, 0.5), mul: 0.2)}.play(s);
b.free;
a.cheby(0.0, 1, 1, 1, 1.0, 0.5, 0.2);
a.cheby(0.0, 1, 1, 1, 0.1, 0.5, 1.0);
a.cheby(0.0, 1, 1, 1, 1.0);
b.free;
a.free;
a.size
a.duration;
// Test with DiskOut
a = CtkBuffer.buffer(32768).load;
// open a file for writing with DiskOut
a.openWrite(0.0, "~/Desktop/test.aiff".standardizePath, 'aiff', 'int16', -1);
b = {DiskOut.ar(a.bufnum, SinOsc.ar(440, 0, 0.2))}.play(s);
// let it run for a moment... then kill
b.free;
//close the file
a.closeWrite.free;
// test to make sure it worked
a = CtkBuffer("~/Desktop/test.aiff".standardizePath).load;
b = {PlayBuf.ar(1, a.bufnum)}.play;
b.free; a.free;
// the fillWithEnv method. A personal favorite!
a = CtkBuffer.buffer(1024).fillWithEnv(env: Env([0, 1, 0], [0.5, 0.5], \sin)).load;
b = {SinOsc.ar(440, 0, 0.2) * BufRd.ar(1, a.bufnum, Phasor.ar(0.0, 0.01, 0, 1024))}.play(s)
b.free;
a.free;