/* Class 4 */


/*


Bundles and precision of execution-


*/

(

s.boot;


SynthDef(\sin, {arg freq = 400, amp = 1, dur = 1;

var env, src;

env = EnvGen.kr(

Env([0, 1, 0], [0.5, 0.5], \sin),

timeScale: dur, levelScale: amp,

doneAction: 2);

src = SinOsc.ar(freq, 0, env);

Out.ar(0, Pan2.ar(src, Rand.new(-0.7, 0.7)));

}).load(s);

)


s.sendMsg(\s_new, \sin, s.nextNodeID, 0, 1);

// Use a bundle to ensure timing


s.sendBundle(0.1, [\s_new, \sin, s.nextNodeID, 0, 1]);


(

var basefreq = 400;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99, \dur, 0.8, \amp, 0.4],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97, \dur, 2, \amp, 0.3],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01, \dur, 2.3, \amp, 0.1],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59, \dur, 0.9, \amp, 0.05],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99, \dur, 4.1, \amp, 0.02]

);

)



(

var basefreq = 400;

s.sendBundle(nil, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), 

\dur, 0.8, \amp, 0.4],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), 

\dur, 2, \amp, 0.3],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), 

\dur, 2.3, \amp, 0.1],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), 

\dur, 0.9, \amp, 0.05],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), 

\dur, 4.1, \amp, 0.02]

);

)


// SystemClock


(

var func;

func = {

var basefreq = 400.rrand(200);

basefreq.postln;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), \dur, 0.8, 

\amp, 0.4],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), \dur, 2,

\amp, 0.3],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), \dur, 2.3, 

\amp, 0.1],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), \dur, 0.9, 

\amp, 0.05],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), \dur, 4.1, 

\amp, 0.02]

);

nil; // return nil... only play this once

};


// here, the func function will be evaluated in 2 seconds

SystemClock.sched(0.1, {func.value; "Post me".postln});

)


SystemClock.clear;


(

var func;

func = {

var basefreq = 400.rrand(200);

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), \dur, 0.8, 

\amp, 0.4],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), \dur, 2,

\amp, 0.3],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), \dur, 2.3, 

\amp, 0.1],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), \dur, 0.9, 

\amp, 0.05],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), \dur, 4.1, 

\amp, 0.02]

);

0.5.rrand(2.0).postln; // keep playing! this function returns a float

};


// here, the func function will be evaluated in 2 seconds

SystemClock.sched(2, func);

)


// clear ALL events from the SystemClock

SystemClock.clear;



(

var func, clock;

func = {

var basefreq = 400.rrand(200);

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), \dur, 0.8, 

\amp, 0.4],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), \dur, 2,

\amp, 0.3],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), \dur, 2.3, 

\amp, 0.1],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), \dur, 0.9, 

\amp, 0.05],

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), \dur, 4.1, 

\amp, 0.02]

);

0.5.rrand(2.0); // keep playing! this function returns a float

};


// create a new instance of TempoClock

clock = TempoClock.new;

clock.tempo_(1);

// here, the func function will be evaluated in 2 beats

clock.sched(2, func); // schedule notes

SystemClock.sched(15, {clock.clear}); // schedule the termination of the notes

)



/* Celia says - "I need some rhythm!"


*/

attachments/class04/DSCF0018.png

/* 


Routines and Tasks 


*/

// Routine will return a value that has been 'yield'ed

r = Routine({

"Step One".postln.yield;

"Step Two".postln.yield;

"Step Three... this function is done!".postln.yield;

});


r.next; // -> Step One

z = r.next; // -> Step Two

z; // equals the string that was returned

r.next; // -> Step  Three... this function is done!

r.next; // nil

r.reset; // you can reset the function

r.next; // Step One


// Routines can also be played as a stream, with timing involved

(

r = Routine({

var basefreq = 400.rrand(200);

basefreq.postln; // post what the current basefreq will be

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1, \amp, 0.5]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), \dur, 0.8, 

\amp, 0.4]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), \dur, 2, 

\amp, 0.3]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), \dur, 2.3, 

\amp, 0.1]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), \dur, 0.9, 

