212 lines
7.3 KiB
Text
212 lines
7.3 KiB
Text
class:: Pproto
|
|
summary:: provide a proto event for an event stream
|
|
related:: Classes/Pattern
|
|
categories:: Streams-Patterns-Events>Patterns>Server Control
|
|
|
|
description::
|
|
|
|
Pproto uses the strong::makeFunction:: to allocate resources (buffers, buses, groups) and create a protoEvent that makes those resources available to a pattern. It is fully compatible with non-realtime synthesis using strong::render::.
|
|
|
|
The strong::makeFunction:: "makes" the protoEvent (i.e. protoEvent is code::currentEnvironment::). Typically, it defines and yields a sequence of events that create the needed resources using the following link::Overviews/Event_types:::
|
|
|
|
definitionList::
|
|
## \allocRead || load a file from ~path, starting at ~firstFileFrame, reading ~numFrames sample frames
|
|
## \cue || cue a file for DiskIn, with ~bufferSize frames
|
|
## \table || load ~amps directly into a buffer
|
|
## \sine1 || generate a buffer from ~amps
|
|
## \sine2 || generate a buffer from ~freqs, ~amps
|
|
## \sine3 || generate a buffer from ~freqs, ~amps, ~phases
|
|
## \cheby || generate a waveshape buffer from ~amps
|
|
## \audioBus || allocate ~channels consecutive audio buses
|
|
## \controlBus || allocate ~channels consecutive control buses
|
|
## \on || create a synth
|
|
|
|
::
|
|
|
|
note::
|
|
These eventTypes will allocate their own buffers and buses unless they are specified. To support this, the key code::\bufNum:: is used rather than code::\bufnum:: which has a default value assigned.
|
|
::
|
|
|
|
When Pproto ends, these eventTypes will respond to the strong::cleanup:: call by strong::deallocating any resources they have allocated::. Do not assume your buffers, buses etc. will exist after Pproto stops!
|
|
|
|
The function yields each event. That event is then performed with possible modifications by enclosing patterns and the player (either an link::Classes/EventStreamPlayer:: or a link::Classes/ScoreStreamPlayer::). The resultant event is returned to the function where it can be assigned to a key within the protoEvent.
|
|
|
|
The patternarray is played using Pfpar, a variant of Ppar that ends when any of its subpatterns end. In this way,
|
|
you can use Pproto to create effects that can be controlled by a pattern that runs in parallel with the note generating pattern and ends together with that note generating pattern (see example 0 below).
|
|
|
|
A strong::cleanupFunction:: that deallocates resources when the pattern ends or is stopped is automatically created. It can be replaced with a user defined cleanup if needed. This function receives two arguments: strong::proto::, the prototype event, and strong::flag::, which is set false if all nodes have been freed already by link::Classes/CmdPeriod::.
|
|
|
|
Examples::
|
|
|
|
note::The syntax used for patterns like Pbind and Pmono is a little bit different for making the code more readable. The difference has nothing to do with Pproto. This is the same: code::Pbind(\freq, 700, \amp, 0.1):: and code::Pbind(*[freq: 700, amp: 0.1])::
|
|
::
|
|
|
|
|
|
subsection::Example 1, loading and granulating a sound file.
|
|
|
|
code::
|
|
(
|
|
SynthDef(\help_playbuf, { | out=0, bufnum = 0, rate = 1, startPos = 0, amp = 0.1, sustain = 1, pan = 0, loop = 1|
|
|
var audio;
|
|
rate = rate * BufRateScale.kr(bufnum);
|
|
startPos = startPos * BufFrames.kr(bufnum);
|
|
|
|
audio = BufRd.ar(1, bufnum, Phasor.ar(0, rate, startPos, BufFrames.ir(bufnum)), 1, 1);
|
|
audio = EnvGen.ar(Env.sine, 1, timeScale: sustain, doneAction: Done.freeSelf) * audio;
|
|
audio = Pan2.ar(audio, pan, amp);
|
|
OffsetOut.ar(out, audio);
|
|
}).add;
|
|
|
|
a = Pproto({
|
|
~newgroup = (type: \group).yield;
|
|
~sf1 = (type: \allocRead, path: Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff").yield;
|
|
},
|
|
Pbind(*[
|
|
instrument: \help_playbuf,
|
|
dur: Pseg([0,0,0.25,0.5, 0.75, 1],10).linexp(0,1,0.01,2),
|
|
legato: 4,
|
|
startPos: Pn(Pseg([0,1], 20), inf),
|
|
rate: Pwhite(1, 1).midiratio,
|
|
loop: 0,
|
|
group: Pkey(\newgroup),
|
|
bufnum: Pkey(\sf1)
|
|
])
|
|
);
|
|
a.play;
|
|
)
|
|
|
|
//a.render("sounds/test.aif", 40)
|
|
//SoundFile.openRead("sounds/test.aif").play
|
|
::
|
|
|
|
subsection::Example 2, loading a waveform buffer and modifying it in performance.
|
|
|
|
code::
|
|
(
|
|
SynthDef(\osc,{ arg out=0, bufnum=0, numbufs = 8, sustain = 1, freq = 500, amp = 0.1, pan = 0;
|
|
var audio;
|
|
audio = Osc.ar(bufnum, freq);
|
|
audio = EnvGen.ar(Env.linen(0.01, 0.90,0.9), 1, timeScale: sustain, doneAction: Done.freeSelf) * audio;
|
|
audio = Pan2.ar(audio, pan, amp);
|
|
OffsetOut.ar(out, audio);
|
|
}).add;
|
|
|
|
b = Pproto({
|
|
~bufnum = (type: \sine1, amps: 1.0/[1,2,3,4,5,6] ).yield;
|
|
},
|
|
Ppar([
|
|
Pbind(*[
|
|
instrument: \osc,
|
|
freq: Pwhite(1, 16) * 100,
|
|
detune: Pfunc { Array.fill(3.rand + 1, {3.0.rand}) },
|
|
dur: Prand([2,2,2.5,1],10),
|
|
db: Pn(Pstep([-10, -20, -20, -15, -20, -20, -20], 0.5) ),
|
|
legato: Pwhite(0.0,1).linexp(0,1,0.1, 3)
|
|
]),
|
|
Pbind(*[
|
|
type: \sine1,
|
|
amps: Pseg(Pfunc{ | ev | Array.fill(10, {1.0.rand}) }, 1),
|
|
numOvertones: Pseg(Pwhite(0, 9), 10).asInteger,
|
|
amps: Pfunc{ | ev | ev[\amps].copyRange(0, ev[\numOvertones]) },
|
|
dur: 0.05,
|
|
bufNum: Pkey(\bufnum)
|
|
])
|
|
])
|
|
);
|
|
b.play
|
|
)
|
|
|
|
//b.render("sounds/test.aif", 40)
|
|
//SoundFile.openRead("sounds/test.aif").play
|
|
::
|
|
|
|
subsection::Example 3, loading a waveshaper buffer and modifying it in performance.
|
|
|
|
code::
|
|
(
|
|
SynthDef(\shaper,{ arg out=0, bufnum=0, numbufs = 8, sustain = 1, freq = 500, amp = 0.1, pan = 0;
|
|
var audio;
|
|
audio = SinOsc.ar(freq);
|
|
audio = EnvGen.ar(Env.linen(0.4, 0.50,0.9), 1, timeScale: sustain, doneAction: Done.freeSelf) * audio;
|
|
audio = Shaper.ar(bufnum, audio);
|
|
audio = Pan2.ar(audio, pan, amp);
|
|
OffsetOut.ar(out, LeakDC.ar(audio));
|
|
}).add;
|
|
|
|
c = Pproto({
|
|
~bufnum = (type: \cheby, amps: 1.0/[1,2,3,4,5,6] ).yield;
|
|
},
|
|
Ppar([
|
|
Pbind(*[
|
|
instrument: \shaper,
|
|
freq: Pwhite(1, 16) * 100,
|
|
detune: Pfunc { Array.fill(3.rand + 1, {3.0.rand}) },
|
|
dur: Prand([2,2,2.5,1],inf),
|
|
db: Pn(Pstep([-10, -20, -20, -15, -20, -20, -20], 0.5) ),
|
|
legato: Pwhite(0.0,1).linexp(0,1,1.1, 5)
|
|
]),
|
|
Pbind(*[
|
|
type: \cheby,
|
|
amps: Pseg(Pfunc{ | ev | Array.fill(10, {1.0.rand}) }, 4),
|
|
dur: 0.05
|
|
])
|
|
])
|
|
);
|
|
c.play
|
|
)
|
|
//c.render("sounds/test.aif", 40)
|
|
//SoundFile("sounds/test.aif").play
|
|
//
|
|
::
|
|
|
|
subsection::Example 4, using an effect with parallel control.
|
|
|
|
code::
|
|
(
|
|
SynthDef(\echo, { arg out=0, maxdtime=0.2, dtime=0.2, decay=2, gate=1;
|
|
var env, in;
|
|
env = Linen.kr(gate, 0.05, 1, 5, 2);
|
|
in = In.ar(out, 2);
|
|
XOut.ar(out, env, CombL.ar(in * env, maxdtime, Lag.kr(dtime, 4), decay, 1, in));
|
|
}, [\ir, \ir, 0.1, 0.1, 0]).add;
|
|
|
|
SynthDef(\fm, { arg out=0, freq, index, decay=2, gate=1;
|
|
var env, in;
|
|
env = Linen.kr(gate, 0.05, 1, 5, 2);
|
|
in = In.ar(out, 2);
|
|
XOut.ar(out, env, SinOsc.ar(freq, in * index));
|
|
}).add;
|
|
|
|
Pproto({
|
|
// start two effect synths (just switching them on) and store the parameters in the environment
|
|
~fsynth = (type: \on, instrument: \fm, freq: 4, index: 1, addAction: 1, db: -30).yield;
|
|
~fControl = [\set, ~fsynth[\id], ~fsynth[\msgFunc] ];
|
|
|
|
~synth = (type: \on, instrument: \echo, addAction: 1).yield;
|
|
~sControl = [\set, ~synth[\id], ~synth[\msgFunc] ];
|
|
|
|
}, [
|
|
Pbind(*[
|
|
#[type, id, msgFunc], Pkey(\fControl), // fetch the parameters from the environment
|
|
|
|
freq: Pseg([0,1], 10).linexp(0,1, 0.1, 1000),
|
|
index: Pseg([0,1], 10).linexp(0,1, 0.1, 100),
|
|
dur: 0.1
|
|
]),
|
|
Pbind(*[
|
|
#[type, id, msgFunc], Pkey(\sControl), // fetch the parameters from the environment
|
|
|
|
dtime: Pwhite(0,0.2),
|
|
decay: Pwhite(0.1,2),
|
|
dur: 1
|
|
]),
|
|
Pbind(*[
|
|
instrument: \default,
|
|
freq: Pwhite(1,16) * 100,
|
|
dur: 0.2,
|
|
db: 0
|
|
])
|
|
]
|
|
).play
|
|
)
|
|
::
|