PLbindefPar container for parallel PLbindefs which allows replacement in object prototyping style


Part of: miSCellaneous


Inherits from: Pdef



PLbindefPar employs a number of parallel PLbindefs, replacement of key streams can be done in object prototyping style in a dedicated PLbindefParEnvironment. This Environment is itself assigned to the PLbindefPar's name in an Environment of choice, by default the current Environment. Setting can thus be done in very condensed syntax, also in combination with EventShortcuts.


NOTE: It's recommended to do cleanup with 'remove' or 'removeAll' after using PLbindef / PLbindefPar as in examples below. Otherwise unwanted or strange behaviour might be caused by leftover sources when playing a new PLbindef / PLbindefPar example with same key. With SC >= 3.7 occasional posts of the default parent event occur with Pbindef, so also with PLbindefPar. I didn't notice any different other behaviour though.



See also: PLx suite, PLbindef, PLbindefEnvironment, PLbindefParEnvironment, EventShortcuts, PLx and live coding with Strings



Creation / Class Methods


*new (...args)

Creates a new PLbindefPar object. First arg should be the key, followed by the number of parallel PLbindefs and the key/value pairs. Values are assigned to single PLbindefs according to the convention of PLbindefParEnvironment::put, see there and examples below. The last arg can be an optional environment which determines where the corresponding PLbindefParEnvironment should be stored, by default this is the current Environment at instantiation time.


Instance Methods


updateSourceEnvir (pairs, size)

Updates PLbindefPar's PLbindefParEnvironment with passed sources. Normally you wouldn't call this explicitely.

clear

Stop parallel PLbindefs and remove name entry from refEnvir.

remove

Removes not only the PLbindefPar but also associated PLbindefs from global entry.

sourceEnvir

Getter for PLbindefPar's PLbindefParEnvironment.

refEnvir

Getter for the Environment where PLbindefPar's PLbindefParEnvironment is associated with PLbindefPar's key.

size

Getter for PLbindefPar's number of parallel PLbindefs.


Examples


// synthdefs to play with


(

SynthDef(\noise_grain, { |out = 0, freq = 400, att = 0.005, rel = 0.1, rq = 0.1, amp = 0.1|

    var sig = { WhiteNoise.ar } ! 2;

    sig = BPF.ar(sig, freq, rq) *

        EnvGen.ar(Env.perc(att, rel, amp), doneAction: 2) *

        (rq ** -1) * (250 / (freq ** 0.8));

    OffsetOut.ar(out, sig);

}).add;


SynthDef(\sin_grain, { |out = 0, freq = 400, att = 0.005, rel = 0.1, amp = 0.1|

    var sig = { SinOsc.ar(freq, Rand(0, 2pi)) } ! 2;

    sig = sig * EnvGen.ar(Env.perc(att, rel, amp), doneAction: 2);

    OffsetOut.ar(out, sig);

}).add;


SynthDef(\saw_grain, { |out = 0, freq = 400, att = 0.005, rel = 0.1, amp = 0.1|

    var sig = { VarSaw.ar(freq, Rand(0, 1)) } ! 2;

    sig = sig * EnvGen.ar(Env.perc(att, rel, amp), doneAction: 2);

    OffsetOut.ar(out, sig);

}).add;


EventShortcuts.on;

)




Ex.1) Setting key streams


// use with EventShortcuts


EventShortcuts.on


(

p = PLbindefPar(\v, 3,

    \i, [\saw_grain, \sin_grain, \noise_grain],

    \d, 0.8,

    \m, [65, 70, 80],

    \rq, 0.002,

\a, 0.03,

    \att, Pwhite(0.05, 0.01)

)

)


p.play


// set all streams


~v.d = 0.2



// set single streams


~v[0].d = 0.1


~v[2].d = 0.4



// parallel intervals and chords for single streams


~v[2].m = Pwhite(80, 90) + [0, 9]


