/* 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!"
*/

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