proxy space - basic concepts  2

external structure of the node proxy, referencing in proxyspace and environments.

previous: jitlib_basic_concepts_01 next: jitlib_basic_concepts_03 

This document covers:

a) normal environment lookup

b) proxyspace as an environment

c) using the proxyspace to change processes on the fly

d) when are the node proxies initialized?

e) moving out of the proxy space

f ) using ProxySpace together with other Environments

a) normal environment lookup

currentEnvironment.postln; // anEnvironment (if not, you haven't left it from last helppage..)

~a; // access the environment: there is nothing stored: nil

~a = 9; // store something

~a; // now 9 is stored

~a + 100; // calculate with it

currentEnvironment.postln; // the value is stored in the environment

~b + ~a; // cause an error: ~b is nil.

~b = -90; // set ~b

~b + ~a; // now this works.

// note that you can always access environments (or ProxySpaces) from outside as well:

x = currentEnvironment;

x[\a] + x[\b] // equivalent to ~b + ~a

// or, if "know" is true, you can access named things with message-like syntax:

x.know = true;

x.a + x.b;

further readings: Environment

b) a proxyspace as an environment

one can replace the current environment with a special type of environment, a ProxySpace.

this environment represents processes that play audio on a server.

p =; // create a new environment, store it in variable p for now.

p.push; // push it, so i becomes the current environment.


currentEnvironment === p; // and they are identical.

~x; // accessing creates a NodeProxy (uninitialized) automatically.

~x + ~y; // this works immediately, because the lookup does not return nil, 

    // but a placeholder (proxy) instead

p.postln; // now there are two empty placeholders in the environment.

c) using the proxyspace to change processes on the fly

//  boot the server


// as soon as a sound function (or any compatible input) is assigned to a proxy

// this sound plays on its own private bus (so it is not audible yet.)


~x = { * 20, [850, 950], 0.2)



// the proxy has been initialized by its first assignment.

// it plays at audio rate (because we have assigned an audio rate ugen function)

// and it has two channels (because the function has stereo output)

~x.index; // a nodeproxy owns a private bus, so its signal can be used in diverse ways. 

// what is the proxy bus's index? this posts the index to the postwindow 

// before it was .ir(nil), now it is initialized to .ar(2)

~x.bus // what is the proxy's bus?; // now listen to it. a monitor is created (see Monitor) that plays the signal

// onto a public bus - by default, this is bus 0, the first audio output bus.

// This monitoring function is independent of the proxy itself.

// for further info see: jitlib_basic_concepts_03 (part c)

// the sound function can be changed at any time:


~x = {[5, 7]) * 5, [1450, 1234], 0.2)



// You can tune a sound function to your liking very easily

// by replacing it with little (or big) variations:

// filter freqs higher:

~x = {[5, 7]) * 5, [1800, 2000], 0.2) }

// same pulse ratio (5/8), different pulse tempo:

~x = {[5, 8] * 3.2) * 5, [1800, 2000], 0.2) }

// different filter:

~x = {[5, 8] * 3.2), [1800, 2000], 0.05) }

// and if you set the proxy's fadeTime, you can create little 

// textures by hand: 

~x.fadeTime = 3; 

// different filter freqs every time:

~x = {[5, 8] * rrand(0.5, 1.5)) * 0.5, ({ exprand(200, 4000) } ! 2), 0.05) }

// here is another proxy:

~y = {, 0) };

~y.bus; // it has two channels, just as the ~x., but it plays on another (private) bus.

// note that ~y is not audible directly,

// but it can be used like a UGen in any other proxy:


~x = { * 8,  [1450, 1234], 0.2)



// when the proxy changes, the result changes dynamically:

~y = {, 18, 1)) * [1, 1] };

~y = {, 0.2) * [1, 1]) };

~y = {[, 18, 1),, 18, 1)]) };

// stop listening. the proxies run in the background.


~y.bus; // ~y is playing on a different bus ...

~x.bus; // than ~x.

// we can also listen to ~y directly:;

// to remove a proxy source, nil can be used:

~y = nil;

// stop listening


further readings: proxyspace_examples Bus AbstractFunction UGens

d) when are the node proxies initialized?

bus initialization of a node proxy happens as soon as it is used for the first time.

later inputs are adjusted to this bus, as far as it is possible.

~z2 = {[1, 2, 3, 4]) }; // a four channel control rate proxy


~z100 = 0.5; // a constant value creates a single channel control rate proxy.

~z100.bus.postln; // the first access (with a numChannels argument) allocates the bus

~z34.bus.postln; // a 3 channel audio proxy

// these initializations can be removed by using clear:



This initialisation happens whenever the proxy is first used. Later, the proxy can

be accessed with other rate/numChannels combinations as needed (rates are converted,

numChannels are extended by wrapping, sources with too many channels are wrapped).

Note that this might cause ambiguous initialisation in which case the proxy should

be always initialized first. A typical problem is demonstrated here:, 2); // initialize 2 audio channels (default). 0 is the output bus number.

// if the proxy is not inititialized, play defaults to 2 channels.

// here it is explicitly given only to make it more clear.

~u = { }; // use only one

~u.numChannels; // 2 channels


if evaluated the other way round, only one channel is used:

~u = { }; // initialize 1 audio channel, 2); // play 2 channels: the 1 channel is expanded into 2.

// numChannels of .play defaults to the proxy's numChannels.

// here it is explicitly given, so to expand the channels

~u.numChannels; // 1 channel


Thus it can be useful to explicitly initialize proxies that use variable type inputs:;; // explicit initialisation

p.postln; // post the whole proxy space

e) moving out of the proxy space:

// play the audio:;

~x = { };

// p is the proxy space:


// to end all processes in p, use end:

p.end(2) // 2 seconds fade out.

// to remove all bus objects and free them from the bus allocato, use clear:



// restore original environment:



~a + ~b; // the old values are still here.

p === currentEnvironment; // this is not the case anymore.

// remove the content, so the garbage collector can release their memory.


// note that if you use this kind of accessing scheme, the objects are not garbage collected

// until the keys are set to nil. This is a common mistake when using normal environments.

// clear all in the normal environment:


f) using ProxySpace together with other Environments

using proxy space as an access scheme for node proxies can get in the way of the

normal use of environments as pseudo variables. Here are some ways to cope with this.

////////////// EnvirDocument is currently unavailable ////////////

//// if you want to keep using the current environment as usual, you can restrict the

//// scope of proxyspace to one document (note: this is mac-only currently)


//EnvirDocument(p, "proxyspace"); // to test this, check for currentEnvironment here 

// // and in the envir document.

// you can also access the proxy space and the proxies in it indirectly:


p[\x] = {, 0, 0.1) };

// or: when the proxyspace is pushed, you can use a normal environment indirectly:


d = ();

d[\buffer1] = Buffer.alloc(s, 1024);

d.use { ~buffer1.postln; ~zz = 81; }; // for more than one access to the environment, use .use

// to access the inner environment of proxy space directly, 

// e.g. to check whether a proxy exists, one can use .envir:


p.envir[\x].postln; // a proxy with this name exists 

p.envir[\nono].postln; // there is no proxy with this name. 

// p[\nono].postln; // this access would have created a new proxy called \nono.

previous: jitlib_basic_concepts_01 next: jitlib_basic_concepts_03