HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / JITLib / ProxySpace / wrapForNodeProxy.sc
bloba08e8c4fdfd6e1f130ff0966a83ffe860a20f23a
1 //these extensions provide means to wrap Objects so that they
2 //make sense within the server bus system according to a node proxy.
4 ////////////////////// graphs, functions, numericals and arrays //////////////////
6 +Object {
8         //objects can define their own wrapper classes dependant on
9         //how they play, stop, build, etc. see SynthDefControl for example
10         //the original (wrapped) object can be reached by the .source message
11         //for objects that only create ugen graphs, define prepareForProxySynthDef(proxy)
13         proxyControlClass {
14                 ^SynthDefControl
15         }
17         makeProxyControl { arg channelOffset=0;
18                 ^this.proxyControlClass.new(this, channelOffset);
19         }
22         //any preparations that have to be done to prepare the object
23         //implement 'prepareForProxySynthDef' to return a ugen func
26         //this method is called from within the Control
27         buildForProxy { arg proxy, channelOffset=0, index;
28                 var argNames;
29                 argNames = this.argNames;
30                 ^ProxySynthDef(
31                         SystemSynthDefs.tempNamePrefix ++ proxy.generateUniqueName ++ index,
32                         this.prepareForProxySynthDef(proxy),
33                         proxy.nodeMap.ratesFor(argNames),
34                         nil,
35                         true,
36                         channelOffset,
37                         proxy.numChannels,
38                         proxy.rate
39                 );
40         }
41         prepareForProxySynthDef { ^this.subclassResponsibility(thisMethod) }
43         defaultArgs { ^nil }
44         argNames { ^nil }
46         //support for unop / binop proxy
47         isNeutral { ^true }
48         initBus { ^true }
49         wakeUp {}
54 +Function {
55         prepareForProxySynthDef { ^this }
56         argNames { ^def.argNames }
57         defaultArgs { ^def.prototypeFrame }
60 +AbstractFunction {
61         prepareForProxySynthDef { ^{ this.value } }
64 +SimpleNumber {
66         prepareForProxySynthDef { arg proxy;
67                 proxy.initBus(\control, 1);
68                 ^{ÊDC.multiNewList([proxy.rate] ++ this) };
69         }
72 +Synth { // better information about common error
73         prepareForProxySynthDef { arg proxy;
74                 Error(
75                         "A synth is no valid source for a proxy.\n"
76                         "For instance, ~out = { ... }.play would cause this and should be:\n"
77                         "~out = { ... }; ~out.play; or (~out = { ... }).play;"
78                 ).throw;
79         }
82 +RawArray {
83         prepareForProxySynthDef { arg proxy;
84                 proxy.initBus(\control, this.size);
85                 ^{ÊDC.multiNewList([proxy.rate] ++ this) };
86         }
89 +SequenceableCollection {
90         prepareForProxySynthDef { arg proxy;
91                 proxy.initBus(\control, this.size);
92                 ^{ this.collect({ |el| el.prepareForProxySynthDef(proxy).value }) }
93                 // could use SynthDef.wrap, but needs type check for function.
94         }
97 +BusPlug {
98         prepareForProxySynthDef { arg proxy;
99                 proxy.initBus(this.rate, this.numChannels);
100                 ^{ this.value(proxy) }
101         }
105 +AbstractOpPlug {
106         prepareForProxySynthDef { arg proxy;
107                 proxy.initBus(this.rate, this.numChannels);
108                 ^{ this.value(proxy) }
109         }
112 //needs a visit: lazy init + channelOffset
114 +Bus {
115         prepareForProxySynthDef { arg proxy;
116                 ^BusPlug.for(this).prepareForProxySynthDef(proxy);
117         }
123 ///////////////////////////// SynthDefs and alike ////////////////////////////////////
126 +SynthDef {
127         buildForProxy {}
128         numChannels { ^nil } //don't know
129         rate { ^nil }
134 +Symbol {
135         buildForProxy {}
136         proxyControlClass {
137                 ^SynthControl
138         }
141 ///////////////////////////// Pattern - Streams ///////////////////////////////////
144 +Stream {
145         proxyControlClass { ^StreamControl }
146         buildForProxy { ^PauseStream.new(this) }
149 +PauseStream {
150         buildForProxy { ^this }
151         proxyControlClass { ^StreamControl }
154 +PatternProxy {
155         buildForProxy { "a numerical pattern does not make sense here.".error; ^nil }
158 +TaskProxy {
159         proxyControlClass { ^StreamControl }
161         buildForProxy {  arg proxy, channelOffset=0;
162                 ^PauseStream(this.endless.asStream
163                         <> (
164                                 nodeProxy: proxy,
165                                 channelOffset: channelOffset,
166                                 server: { proxy.server },
167                                 out: { proxy.index },
168                                 group: { proxy.group }
169                         )
170                 )
171         }
174 +EventPatternProxy {
175         proxyControlClass { ^PatternControl }
177         buildForProxy {  arg proxy, channelOffset=0;
178                 ^this.endless.buildForProxy(proxy, channelOffset)
179         }
183 +Pattern {
184         proxyControlClass { ^PatternControl }
186         buildForProxy { arg proxy, channelOffset=0;
187                 var player = this.asEventStreamPlayer;
188                 var event = player.event.buildForProxy(proxy, channelOffset);
189                 ^event !? { player };
190         }
193 + Event {
194         proxyControlClass { ^StreamControl }
195         buildForProxy { arg proxy, channelOffset=0;
196                 var ok, index, server, numChannels, rate, finish;
197                 ok = if(proxy.isNeutral) {
198                         rate = this.at(\rate) ? 'audio';
199                         numChannels = this.at(\numChannels) ? NodeProxy.defaultNumAudio;
200                         proxy.initBus(rate, numChannels);
201                 } {
202                         rate = proxy.rate; // if proxy is initialized, it is user's responsibility
203                         numChannels = proxy.numChannels;
204                         true
205                 };
206                 ^if(ok) {
207                                 index = proxy.index;
208                                 server = proxy.server;
209                                 this.use({
210                                         ~channelOffset = channelOffset; // default value
211                                         ~out = { ~channelOffset % numChannels + index };
212                                         ~server = server; // not safe for server changes yet
213                                         finish = ~finish;
214                                         ~group = { proxy.group.asNodeID };
215                                         ~finish = {
216                                                 finish.value;
217                                                 proxy.nodeMap.addToEvent(currentEnvironment);
218                                                 ~group = ~group.value;
219                                                 ~out = ~out.value;
220                                         }
221                                 });
222                                 this
223                 } { nil }
224         }
229 /////////// pluggable associations //////////////
232 +Association {
233         buildForProxy { arg proxy, channelOffset=0, index;
234                 ^AbstractPlayControl.buildMethods[key].value(value, proxy, channelOffset, index)
235         }
236         proxyControlClass {
237                 ^AbstractPlayControl.proxyControlClasses[key] ? SynthDefControl
238         }
241 +AbstractPlayControl {
242         makeProxyControl { ^this.deepCopy } //already wrapped, but needs to be copied
244         /* these adverbial extendible interfaces are for supporting different role schemes.
245         it is called by Association, so ~out = \filter -> ... will call this. The first arg passed is   the value of the association */
247         *initClass {
248                 proxyControlClasses = (
249                         filter: SynthDefControl,
250                         xset: StreamControl,
251                         set: StreamControl,
252                         stream: PatternControl,
253                         setbus: StreamControl,
254                         setsrc: StreamControl
255                 );
257                 buildMethods = (
259                 filter: #{ arg func, proxy, channelOffset=0, index;
260                         var ok, ugen;
261                         if(proxy.isNeutral) {
262                                 ugen = func.value(Silent.ar);
263                                 ok = proxy.initBus(ugen.rate, ugen.numChannels);
264                                 if(ok.not) { Error("NodeProxy input: wrong rate/numChannels").throw }
265                         };
267                         { arg out;
268                                 var e;
269                                 e = EnvGate.new * Control.names(["wet"++(index ? 0)]).kr(1.0);
270                                 if(proxy.rate === 'audio') {
271                                         XOut.ar(out, e, SynthDef.wrap(func, nil, [In.ar(out, proxy.numChannels)]))
272                                 } {
273                                         XOut.kr(out, e, SynthDef.wrap(func, nil, [In.kr(out, proxy.numChannels)]))                              };
274                         }.buildForProxy( proxy, channelOffset, index )
276                 },
277                 set: #{ arg pattern, proxy, channelOffset=0, index;
278                         var args;
279                         args = proxy.controlNames.collect(_.name);
280                         Pbindf(
281                                 pattern,
282                                 \type, \set,
283                                 \id, Pfunc { proxy.group.nodeID },
284                                 \args, args
285                         ).buildForProxy( proxy, channelOffset, index )
286                 },
287                 xset: #{ arg pattern, proxy, channelOffset=0, index;
288                         Pbindf(
289                                 pattern,
290                                 \play, { proxy.xset(*proxy.controlNames.collect(_.name).envirPairs) }
291                         ).buildForProxy( proxy, channelOffset, index )
292                 },
293                 setbus: #{ arg pattern, proxy, channelOffset=0, index;
294                         var ok = proxy.initBus(\control);
295                         if(ok.not) { Error("NodeProxy input: wrong rate").throw };
296                         Pbindf(
297                                 pattern,
298                                 \type, \bus,
299                                 \id, Pfunc { proxy.group.nodeID },
300                                 \array, Pkey(\value).collect { |x| x.keep(proxy.numChannels) },
301                                 \out, Pfunc { proxy.index }
302                         ).buildForProxy( proxy, channelOffset, index )
303                 },
304                 setsrc: #{ arg pattern, proxy, channelOffset=0, index=0;
305                         pattern.collect { |event|
306                                 event[\type] = \rest;
307                                 proxy.put(index + 1, event[\source]);
308                                 event
309                         }.buildForProxy( proxy, channelOffset, index );
310                 },
311                 control: #{ arg values, proxy, channelOffset=0, index;
312                         { Control.kr(values) }.buildForProxy( proxy, channelOffset, index );
313                 },
314                 filterIn: #{ arg func, proxy, channelOffset=0, index;
315                         var ok, ugen;
316                         if(proxy.isNeutral) {
317                                 ugen = func.value(Silent.ar);
318                                 ok = proxy.initBus(ugen.rate, ugen.numChannels);
319                                 if(ok.not) { Error("NodeProxy input: wrong rate/numChannels").throw }
320                         };
322                         { arg out;
323                                 var in;
324                                 var egate = EnvGate.new;
325                                 var wetamp = Control.names(["wet"++(index ? 0)]).kr(1.0);
326                                 var dryamp = 1 - wetamp;
327                                 if(proxy.rate === 'audio') {
328                                         in = In.ar(out, proxy.numChannels);
329                                         XOut.ar(out, egate, SynthDef.wrap(func, nil,
330                                                 [in * wetamp]) + (dryamp * in))
331                                 } {
332                                         in = In.kr(out, proxy.numChannels);
333                                         XOut.kr(out, egate, SynthDef.wrap(func, nil,
334                                                 [in * wetamp]) + (dryamp * in))
335                                 };
336                         }.buildForProxy( proxy, channelOffset, index )
337                 },
339                 mix: #{ arg func, proxy, channelOffset=0, index;
341                         { var e = EnvGate.new * Control.names(["mix"++(index ? 0)]).kr(1.0);
342                                 e * SynthDef.wrap(func);
343                         }.buildForProxy( proxy, channelOffset, index )
344                 };
346                 )
347         }