~v[0].m = Pwhite(50, 65) + [0, 5]


~v[1].m = Pwhite(70, 75.5) + [0, 3, 7]




// differentiate setting with collection


~v.m = [50, Pwhite(90, 80), 96]



// reference to last sources


~v.m = ~v.m + 1


~v[2].m = ~v[2].m - Pwhite(0, 5)




// pause single streams


~v[1].isRest = true


~v[1].isRest = false



// shorter, pause with amplitude setting


~v[0].a = 0


~v[0].a = 0.05




// differentiate setting with collection produced by Function


~v.m = { |i| (i * 15) + PLseq((0..9)) + [55, 60] } ! 3



// set only some streams with an index list, passed as first arg to 'value'


~v.([0, 1], \d, 0.3)


~v.([1, 2], \d, 0.5)



// set all, they might be time-shifted


~v.d = 0.2



// hard sync with reset


p.reset


p.stop


p.remove




Ex.2) More parallel streams, from granular to additive

// use EventShortcuts


EventShortcuts.on


(

p = PLbindefPar(\y, 12,

// wrapped indexing: collection is used for all 12 streams

\i, [\saw_grain, \sin_grain, \noise_grain],

\d, (1..12)/100,

\m, { rrand(60, 90) } ! 12,

\a, 0.02,

\rq, 0.002

)

)


p.play


// evaluate more than once


~y.m = { rrand(60, 90) } ! 12



// refer to current midinotes


~y.m = ~y.m - 1



~y.d = [1, 2, 4] / 8


~y.d = ~y.d / 4


p.stop


p.remove



// replacement introducing additive structures


(

p = PLbindefPar(\add, 12,

\i, \sin_grain, 

\d, 1, 

\m, { Pn(Pseries(rrand(40, 60), Pwhite(1.0, 3), 20)) } ! 12, 

\att, 5,

\rel, 5,

\a, 0.005

).play

)


~add.f = { PLseq((1..16)) * (100 + Pwhite(0.0, 3)) } ! 12


p.stop


p.remove




Ex.3a) PLbindefPar with VarGui, same control params for parallel streams

// if different streams should read from one control

// it's best to refer via a dedicated key



// start with gui and define pitch sequence with sliders


(

EventShortcuts.on;


p = PLbindefPar(\w, 3,

\i, [\saw_grain, \sin_grain, \noise_grain],

\d, 0.25,

\n, PLseq(\midi),

\m, [70, 75, 80] + Pkey(\n),

\a, 0.05,

\rq, 0.005

);


VarGui([\midi, [0, 12, \lin, 1, 0] ! 7], stream: p.trace).gui

)


~w.d = [1, 2, 4] / 8



// see difference


~w.m = [70, 75, 80].collect(Pkey(\n) + _)  // ~w.m = [70, 75, 80].collect { |i| Pkey(\n) + i }



~w.m = [70, 75, 80] + Pkey(\n)



// three streams of chords, all refering to same sequence


~w.m = { |i| 70 + Pkey(\n) + [0, 7, 14] - (i * 12) } ! 3



// manipulate step source


~w.n = Pstutter(3, PLseq(\midi))


~w.n = ~w.n + PLseq([0, 0.5, 1])



~w.d = [1, 3, 4] / 8


p.remove



Ex.3b) PLbindefPar with VarGui, different control params for parallel streams

// three array controls


(

EventShortcuts.on;


p = PLbindefPar(\t, 3,

\i, [\saw_grain, \sin_grain, \noise_grain],

\d, 0.25,

\n, { |i| PLseq((\midi ++ i).asSymbol) } ! 3,

\m, [60, 75, 90].collect { |i| Pkey(\n) + i },

\a, 0.05,

\rq, 0.005

);


VarGui(

// three array var controls

({ |i| [(\midi ++ i).asSymbol, [0, 12, \lin, 1, 0] ! 7] } ! 3).flatten,

stream: p

).gui

)