\amp, 0.05]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), \dur, 4.1, 

\amp, 0.02]);

r.yieldAndReset;

});

)

r.play(SystemClock);

r.stop; // you can stop it

r.play; // but can't start it again!

r.reset; //reset it first

r.play; // assumes SystemClock (its' default arg);


// Tasks are a little more flexible... they can be paused and restarted. 

// They can't reset themselves

// Tasks always return themselves


t = Task({

var basefreq = 400.rrand(200), tempo;

tempo = t.clock.tempo;

basefreq.postln; // post what the current basefreq will be

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq, \dur, 1 * tempo.reciprocal,

\amp, 0.5]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 0.99.rrand(1.05), \dur, 0.8 * tempo.reciprocal, 

\amp, 0.4]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 1.97.rrand(2.25), \dur, 2 * tempo.reciprocal, 

\amp, 0.3]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.01.rrand(2.5), \dur, 2.3 * tempo.reciprocal, 

\amp, 0.1]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 2.59.rrand(3.05), \dur, 0.9 * tempo.reciprocal, 

\amp, 0.05]);

0.1.rrand(0.5).wait;

s.sendBundle(0.1,

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, basefreq * 3.99.rrand(4.5), \dur, 4.1 * tempo.reciprocal, 

\amp, 0.02]);

});

t.clock; // defaults to TempoClock... just as accurate as SystemClock

t.play;

t.pause;

t.next;

t.resume;

t.reset;

c = TempoClock.new;

c.tempo_(32/60); // the Tempo default to 1 beat per second (or 60 BPM)... make it 32

t.play(c);

t.reset;

c.tempo_(240/60);

t.play(c);

c.tempo_(32/60);

// create instances with .do - less typing


t = Task({

var basefreq = 400.rrand(200), freqs, durs, amps;

var numnotes = 20;

("Basefreq: " ++ basefreq).postln; // post what the current basefreq will be

// calculate an array of frequencies for the notes

freqs = Array.fill(numnotes, {arg i; 

basefreq * ((i + 1) - 0.5).rrand((i + 1) + 0.5)});

("freqs: "++ freqs).postln;

durs = Array.fill(numnotes, {arg i; (numnotes-(i-1.0)).rand});

("durs: " ++ durs).postln;

amps = Array.fill(numnotes, {arg i; (i + 1).reciprocal});

("amps: " ++ amps).postln;

numnotes.do({arg i;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, freqs[i], \dur, durs[i], 

\amp, amps[i] * amps.sum.reciprocal]);

0.1.rrand(0.5).wait;

})

});

t.play;

t.reset;

t.play;

t.reset;


// Tasks as a result of a function... the function creates and returns a new Task


(

t = {arg basefreq, numnotes;

var task;

task = Task({

var freqs, durs, amps;

// calculate an array of frequencies for the notes

freqs = Array.fill(numnotes, {arg i; 

basefreq * ((i + 1) - 0.5).rrand((i + 1) + 0.5)});

freqs.postln;

durs = Array.fill(numnotes, {arg i; (numnotes-(i-1.0)).rand});

durs.postln;

amps = Array.fill(numnotes, {arg i; (i + 1).reciprocal});

amps.postln;

numnotes.do({

arg i;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, freqs[i], \dur, durs[i], 

\amp, amps[i] * amps.sum.reciprocal]);

0.1.rrand(0.5).wait;

})

});

task.play;

};

);


a = t.value(440, 10); // this returns the Task... so I can still pause it!

b = t.value(220, 30);

a.pause;

a.resume;

b.pause;

b.resume;


// Tasks of Tasks!

r = Task({

10.do({arg i;

// algoithmically control the pitches that will be created

t.value(220.rrand(440) * (i + 1), 10.rrand(20));

2.rrand(5).wait;

});

});

r.play;

r.reset;

r.play;

r.pause;

r.resume;

r.stop;


// Change the function 't' to only create the Task (don't play it)


