class:: Ndef summary:: node proxy definition categories:: Libraries>JITLib>NodeProxy, Live Coding related:: Classes/ProxySpace, Classes/Tdef description:: Reference to a proxy, forms an alternative to link::Classes/ProxySpace::. All methods are inherited from link::Classes/NodeProxy::. code:: Ndef(key) //returns the instance Ndef(key, obj) //stores the object and returns the instance, like Tdef and Pdef. :: Graphical editor overviewing all current Ndefs: link::Classes/NdefMixer::. A general overview: link::Overviews/JITLib::. subsection::First Example code:: s.boot; Ndef(\a).play; // play to hardware output. Ndef(\a).fadeTime = 2; // fadeTime specifies crossfade // set the source Ndef(\a, { SinOsc.ar([350, 351.3], 0, 0.2) }); Ndef(\a, { Pulse.ar([350, 351.3] / 4, 0.4, 0.2) }); Ndef(\a, Pbind(\dur, 0.03, \freq, Pbrown(0, 1, 0.1, inf).linexp(0, 1, 200, 350))); Ndef(\a, { Ringz.ar(Ndef.ar(\b), [350, 351.3] * 2, 0.4) }); Ndef(\b, { Impulse.ar([5, 7]/2, [0, 0.5], 0.15) }); Ndef.clear(3); // clear all after 3 seconds :: ClassMethods:: private::initClass subsection::Creation method::new Return a new node proxy and store it in a global ProxySpace under the key. If there is already an Ndef there, replace its object with the new one. The object can be any supported class, see link::Classes/NodeProxy#Supported sources:: help. argument::key the name of the proxy (usually a symbol). If only the key is given and no object, it returns the proxy object: code:: Ndef(\x) // get the proxy :: If key is an association, it is interpreted as strong::key -> server name::. (order changed in SC3.3 !). If no name is given, it uses the default server that was default when Ndef was first called. (to change it, see link::#*defaultServer::). argument::object an object code:: Ndef(\x, { Dust.ar }); // returns the proxy and set the source object. :: method::ar equivalent to code::*new(key).ar(numChannels, offset):: (see link::Classes/BusPlug#ar::) method::kr equivalent to code::*new(key).kr(numChannels, offset):: (see link::Classes/BusPlug#kr::) method::clear clear all proxies method::defaultServer set the default server (default: code::Server.default::) method::all Return the dictionary of all servers, pointing to proxyspaces with Ndefs for each. code:: Ndef.all; :: method::dictFor Return the proxyspace for a given server. code:: Ndef.dictFor(s); :: subsection::Setting default parameters Behind every Ndef there is one single instance of link::Classes/ProxySpace:: per server used (usually just the one for the default server). This ProxySpace keeps default values for the proxies that can be set. This can be done by: code:: // set default quant Ndef(\x).proxyspace.quant = 1.0; :: The other values that can be set in such a way are: code::clock, fadeTime, quant, reshaping, awake::. Examples:: code:: s.boot; Ndef(\sound).play; Ndef(\sound).fadeTime = 1; Ndef(\sound, { SinOsc.ar([600, 635], 0, SinOsc.kr(2).max(0) * 0.2) }); Ndef(\sound, { SinOsc.ar([600, 635] * 3, 0, SinOsc.kr(2 * 3).max(0) * 0.2) }); Ndef(\sound, { SinOsc.ar([600, 635] * 2, 0, SinOsc.kr(2 * 3).max(0) * 0.2) }); Ndef(\sound, Pbind(\dur, 0.17, \freq, Pfunc({ rrand(300, 700) })) ); Ndef(\lfo, { LFNoise1.kr(3, 400, 800) }); Ndef(\sound).map(\freq, Ndef(\lfo)); Ndef(\sound, { arg freq; SinOsc.ar([600, 635] + freq, 0, SinOsc.kr(2 * 3).max(0) * 0.2) }); Ndef(\lfo, { LFNoise1.kr(300, 400, 800) }); Ndef.clear; //clear all Ndefs :: subsection::using Ndef inside other Ndefs code:: Ndef(\lfo2, { LFNoise1.kr(LFNoise1.kr(0.1).exprange(1, 300) ! 2, 400, 800) }); Ndef(\sound, { Blip.ar(Ndef.kr(\lfo2), 30) * 0.2 }).play; Ndef(\lfo2, { [MouseX.kr(10, 300, 1), MouseY.kr(10, 300, 1)] }); :: subsection::setting and mapping parameters code:: Ndef(\sound, { |freq = 56, numHarm = 10| Blip.ar(freq, numHarm, 30) * 0.2 }).play; Ndef(\sound).set(\freq, 15); Ndef(\sound).set(\freq, 15, \numHarm, 100); Ndef(\lfo, { LFNoise2.kr(2).exprange(10, 200) }); Ndef(\sound).map(\numHarm, Ndef(\lfo)); Ndef(\sound).set(\numHarm, nil); // unmap. Ndef(\sound).stop; :: subsection::Simple audio routing with the <<> operator code:: ( Ndef(\sound, { RHPF.ar( \in1.ar([0, 0]) * \in2.ar([0, 0]), \freq.kr(6000, 2), \rq.kr(0.2) ) * 7 }).play; Ndef(\sound).fadeTime = 0.2; // avoid harsh clicks ) Ndef(\a, { SinOsc.ar(MouseX.kr(300, 1000, 1) * [1, 1.2], \phase.ar([0, 0]) * 0.2) }); Ndef(\b, { LFDNoise3.ar(MouseY.kr(3, 1000, 1) * [1, 1.2]) }); Ndef(\c, { LFTri.ar(MouseY.kr(3, 10, 1) * [1, 1.2]).max(0) }); Ndef(\a).fadeTime = 0.2; // avoid harsh clicks again Ndef(\sound) <<>.in1 Ndef(\a); Ndef(\sound) <<>.in2 Ndef(\b); Ndef(\sound) <<>.in2 Ndef(\c); Ndef(\a) <<>.phase Ndef(\sound); Ndef(\a) <<>.phase nil; // unmap Ndef.clear(3); // fade out and clear all Ndefs :: subsection::Embedding multi-channel Patterns, playing Streams in parallel Controlling multi-channeled sequenced streams and having independent control over filtering and node ordering is a difficult topic in SuperCollider. However, using Ndefs (or their superclass link::Classes/NodeProxy:: or a link::Classes/ProxySpace::) may provide a convenient solution. code:: // a SynthDef, creating single-channel grain when instantiated ( SynthDef(\grain, { |out=0, freq=300, amp=0.3| OffsetOut.ar(out, Pulse.ar(freq) * EnvGen.kr(Env.perc, doneAction: Done.freeSelf) * amp) }).add; ) // number of channels ~numChans = 5; // values in a Pattern may be set in various ways // here we use control buses, except for \dur which // doesn't accept a control bus in parallel playing streams // therefore we use PatternProxies ~durs = ~numChans.collect({ |i| PatternProxy(0.5 + (i/10)) }); // other parameters could as well be controlled in PatternProxies, // yet, control buses are convenient either ~freqs = Bus.control(s, ~numChans); ~freqs.setn(Array.geom(~numChans, 300, 1.1)); ~amps = Bus.control(s, ~numChans); ~amps.setn(0.2!~numChans); // the Pattern: a Ppar holding one Pbind for each channel, // all wrapped in a Pdef ( Pdef(\ppar, Ppar({ |i| Pbind( \instrument, \grain, // we only set a single channel \dur, ~durs[i], \freq, ~freqs.subBus(i).asMap, \amp, ~amps.subBus(i).asMap, // the Pattern will play to a yet unknown private bus // we only want to make sure the offset is right \channelOffset, i, ) }!~numChans) ) ) // initialize an Ndef that will hold the Pdef as its source // make sure the Ndef gets initialized to the right number of channels by calling 'mold' Ndef(\ppar).mold(~numChans, \audio, \elastic); Ndef(\ppar)[0] = Pdef(\ppar); // mix the 5 channel audio coming from Ndef(\ppar) down to stereo // Splay will spread the channels over the stereo panorama // possibly use headphones to clearly identify the effect Ndef(\stereo, { Splay.ar(\in.ar(0!~numChans)) }); // concatenate the Ndefs, so Ndef(\ppar)'s out will feed into Ndef(\stereo)'s in Ndef(\stereo) <<> Ndef(\ppar); Ndef(\stereo).play; // change durations ~durs.do({ |pp, i| pp.source = Pseq(Array.fib(5, i/10 + 0.1, i+1/5), inf) }); ~durs.do({ |pp, i| pp.source = 0.5 + (i/10) }); ~durs.do({ |pp| pp.source.postcs }); // frequencies ~freqs.setn(Array.geom(~numChans, 250, 1.6)); ~freqs.setn(Array.geom(~numChans, 300, 1.1)); // add a filter Ndef ( Ndef(\filter, { HPF.ar( \in.ar(0!~numChans), SinOsc.ar({|i| 2 + i}!~numChans) + 1 * \multFreq.kr(Array.geom(~numChans, 400, 2)) ) }).mold(~numChans, \audio, \elastic); ) // set a fadeTime for smooth transitions and add the filter to the chain #[ppar, stereo, filter].do({ |k| Ndef(k).fadeTime_(3) }); Ndef(\stereo) <<> Ndef(\filter) <<> Ndef(\ppar); // set filter param, considering fadeTime Ndef(\filter).xset(\multFreq, Array.rand(~numChans, 20, 10000)); Ndef.clear; Pdef.clear; :: subsection::Making Copies method::copy Because an Ndef is a unique instance for a given key, it can be copied only by supplying a new key. See also: link::Classes/NodeProxy#-copy::. code:: Ndef(\x, { SinOsc.ar(Rand(500, 900)) * 0.1 }).play; Ndef(\x).copy(\y); Ndef(\y).play; :: argument::newKey A valid new key, usually a link::Classes/Symbol:: subsection::Recursion Ndefs can be used recursively. A structure like the following works: code:: Ndef(\sound, { SinOsc.ar([600, 635], Ndef.ar(\sound), LFNoise1.kr(2).max(0) * 0.2) }); Ndef(\sound).play; Ndef.clear; :: This is because there is a feedback delay (the server's strong::block size::), usually 64 samples, so that calculation can reiterate over its own outputs. For single sample feedback, see: code:: (Platform.resourceDir +/+ "examples/demonstrations/single_sample_feedback.scd").openDocument; :: subsection::Using different servers code:: // create a new server a = Server(\foo, NetAddr("127.0.0.1", 57123)).boot.makeWindow; Ndef(\sound, { SinOsc.ar([600, 635]) * SinOsc.kr(2).max(0) * 0.2 }).play; // play on default Ndef(\sound -> \foo, { SinOsc.ar([700, 745]) * SinOsc.kr(2).max(0) * 0.2 }).play;// play on foo // clear definitions Ndef(\sound -> \foo).clear(3); Ndef(\sound).clear(3); a.dump; // display settings of new server a.quit; // terminate new server :: subsection::GUI code:: // create a window for a given Ndef Ndef(\sound).edit ( Ndef(\sound, { |freq = 440, rate = 2| SinOsc.ar(freq * [1, 1.625]) * SinOsc.kr(rate).max(0) * 0.2 }).play; ) // set lags for controls: Ndef(\sound).lag(\freq, 0.2, \rate, 0.5); Ndef(\sound).clear(1); // create a mixer for all Ndefs: NdefMixer(s); :: subsection::Using Associations For a complete list, see link::Classes/NodeProxy::, and link::Reference/NodeProxy_roles:: code:: // setsrc ( Ndef(\x, \setsrc -> Pbind(\source, Pseq([ { LFTri.ar(280 * Line.kr(1.1, 0.4, rrand(2, 3)) + [0,1]) * 0.1 }, { Pulse.ar(40 + [0,1]) * 0.1 }, { LFTri.ar(LFTri.kr(1).round(1.0.rand) + 1 * 180 + [0,1], 0.04) * 0.3 }, ], inf), \dur, Prand([3, 2, 4], inf) ) ).play; ) ::