/*
ProcMod / ProcEvents
*/
/* basic ProcMod usage */
a = CtkNoteObject(
SynthDef(\singrain, {arg envbus, freq, dur;
OffsetOut.ar(0, Pan2.ar(
SinOsc.ar(freq, 0, 0.1) * In.kr(envbus) *
EnvGen.ar(
Env.sine(dur, 1), doneAction: 2),
Rand.new(-1.0, 1.0)
))
})
);
z = ProcMod(Env([0, 1, 0], [1, 3], \sin, 1), 0.2, \imnew)
.function_({arg group, envbus, server;
Task({
loop({
a.new(target: group).freq_(880.rrand(1760)).dur_(0.1)
.envbus_(envbus).play;
0.02.wait;
})
})
})
.onReleaseFunc_({"I just released".postln})
.releaseFunc_({"I'm done".postln});
// ProcMod controlling ProcMod
y = ProcMod.new.function_({
Routine({
z.play;
2.wait;
z.release;
2.wait;
z.play;
2.wait;
z.release;
})
});
y.play;
y.function.reset;
z.play;
z.release;
z.gui;
(
// set-up variables
var insbus, routebus, bufsize, inbuf, c, initproc, endproc, pevents, warpproc, bpfproc, pnotes, thisdir;
thisdir = Document.current.dir;
// load the CtkProtoNotes from another file
pnotes = (thisdir ++ "/class10SD.rtf").load;
s = Server.local;
s.options.numOutputBusChannels_(8)
.numInputBusChannels_(8)
.memSize_(32768);
insbus = s.options.numOutputBusChannels + 6;
bufsize = 10; // in MINUTES!
s.boot;
// ProcEvents takes a ProcMod that will be executed when the first event
// is triggered, as well as a ProcMod to be executed when the events are
// finished or killed
// .. so here some events that need to happen no matter where we are
// starting in the event list
// this function returns the ProcMod.. the inbus, routebus and recordbuf
// won't be allocated until the server-boot command is given, so they will
// be passed in as arguments to the function
initproc = {arg inbus, routebus, recordbuf;
ProcMod(id: \first)
.function_({
var innode;
// these init events will run at the heaad and tail of group 0,
// to ensure they are always first or last in the order of
// execution. read in our input and limit it... send it out to
// a virtual bus
innode = pnotes[\limiter].new(addAction: 0, target: 0)
.inbus_(inbus).outbus_(routebus).level_(3.dbamp).play;
pnotes[\record].new(addAction: 3, target: innode)
.inbus_(routebus).buffer_(recordbuf).play;
pnotes[\limitout].new(addAction: 1, target: 0)
.inbus_(0).level_(1).play;
});
};
// ... and here is some that have to happen when we are finished
endproc = ProcMod(id: \last)
// free the buffer!!!
.function_({
inbuf.free;
s.freeAll;
"Buffer freed".postln});
// I want a ProcMod that will create a Warp texture. Since I may want to
// call this same basic idea a number of times, I wll create a function
// that will create a new ProcMod every time I need one. This keeps the
// whole approach modular
warpproc = {arg id, amp, procenv, outbus, warpbuf, freqscale, stretch, winsize, winenv, overlaps,
addAction = 0, target = 1;
var proc;
// when you pass an envelope to the ProcMod, a control bus and a synth
// will be created that will write the envelope's values out to the
// control bus. We can acces that bus (in this case) by calling
// proc.envbus later.
proc = ProcMod(procenv, amp, id, addAction: addAction, target: target, server: s)
.function_({arg group, envbus, server;
// the ProcMod function can take a single-shot function OR a
// Routine / Task. If a Routine or Task is used, it will be run
// until the ProcMod has released.
Task({
var thisnode;
// this will create windows of warps... each warp will
// start slightly in the past
inf.do({arg i;
pnotes[\warps].new(target: group)
.outbus_(outbus).dur_(winsize).envbus_(envbus).warpbuf_(warpbuf)
.freqscale_(
freqscale.isArray.if({
freqscale.wrapAt(i)
}, {
freqscale
})
)
.starttime_(pevents.now - 0.1).stretch_(stretch).loc_(1.0.rand2)
.env_(winenv).play;
(winsize / overlaps).wait;
})
})
})
};
bpfproc = {arg id, amp, procenv, inbus, outbus, freqstart, winsize,
overlaps, addAction = 0, target = 1;
var proc;
proc = ProcMod(procenv, amp, id, addAction: addAction, target: target, server: s)
.function_({arg group, envbus, server;
Task({
// for this gesture, the longer it goes on, the wider
// the freq gliss
var thisnode, freqend, freqenv, thisfreq, winenv, rqenv;
winenv = Env.sine(1, 1).asArray;
freqend = Env([0, 12], [30]);
rqenv = Env([0.1, 0.001], [30]);
inf.do({arg i;
// calculate some parameters for this note
// create a new freqenv for each note
thisfreq = freqstart.isArray.if({
freqstart.choose
}, {
freqstart
});
// pevents.now(id) will return the time within this event
freqenv = Env([thisfreq,
thisfreq * freqend[pevents.now(id)].rand.midiratio],
[winsize], \exp).asArray;
pnotes[\bpf].new(target: group)
.inbus_(inbus).outbus_(outbus).dur_(winsize).envbus_(envbus)
.rq_(rqenv[proc.now]).env_(winenv).freq_(freqenv).play;
(winsize / overlaps).wait;
})
})
});
};
// certain things (like messages tot he Server object) probably won't
// work until the server is booted... wrap all that into the waitForBoot
// method
s.waitForBoot({
Routine.run({
var warpsend;
c = Condition.new;
warpsend = s.audioBusAllocator.alloc(2);
// an audio bus to route the limited ins input
routebus = s.audioBusAllocator.alloc(1);
// allocate the buffer
inbuf = CtkBuffer.buffer(bufsize * 60 * s.sampleRate, server: s).load;
s.sync(c);
"buffer allocated".postln;
// see the ProcEvents helpfile
pevents = ProcEvents.new([
/* 0 */ [nil, nil],
/* 1 */ [
warpproc.value(\p1, 1,
Env([0, 1, 0], [4, 4], [4, -2], 1),
0, inbuf, [3, -7, 5, 3, -12].midiratio, 0.2, 8,
Env([0, 1, 1, 0], [0.2, 0.6, 0.2], \sin), 4),
nil
],
/* 2 */ [
warpproc.value(\p2, 1,
Env([0, 1, 0], [4, 4], [4, -2], 1), 0, inbuf,
[12, 5, 15, 3, -12].midiratio, 0.8, 3,
Env([0, 1, 1, 0], [0.2, 0.6, 0.2], \sin),4),
\p1
],
/* 3 */ [
bpfproc.value(\p3, 0.5,
Env([0, 1, 0], [4, 10], [4, -10], 1), routebus, 0,
Array.fill(10, {1000.rrand(5000)}), 6, 4),
nil
],
/* 4 */ [nil, [\p2, \p3]],
/* 5 */ [
[
warpproc.value(\p5a, 1, Env([0, 1, 0], [1, 10], \sin, 1),
warpsend, inbuf,[3, -7, 5, 3, -12].midiratio, 0.2, 8,
Env([0, 1, 1, 0], [0.2, 0.6, 0.2], \sin), 4),
bpfproc.value(\p5b, 0.5,
Env([0, 1, 0], [4, 10], [4, -10], 1), warpsend, 0,
Array.fill(10, {1000.rrand(5000)}), 6, 4, 1, 1),
bpfproc.value(\p5c, 0.5,
Env([0, 1, 0], [4, 10], [4, -10], 1), warpsend + 1, 0,
Array.fill(10, {1000.rrand(5000)}), 4, 3, 1, 1)
],
nil],
/* 6 */ [nil, [\p5a, \p5b, \p5c]]
], 1, initproc.value(insbus, routebus, inbuf), endproc, "Demo");
// Routines run on the SystemClock (more accurate) but GUI events
// need to be handled on the AppClock. To do this, we wrap the GUI
// call into a function, and 'defer' it to the AppClock
{pevents.perfGUI; pevents.bigNumGUI}.defer;
})
})
)
/* Homework 2
1) Read the Writing-Classes helpfile, and review the brief notes on classes from class 5. Then, create a class that performs operations on data for you. You should use instance variables to store data or objects, have at least a *new creation method. Also, look at the posted Getval class as an example. This class will simply pull together any combination of functionality that you may already use, or create something new. This class does not need to be complicated... this is simply for experience.
2) Create a second class that acts as a pseudo-UGen. BLowPass4, BHiPass4 and BarkDelay are good examples.
3) Using ProcMod and possibly ProcEvents, create a small exercise (1-3 minutes) that steps through a number of processes. Your piece should be completely synthesized or your source should be a single input - either a SynthDef that is writing sound to a virtual bus, a playbuf that is playing a soundfile, or live input. Include at least two different kinds of processes, and at least three events. Feel free to make use of patterns to help with decision making.
4) Be prepared to discuss what you want to do for your final project. Your ideas should include a conceptual approach to the overall work, as well as your means for producing the work.
Due Tuesday, July 10th
*/