(

t = {arg basefreq, numnotes;

var task;

task = Task({

var freqs, durs, amps;

// calculate an array of frequencies for the notes

freqs = Array.fill(numnotes, {arg i; 

basefreq * ((i + 1) - 0.5).rrand((i + 1) + 0.5)});

freqs.postln;

durs = Array.fill(numnotes, {arg i; (numnotes-(i-1.0)).rand});

durs.postln;

amps = Array.fill(numnotes, {arg i; (i + 1).reciprocal});

amps.postln;

numnotes.do({

arg i;

s.sendBundle(0.1, 

[\s_new, \sin, s.nextNodeID, 0, 1, \freq, freqs[i], \dur, durs[i], 

\amp, amps[i] * amps.sum.reciprocal]);

0.1.rrand(0.5).wait;

})

});

};


// a is an array of Tasks

a = Array.fill(10, {arg i; t.value(220.rrand(440) * (i + 1), 10.rrand(20))});

a.postln;

// iterate through the array of Tasks, play them, and wait in between

r = Task({

a.do({arg me, i;

me.postln;

me.play;

1.rrand(5).wait;

});

});

)


a[5].play;


a.choose.play;


r.play; // play the Task

r.pause;

r.play;

r.pause;

r.reset;

r.play;

( // pause the larger Task AND each individual Task (if it is playing)

r.pause;

// iterate over the array of Tasks 

a.do({arg me, i; 

// me is the individual Task in each slot of the Array... check if it is playing

me.pause;

})

)


r.resume; // start up again where we left off

r.stop;

// some other handy iterative techniques... all pass in a value and an incrementer

// Integer-reverseDo

reverseDo(10, {arg me, i; [me, i].postln;});

// or

10.reverseDo({arg me, i; [me, i].postln;});


// ArrayedCollection-reverseDo

reverseDo([220, 440, 880, 1760], {arg me, i; [me, i].postln});

//or

[220, 440, 880, 1760].reverseDo{arg me, i; [me, i].postln};


// loop

t = Task({ loop({ "This is a loop".postln; 1.wait })});

t.play;

t.stop;


/* WHENEVER YOU USE LOOP... MAKE SURE IT IS 1) IN A TASK OR ROUTINE 2) THERE IS SOME WAIT INVOLVED */

// block

a = block {arg func;

inf.do({arg i;

i.postln;

(i == 7).if({func.value(nil)}); // a conditional!

})

};

// a is the value returned from the block

a;

/* Conditionals / Booleans */


// if(boolean, trueFunc, falseFunc)

a = if(1 > 2, {"This is true".postln; 10}, {"This is false".postln; 20});

a

// or

a = (1 < 2).if({"True".postln; \yay}, {"False".postln; \boo})

a

// notice that

1 < 2; // -> true

2 < 1; // -> false


// you must surround your tests with parens!

((1 < 2) && (2 > 3)); // -> true- checks if both are true

((1 < 2) || (3 < 2)); // -> true- checks if either condition is true

(1 == 1); // checks equality

(1 != 2); // checks inequality

(1 <= 1); // less then or equal


/* Large scale structures, and Control Busses. */


// a couple of synthdefs... one with an envelope output to a CONTROL bus, 

// another with a tiny grain of sound, and a function that creates a task to run 

// them

(

SynthDef(\controlenv, {arg gate = 1, outbus;

var env;

// a  simple Env... 1 second up, hold, then one second down

env = EnvGen.kr(

Env([0, 1, 0], [1, 1], \sin, 1),

gate,

doneAction: 2);

Out.kr(outbus, env);

}).load(s);

SynthDef(\grainy, {arg freq = 440, amp = 1, dur = 1, loc = 0, control;

var src, env;

env = EnvGen.kr(

Env([0, 1, 0], [0.5, 0.5], \sin), 

timeScale: dur, levelScale: amp, doneAction: 2);

src = SinOsc.ar(freq);

env = env * In.kr(control); // multiply this envelopes values by the control 

// bus values

Out.ar(0, Pan2.ar(src * env, loc));

}).load(s);

t = {arg grainsize = 1, overlaps = 8;

var task, controlnode;

// grab a node number for the controlnode

controlnode = s.nextNodeID;

task = Task({

var controlbus, waittime;

// grab a control bus for the 'global' env

controlbus = s.controlBusAllocator.alloc(1);

// calculate the waittime between grains

waittime = grainsize / overlaps;

// start the controlenv

s.sendBundle(0.1, 

[\s_new, \controlenv, controlnode, 0, 1, \outbus, controlbus]);

loop({

s.sendBundle(0.1, 

[\s_new, \grainy, s.nextNodeID, 1, 1, \freq, 440.rrand(880), 

\amp, overlaps.reciprocal, \dur, grainsize, \control, controlbus]);

waittime.wait;

})

});

// return an array with the controlnode and the task (so we can use them later)

[controlnode, task];

};

);