~t.d = [1, 3, 2] / 8


~t.m = [[50, 55, 60], [76, 77, 78, 79], [86, 90, 94]].collect(Pkey(\n) + _)



~t.d = PLrand([1, 3, 2]) / 8



~t[2].a = 0


~t[0].a = 0



p.remove




Ex.4) PLbindefPar with PbindFx

See also Ex.7c in PbindFx help


// boot server with extended resources


(

s.options.numPrivateAudioBusChannels = 1024;

s.options.memSize = 8192 * 16;

s.reboot;


// fx synths


SynthDef(\echo, { |out, in, maxEchoDelta = 0.2, echoDelta = 0.1,

    decayTime = 1, amp = 1, mix = 1|

    var sig, inSig = In.ar(in, 2);

    sig = DelayL.ar(

        CombL.ar(inSig, maxEchoDelta, echoDelta, decayTime, amp),

        maxEchoDelta,

        maxEchoDelta - echoDelta

    );

    Out.ar(out, (1 - mix) * inSig + (sig * mix));

}).add;


SynthDef(\wah, { |out, in, resLo = 200, resHi = 5000,

    cutOffMoveFreq = 0.5, rq = 0.1, amp = 1, mix = 1|

    var sig, inSig = In.ar(in, 2);

    sig = RLPF.ar(

        inSig,

        LinExp.kr(LFDNoise3.kr(cutOffMoveFreq), -1, 1, resLo, resHi),

        rq,

        amp

    ).softclip;

    Out.ar(out, (1 - mix) * inSig + (sig * mix));

}).add;



// prepare EventShortcuts for additional keys


EventShortcuts.addOnBase(\default, \fxs, (

dec: \decayTime,

cd: \cleanupDelay,

cf: \cutOffMoveFreq,

fxo: \fxOrder,

dta: \echoDelta

), true);


EventShortcuts.makeCurrent(\fxs);


EventShortcuts.on;

)



(

// source and fxs passed as PLbindefs


k = [\r, \echo, \wah];


PLbindefPar(\r, 12,

\i, [\noise_grain, \saw_grain],

\d, 0.25,

\m, PLseq([60, 60, 60, 62]),

\fxo, PLseq([0, 0, 1, 2]),

// echo introduces delay, so do delay if no echo

\lag, Pfunc { |e| e.fxo.asArray.includes(1).if { 0 }{ 0.2 } },

\a, 0.03,

\att, 0.01,

\rel, 0.1,


\cd, Pkey(\att)+ Pkey(\rel) + 0.001

);


PLbindef(\echo,

\fx, \echo,

\dta, 0.06,

\a, 1,

\dec, Pwhite(0.3, 1.8),

\cd, Pkey(\dec)

);


PLbindef(\wah,

\fx, \wah,

\cf, Pwhite(1, 3),

\a, 1,

\cd, 0.01

);


// all are Pdefs too, globally stored, so we can collect like this


p = PbindFx(*k.collect(Pdef(_))); // PbindFx(*(k.collect { |x| Pdef(x) }));


q = p.play;

)



// differentiate rhythm


~r.d = (1..12) / 8



// manipulate midinotes


~r.m = ~r.m + PLseq([0, 12, -12])


~r.m = ~r.m + PLrand([0, [0, -4]])


~r.m = ~r.m + PLrand([0, [0, -7]])




// echo param and fx order sequence


~echo.dta = Pwhite(0.03, 0.08)


~r.fxo = PLseq([0, 0, 1, 2, 1, [1, 2]])



~r.m = (48, 50..70)


// produce overtone series with echodelta


~echo.dta = 1 / 24.midicps / PLseq((1..16))



// this also works in chords ("fx expansion")

// source is processed twice per event


~echo.dta = 1 / [24, 26.7].midicps / PLseq((1..32).mirror)




// cleanup


q.stop


k.do { |i| Pdef(i).remove }