NodeProxy a reference on a server
superclass: BusPlug
Generally a proxy is a placeholder for something, which in this case
is something playing on a server that writes to a limited number of busses.
(this can be for example a synth or an event stream). The rate and number
of channels is determined either when the instance is created (.control/.audio)
or by lazy initialisation from the first source.[the_lazy_proxy]
These objects can be replaced, mapped and patched and used for calculation.
ProxySpace returns instances of NodeProxy. all the examples below apply to ProxySpace accordingly:
a = NodeProxy(s) is equivalent to ~a;
a.source = ... is equivalent to ~a = ...
a[3] = ... is equivalent to ~a[3] = ...
see also: the_lazy_proxy jitlib_efficiency, jitlib_fading, jitlib_asCompileString,
General overview: JITLib
Note that NodeProxy plays on a private bus.
if you want to hear the output, use a.play and a.stop.
free only the inner players: a.free
for free inner players and stop listen: a.end
entirely removing all inner settings: a.clear
instance creation
*new(server)
*audio(server, numChannels)
*control(server, numChannels)
reading from the bus
play(fromIndex, fromNumChannels, toIndex, toNumChannels, target, multi, volume, fadeTime)
plays from a bus index with a number of channels to another index with a number
of channels, within a target group, or a server.
multi: keep old links and add new one
volume: volume at which to monitor
fadeTime: fade in fade out time
playN(outs, amps, ins, vol, fadeTime, group, addAction)
outs array of destination channels
amps array of amplitudes for each channel
outs array of source channels
see playN
stop(fadeTime)
stop to play out public channels (private channels keep playing as others might listen still)
this stop the monitoring. to stop the objects playing, use free, release
fadeTime: decay time for this action
end(fadeTime)
releases the synths and stops playback
fadeTime: decay time for this action
ar(numChannels)
kr(numChannels)
return a link to my output, which is limited by [numChannels]
causes an uninitialized proxy to create a matching bus.
normally ar defaults to stereo, kr to mono. this can be set in the classvars:
defaultNumAudio, defaultNumControl
/*
supported inputs:
NodeProxy played by reading from
Function interpreted as UGenFunc
SimpleNumber used to write to bus continously
Bus reads from that bus
SynthDef plays a synth from the def
Symbol plays a synth from the def with this name
Pattern played as event pattern
Stream played as event stream
nil removes all objects
Pdef played like a stream
Task played, no output assigned
Tdef played like Task
AbstractPlayer started in a separate bus, mapped to this bus
Instr converted to player and started
Associations:
(\filter -> func) filter previous input (with post control)
(\filterIn -> func) filter previous input (with pre control)
(\set -> event pattern) set controls
(\control -> array or number) prepare an efficient way to set values by index
(\mix -> func) mix audio
*/
setting the source:
source_(anObject)
play a new synth through me, release old one.
anObject can be one of the supported inputs (see above)
[only if the used synthdef (applies also to patterns) has the right number of channels
and an out argument, this can be used to do filtering.
if you supply a gate, the nodeProxy will assume doneAction 2 and fade out].
prime(anObject)
set source without starting the synth. To start it, send can be used later.
running synths are released
add(anObject, channelOffset, extraArgs)
play a new synth, add it to the present ones
removeAt(index)
remove the synth at index i and its player definition
removeLast
remove the last synth and its player definition
put(index, anObject, channelOffset, extraArgs, now)
set the source by index.
index:
where the object should be placed in the internal order.
if -1, all objects are freed
anObject:
can be a Function, an Instr, any valid UGen input
a pattern can be used if it returns an EventStream.
channelOffset:
using a multichannel setup it can be useful to set this.
when the objects numChannels is smaller than the proxy
extraArgs: extra arguments that can be sent with the object directly (not cached)
now if set to false, only prepare the source and do not start the object (see prime)
put can be used as array indexing: a[0] = { SinOsc.ar }
one can put an object at any index, only the order of indices is relevant.
if the index equals an existing index, the object at this index is replaced.
using multiple index expands into multiple objects: a[0..3] = ... or a[[0, 4, 6]] = [ .., ..., ..]
pause
pause all objects and set proxy to paused
resume
if paused, start all objects
orderNodes(nodeProxy, nodeProxy...)
arrange the order of groups from this to the last.
this can be important when external input is filtered in order to minimize latency.
if a parentGroup was provided, the nodes must be in the same parentGroup.
group-like behaviour:
set(key, val, ...)
I behave like my nodeMap: see [NodeMap]
set, setn, unset, unmap
map(key(s), proxy, ... )
map the arguments in keys to the subsequent channels of a control proxy
(keys can be a symbol or a number)
if the proxy has multiple channels, subsequent channels of the control,
if present, are mapped (mapn)
note that you should not map to more channels than the control has.
setn(key, list, ...)
set ranges of controls
run(flag)
pause/unpause all synths in the group
xset(key, val, ...)
set with crossfade into new setting
xmap(keys, proxy)
map with crossfade into new setting
xsetn()
untested
lag(key, val, ...)
set the lag values of these args (identical to setRates)
to remove these settings, use: lag(\key1, nil, key2, nil, ...)
setRates(key, rate1, ...)
set the default rate (\tr, \ir, numerical) for synthDef arg
rate of nil removes setting
bus-like behaviour:
line(value, dur)
set my bus to the new value in dur time linearly
xline(value, dur)
set my bus to the new value in dur time exponentially
gate(value, dur)
gate my bus to the level value for dur time
// do not work properly yet !
lineAt(key, value, dur)
set the control value to the new value in dur time linearly
xlineAt(key, value, dur)
set control value to the new value in dur time exponentially
gateAt(key, value, dur)
gate my control to the level value for dur time.
if the control was not set before, stay at the new value
sending synths to server
(normally the source_() message does the sending already, but it can be used for spawning)
wakeUp
until the proxy is not used by any output ( either .play or .ar/.kr )
it is not running on the server. you can wake it up to force it playing.
normally this is not needed.
send(argList, index, freeLast)
send a new synth without releasing the old one.
the argument list is applied to the synth only.
freeLast: if to free the last synth at that index
if index is nil, sends all
sendAll(argList, freeLast)
send all synths without releasing the old one.
the argument list is applied to all synths.
freeLast: if to free present synths
release and cleaning up:
free(fadeTime)
release all my running synths and the group
fadeTime: decay time for this action
release(fadeTime)
release running synths
fadeTime: decay time for this action
clear(fadeTime)
reset everything to nil, neutralizes rate/numChannels
if a fadeTime is given, first fade out, then clear.
setting properties:
fadeTime_(time)
set the attack/release time
clock_(aClock)
use a tempo clock for scheduling beat accurate
misc:
record(path, headerFormat, sampleFormat)
record output to file (returns a [RecNodeProxy] that you can use for control)
returns a [RecNodeProxy]
*defaultNumAudio_(n)
set the default channel number for audio busses
*defaultNumControl_(n)
set the default channel number for control busses
for more examples see ProxySpace
///////////////////// using node proxy with ugen functions /////////////////////
s.boot;
a = NodeProxy.audio(s, 2);
a.play; // play to hardware output, return a group with synths
// setting the source
a.source = { SinOsc.ar([350, 351.3], 0, 0.2) };
// the proxy has two channels now:
a.numChannels.postln;
a.source = { SinOsc.ar([390, 286] * 1.2, 0, 0.2) };
// exeeding channels wrap:
a.source = { SinOsc.ar([390, 286, 400, 420, 300] * 1.2, 0, 0.2) };
// other inputs
a.source = { WhiteNoise.ar([0.01,0.01]) };
a.source = 0;
a.source = \default; // synthDef on server
a.source = SynthDef("w", { arg out=0; Out.ar(out,SinOsc.ar([Rand(430, 600), 600], 0, 0.2)) });
a.source = nil; // removes any object
// feedback
a.source = { SinOsc.ar(a.ar * 7000 * LFNoise1.kr(1, 0.3, 0.6) + 200, 0, 0.1) };
a.source = { SinOsc.ar(a.ar * 6000 * MouseX.kr(0, 2) + [100, 104], 0, 0.1) };
// fadeTime
a.fadeTime = 2.0;
a.source = { SinOsc.ar([390, 286] * ExpRand(1, 3), 0, 0.2) };
// adding nodes
a.add({ SinOsc.ar([50, 390]*1.25, 0, 0.1) });
a.add({ BrownNoise.ar([0.02,0.02]) });
// setting nodes at indices:
a[0] = { SinOsc.ar( 700 * LFNoise1.kr(1, 0.3, 0.6) + 200, 0, 0.1) };
a[1] = { LFPulse.kr(3, 0.3) * SinOsc.ar(500, 0, 0.1) };
a[2] = { LFPulse.kr(3.5, 0.3) * SinOsc.ar(600, 0, 0.1) };
a[3] = { SinOsc.ar([1,1.25] * 840, 0, 0.1) };
// filtering: the first argument is the previous bus content. more args can be used as usual.
a[3] = \filter -> { arg in; in * SinOsc.ar(Rand(100,1000)) };
a[2] = \filter -> { arg in; in * MouseY.kr(0,1) };
a[8] = \filter -> { arg in; in * MouseX.kr(0,1) };
a[4] = \filter -> { arg in; in * SinOsc.ar(ExpRand(1,5)).max(0) };
// setting controls
a.fadeTime = 2.0;
a.source = { arg f=400; SinOsc.ar(f * [1,1.2] * rrand(0.9, 1.1), 0, 0.1) };
a.set(\f, rrand(900, 300));
a.set(\f, rrand(1500, 700));
a.xset(\f, rrand(1500, 700)); // crossfaded setting
a.source = { arg f=400; RLPF.ar(Pulse.ar(f * [1,1.02] * 0.05, 0.5, 0.2), f * 0.58, 0.2) };
// control lags
a.lag(\f, 0.5); // the objects are built again internally and sent to the server.
a.set(\f, rrand(1500, 700));
a.lag(\f, nil);
a.set(\f, rrand(1500, 700));
a.fadeTime = 1.0;
// mapping controls to other node proxies
c = NodeProxy.control(s, 2);
c.source = { SinOsc.kr([10,20] * 0.1, 0, 150, 1300) };
a.map(\f, c);
a[0] = { arg f=400; RHPF.ar(Pulse.ar(f * [1,1.2] * 0.05, 0.5, 0.2), f * 0.58, 0.2) };
c.source = { SinOsc.kr([10,16] * 0.02, 0, 50, 700) };
c.source = { Line.kr(300, 1500, 10) + SinOsc.kr(20 * [1,2], 0, 100) };
a[1] = { arg f; LFPar.ar(f % MouseX.kr(1, 40, 1) * 4 + 360, 0, 0.2) };
// map multiple channels of one proxy to multiple controls of another
// recently changed behaviour!
a.source = { arg f=#[400, 400]; LPF.ar(Pulse.ar(f[0] * [0.4,1], 0.2, 0.2), f[1] * 3) };
a.map(\f, c); // multichannel proxy c is mapped to multichannel control of a
a.source = { arg f=#[400, 400]; LPF.ar(Pulse.ar(f, 0.2, 0.2), f[1]) };
a.source = { arg f=#[400, 400]; Formant.ar(140, f * 1.5, 100, 0.1) };
c.source = { SinOsc.kr([Line.kr(1, 30, 10), 1], 0, [100, 700], [300, 700]) };
c.source = 400;
c.fadeTime = 5.5;
c.source = { LFNoise0.kr([2.3, 1.0], [100, 700], [300, 1700]) };
c.source = { SinOsc.kr([2.3, 1.0], 0, [100, 700], [300, 1700]) };
c.source = 400;
// behave like a sc2 plug
c.gate(1400, 0.1);
c.gate(1000, 0.1);
c.line(1000, 1);
// direct access
a.lineAt(\f, 300, 2);
a.xlineAt(\f, 600, 0.3);
a.gateAt(\f, 1600, 0.3);
// changing nodeMaps
a.unmap(\f);
n = a.nodeMap.copy;
n.set(\f, 700);
a.fadeToMap(n);
n = a.nodeMap.copy;
n.set(\f, 400);
a.fadeTime = 1.0;
a.fadeToMap(n, [\f]); // linear interpolation to new map: experimental
a.map(\f, c); // restore mapping
// sending envelopes (up to 8 levels)
w = Env.new(Array.rand(3, 400, 1000),Array.rand(2, 0.3, 0.001), -4);
c.env(w);
c.env(w);
w = Env.new(Array.rand(8, 400, 1000),Array.rand(7, 0.03, 0.1));
c.env(w);
c.env(w);
// stop synthesis, then wake up proxies:
a.stop; // stop the monitor
a.play; // start the monitor
a.end; // release the synths and stop the monitor
c.free; // free the control proxy c
///////////////////// channel offset/object index /////////////////////
a = NodeProxy.audio(s,2);
a.play;
a[0] = { Ringz.ar(Impulse.ar(5, 0, 0.1), 1260) };
a.put(1, { Ringz.ar(Impulse.ar(5.3, 0, 0.1), 420) }, 1);
a.put(0, { Ringz.ar(Dust.ar([1,1]*15.3, 0.1), 720) }, 1);
a.put(1, { Ringz.ar(Impulse.ar(5.3, 0, 0.1), 420) }, 1);
a.end;
///////////////////// beat accurate playing /////////////////////
a = NodeProxy.audio(s,2);
a.play;
a.clock = TempoClock(2.0).permanent_(true); // round to every 2.0 seconds
a.source = { Ringz.ar(Impulse.ar(0.5, 0, 0.3), 3000, 0.01) };
a[1] = { Ringz.ar(Impulse.ar([0.5, 1], 0, 0.3), 1000, 0.01) };
a[2] = { Ringz.ar(Impulse.ar([3, 5]/2, 0, 0.3), 8000, 0.01) };
a[3] = { Ringz.ar(Impulse.ar([3, 5]*16, 0, 0.3), 5000, 0.01) * LFPulse.kr(0.5, 0, 0.05) };
a.removeLast;
a.removeAt(2);
a.clear;
///////////////////// using patterns - event streams /////////////////////
(
// must have 'out' or 'i_out' argument to work properly
SynthDef("who", { arg freq, gate=1, out=0, ffreq=800, amp=0.1;
var env;
env = Env.asr(0.01, amp, 0.5);
Out.ar(out, Pan2.ar(
Formant.ar(freq, ffreq, 300, EnvGen.kr(env, gate, doneAction:2)), Rand(-1.0, 1.0))
)
}).memStore;
)
(
s.boot;
a = NodeProxy.audio(s, 2);
a.fadeTime = 2;
b = NodeProxy.audio(s,2);
b.fadeTime = 3;
)
a.play; // monitor output
// play the pattern silently in b
b.source = Pbind(\instrument, \who, \freq, 500, \ffreq, 700, \legato, 0.02);
// play b out through a:
a.source = b;
// filter b with ring modulation:
a.source = { b.ar * SinOsc.ar(SinOsc.kr(0.2, 300, 330)) }; // filter the input of the pattern
a.source = { b.ar * LFCub.ar([2, 8], add: -0.5) }; // filter the input of the pattern
a.source = b;
// map b to another proxy
c = NodeProxy.control(s, 1).fadeTime_(1);
c.source = { SinOsc.kr(2, 0, 400, 700) };
// now one can simply embed a control node proxy into an event pattern.
// (this works not for \degree, \midinote, etc.)
// embedding in other patterns it will still return itself.
b.source = Pbind(\instrument, \who, \freq, 500, \ffreq, c, \legato, 0.02);
c.source = { SinOsc.kr(SinOsc.kr(0.2, 0, 10, 10), 0, 400, 700) };
c.source = { LFNoise1.kr(5, 1300, 1500) };
c.source = { MouseX.kr(100, 5500, 1) };
(
b.source = Pbind(
\instrument, \who,
\freq, Pseq([600, 350, 300],inf),
\legato, 0.1,
\ffreq, Pseq([c, 100, c, 100, 300, 600], inf), // use proxy in a pattern
\dur, Pseq([1, 0.5, 0.75, 0.25] * 0.4, inf),
\amp, Pseq([0.2, 0.2, 0.1, 0.1, 0.2], inf)
);
)
b[2] = Pbind(\instrument, \who, \freq, 620, \ffreq, Prand([500,c],inf), \legato, 0.1, \dur, 0.1);
b[3] = Pbind(\instrument, \who, \ffreq, 5000, \freq, Pseq([720, 800],inf), \legato, 0.1, \dur, 0.1, \amp, 0.01);
b[4] = Pbind(\instrument, \who, \freq, Pseq([700, 400],inf), \legato, 0.1, \ffreq, 200);
b[1] = { WhiteNoise.ar([0.01,0.01]) };
b[4] = { arg ffreq=800; Resonz.ar(WhiteNoise.ar([1,1]), ffreq, 0.05) };
b.map(\ffreq, c); // map the control to the proxy
b.removeLast;
b.removeLast;
a.source = { b.ar * WhiteNoise.ar(0.1, 1) };
a.source = { b.ar * WhiteNoise.ar(0.1, 1) + (b.ar * SinOsc.ar(SinOsc.kr(0.01, 0, 50, 330))) };
c.source = { XLine.kr(1900, 10, 10) };
a.clear(10); b.clear(10); c.clear(10); // fade out and clear all (free bus, group and synths)