// the function 't' returns an array of two values, we can use the # operator to

// assign those values

// to two different variables

#n, r = t.value(0.2, 8);

s.queryAllNodes

n;

r;

// play the task

r.play;

// free the envelope

s.sendMsg(\n_set, n, \gate, 0);

// sound stops... but the task is still going... stop it

r.stop;


#n, r = t.value(0.02, 1);

r.play;

// free the envelope

s.sendMsg(\n_set, n, \gate, 0);

// sound stops... but the task is still going... stop it

r.stop;



// control other parameters


(

SynthDef(\controlenv, {arg gate = 1, outbus, fadein = 1, fadeout = 1;

var env;

// a  simple Env... 1 second up, hold, then one second down

env = EnvGen.kr(

Env([0, 1, 0], [fadein, fadeout], \sin, 1),

gate,

doneAction: 2);

Out.kr(outbus, env);

}).load(s);

SynthDef(\grainy, {arg freq = 440, amp = 1, dur = 1, loc = 0, control;

var src, env;

env = EnvGen.kr(

Env([0, 1, 0], [0.5, 0.5], \sin), 

timeScale: dur, levelScale: amp, doneAction: 2);

src = SinOsc.ar(freq);

env = env * In.kr(control); // multiply this envelopes values by the control 

// bus values

Out.ar(0, Pan2.ar(src * env, loc));

}).load(s);

t = {arg grainsize = 1, overlaps = 8, fadein = 1, fadeout = 1;

var task, controlnode;

// grab a node number for the controlnode

controlnode = s.nextNodeID;

task = Task({

var controlbus, waittime;

// grab a control bus for the 'global' env

controlbus = s.controlBusAllocator.alloc(1);

// calculate the waittime between grains

waittime = grainsize / overlaps;

// start the controlenv

s.sendBundle(0.1, 

[\s_new, \controlenv, controlnode, 0, 1, \outbus, controlbus, \fadein, fadein, \fadeout, fadeout]);

loop({

s.sendBundle(0.1, 

[\s_new, \grainy, s.nextNodeID, 1, 1, \freq, 440.rrand(880), 

\amp, overlaps.reciprocal, \dur, grainsize, \control, controlbus]);

waittime.wait;

})

});

// return an array with the controlnode and the task (so we can use them later)

[controlnode, task];

};

);


#n, r = t.value(0.02, 1);

r.play;

// free the envelope

s.sendMsg(\n_set, n, \gate, 0);

// sound stops... but the task is still going... stop it

r.stop;


#n, r = t.value(1, 3, 5, 10);

r.play;

// free the envelope

s.sendMsg(\n_set, n, \gate, 0);

// sound stops... but the task is still going... stop it

r.stop;

/*


Control Busses

The Control.names UGen


*/


// A sample env

a = Env([440, 880, 220], [2, 10], [\sin, \exp]).plot; // you can plot Envs

a.at(0.2); // a's value at 0.2 seconds

a[1]; // using the [] shortcut

0.for(a.times.sum, {arg me; a[me].postln});


a.asArray;


// Using Env-at to possibly calculate freqs

