1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
title:: jitlib_efficiency
summary:: Efficient coding with NodeProxy
categories:: Libraries>JITLib>Tutorials
related:: Overviews/JITLib
link::Classes/NodeProxy:: (and, in disguise link::Classes/ProxySpace::) represent "pronouns", placeholders for all kinds of sound producing objects that are able to write to a specific bus on the server.
To prepare such an object for playing, different objects require different things, some very little, some more. As working with the placeolders does not show directly which actions are very efficient and whic ones are not, it is shown here more in detail.
This is also important if you want to automate certain processes (e.g. for control by an external interface or a task) - some things (a) are not meant to be used in certain ways and better solutions should be used instead then, others are much more efficient (b, c).
code::
a = NodeProxy.audio;
ProxySpace.push;
a.source = ... is equivalent to ~a = ...
a.add(...) a.put(0,...) a[0] = ... ~a[0] = ... are equivalent in cpu load.
::
section::a) rebuild and send: manual rate
the following code requires a rebuild and send of a link::Classes/SynthDef:: and is thus most cpu-expensive. though fine for slower use (esp.hand-use) for automatisation it is better to build a synthdef and assign it.
code::
~a = { someUGenFunction };
~a = Patch(instrname, args);
~a = SynthDef(\name, { someUGenFunction });
// the same applies to rebuilding the graphs:
~a.rebuild
// this rebuild is also used when setting one of the following properties:
server, bus, setRates
::
section::b) starting synths and tasks
the following code sends commands to the server to start synths, which is load mainly on the server and depends on the characteristics of the synthdef:
code::
~a = \synthDefName; // the synthdef is already on the server
~a = Pbind(\instrument, name, \freq, ...);
~a = Routine({ loop({ s.sendMsg("/s_new", name, ...)}) });
~a.refresh; ~a.wakeUp; // waking up a stopped proxy does not require a resend
::
these resend the synth with new properies
code::
~a.send(...) // directly sends a message. the mapping bundle of the proxy is cached
~a.sendAll(...)
// for the following the bundle is recalculated if a new key is assigned.
// if you use the same key with a different value, the bundle is modified
~a.xset(...) ~a.xmap(...)
~a.nodeMap_(a map)
~a.fadeToMap(a map)
// synthdefs for these things are on the server already.
~a.gate, ~a.env, ~a.line, ~a.xline
// some more calculations have to be made on client side, so if automated, it is better to use
// the above or a lag in the synth.
~a.lineAt(key), ~a.xlineAt(key)
::
section::c) sending messages to running synths
for these the least calculation has to be done
code::
~a.set(\freq, 400, \dt, 0.2); ~a.unset(\freq); // if running the bundle will be recalculated
~a.map(\freq, ~lfo); ~a.unmap(\freq);
~a.fadeTime = 2;
~a.gateAt(key)
// for avoiding bundle recalculation you can directly talk to the group.
// this setting will not be kept when you exchange the synth
~a.group.set(\freq, 500);
::
section::switching audio
(this can now be done with map!)
todo: rewrite this part.
control rate sources can be easily and efficiently switched using strong::map:: or strong::xmap::. here is an example of how already running audio rate inputs can be switched. it is about as efficient as (b) - first example (setting a defname). it works only for 1 or 2 channels right now.
code::
(
s.boot;
p = ProxySpace.push(s);
)
~out.play;
~s1 = { Blip.ar(Rand(32,15), 100, 0.5) };
~s2 = { SinOsc.ar(740, 0, 0.1) };
~s3 = { Pulse.ar(140, 0.2, 0.1) };
~out = { Pan2.ar(~mix.ar(1), MouseX.kr(-1,1)) };
~mix.read(~s1);
~mix.read(~s2);
~mix.read(~s3);
//resetting the source stops reading
~mix = \default;
//now you can also crossfade audio efficiently:
~mix.fadeTime = 1.5;
~mix.read(~s1);
~mix.read(~s2);
~mix.read(~s3);
// automation:
(
t = Task({
var dt;
loop({
dt = rrand(0.01, 0.3);
~mix.fadeTime = dt;
~mix.read([~s1, ~s2, ~s3].choose);
dt.wait;
});
});
)
t.play(SystemClock);
// change the sources meanwhile:
~s1 = { Blip.ar(105, 100, 0.2) };
~s2 = { SinOsc.ar(350, 0, 0.1) };
~s3 = { Pulse.ar(60, 0.2, 0.1) };
~freq = { MouseX.kr(200, 600, 2) };
~s1 = { Blip.ar(~freq.kr * 0.3, 10, 0.2) };
~s2 = { SinOsc.ar(~freq.kr, 0, 0.1) };
~s3 = { Pulse.ar(~freq.kr * 0.2, 0.2, 0.1) };
t.stop;
// note that when restarting ~out, the inputs have to be woken up.
// to avoid this, you can add the inputs to the mix nodeMap parents:
~mix.nodeMap.parents.putAll( (s1: ~s1, s2: ~s2, s3: ~s3) );
// also the task can be added to the proxy:
(
~mix.task = Routine({
loop({
~mix.fadeTime = rrand(0.01, 0.1);
~mix.read([~s1, ~s2, ~s3].choose);
[0.2, 0.4].choose.wait;
});
});
)
::
|