(

SynthDef(\controlenv, {arg gate = 1, outbus;

var env, envgen;

// Control allows you to pass an array of values into a synth

// here, we are creating a dummy envelope with up to 10 empty time values

// your dummy value represents a maximum amount of data you can pass into 

// this Control

env = Control.names([\env]).kr(Env.newClear(10).asArray);

envgen = EnvGen.kr(

env,

gate,

doneAction: 2);

Out.kr(outbus, envgen);

}).load(s);

SynthDef(\grainy, {arg freq = 440, amp = 1, dur = 1, loc = 0, control;

var src, env;

env = EnvGen.kr(

Env([0, 1, 0], [0.5, 0.5], \sin), 

timeScale: dur, levelScale: amp, doneAction: 2);

src = SinOsc.ar(freq);

env = env * In.kr(control); 

Out.ar(0, Pan2.ar(src * env, loc));

}).load(s);

t = {arg grainsize = 1, overlaps = 8, controlenv = Env([0, 1, 0], [1, 1]), 

freq = 440, freqdev = 0.1;

var task, controlnode;

// grab a node number for the controlnode

controlnode = s.nextNodeID;

task = Task({

var controlbus, waittime, starttime;

// declaring the variables needed by the 'loop' will avoid the warning

var thisfreq, now;

// grab the current time from the Main clock... we will use this to see how long

// out process has been running so we can grab values from a possible freq 

// envelope

starttime = Main.elapsedTime;

controlbus = s.controlBusAllocator.alloc(1);

waittime = grainsize / overlaps;

// take the controlenv that is passed into the function, and convert it to an 

// Array

controlenv = controlenv.asArray;

// start the controlenv... now two OSC messages are needed, \s_new, and n_setn

s.sendBundle(0.1, 

[\s_new, \controlenv, controlnode, 0, 1, \outbus, controlbus],

// this will build the message to send the array values into the synth

[\n_setn, controlnode, \env, controlenv.size] ++ controlenv);

loop({

// compare the current Main time with the starttime for a 'now' value

now = Main.elapsedTime - starttime;

// check to see if freq is an Env or just a value

thisfreq = (freq.isKindOf(Env)).if({

// if an Env, calculate its value at 'now'

freq.at(now)

}, {

freq

});

// calculate a freq with the deviation worked in... rand 2 gives a 

// value between - value and + value

thisfreq = (thisfreq*freqdev).rand2 + thisfreq;

s.sendBundle(0.1, 

[\s_new, \grainy, s.nextNodeID, 0, 1, \freq, thisfreq, 

\amp, overlaps.reciprocal, \dur, grainsize, \control, controlbus]);

waittime.wait;

})

});

// return an array with the controlnode and the task (so we can use them later)

[controlnode, task];

};

)


#n, r = t.value(0.2, 8, Env([0.0001, 1, 0.01, 1, 0.00001], [0.2, 2, 5, 1], \exp),

440);

r.play;


r.stop;


(

#n, r = t.value(0.2, 8, Env([0.0001, 1, 0.00001], [0.2, 5], \exp, 1), 

Env([440, 880, 220], [5, 10], [\exp, \sin]), 0.2);

)

r.play;

s.sendMsg(\n_set, n, \gate, 0);

r.stop;


/*

More UGens to explore (part of my extension lib)


Pluck, MoogVCF, BEQSuite, SinGrain, FMGrain, SinGrainB, FMGrainB, SinGrainI, FMGrainI


HOMEWORK 1- Due Wednesday, June. 27th


1) create a function that will take an argument called basefreq, and will return an array of the first 16 harmonic partials of that basefreq

2) Using the In UGen, create 3 synthdefs that will

a) filter the input with a Resonz filter, with the freq argument of the 

Resonz controlled with a Random UGen

b) delay the input with a DelayC UGen

c) will filter the input with a CombC delay / filter, and route out to a 

virtual bus. Then a second SynthDef should read off that bus and apply a 

band pass filter (BPF). Create one instance of the CombC SynthDef, and 

multiple instances of the BPF reading off that CombC output... you may want 

to create a group. Watch out for order-of-execution problems.

3) Using Function, Task and/or Routine, create a short study that may or may not 

use real-time input. Using the synthdefs you created for problem 2 or others 

that you create specifically for this problem, create a few gestures that can be 

stored, started and stopped using global variables (as in the examples above). 

Comment your code heavily to describe what you are doing, and include in the 

execution of your piece some directions that involve timing, etc.  Or, 

if the timing of your piece is to be precise, create a master Routine or Task to 

play the piece for you.  1-3 minutes.

*/