HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / JITLib / ProxySpace / ProxyInterfaces.sc
blob89d047d7d798b57861ec7ecb4b849de1f28c4d99
1 // lightweight objects that insulate different ways of playing/stopping.
2 // the bundle that is passed in is a MixedBundle
5 AbstractPlayControl {
7         var <source, <>channelOffset;
8         var <paused=false;
10         classvar <>buildMethods, <>proxyControlClasses; // see wrapForNodeProxy for methods
12         *new { | source, channelOffset = 0 |
13                 ^super.newCopyArgs(source, channelOffset);
14         }
16         build { ^true }
17         pause { this.stop }
18         resume { this.start }
19         nodeID { ^nil }
21         readyForPlay { ^true }
22         distributable { ^false } // shared proxy support
24         loadToBundle {}
25         spawnToBundle {} // only active in synthcontrols
28         playToBundle { | bundle, args |
29                 bundle.addOnSendMessage(this, \play); //no latency (latency is in stream already)
30                 ^nil //return a nil object instead of a synth
31         }
33         stopToBundle { | bundle |
34                 bundle.addOnSendMessage(this, \stop);
35         }
37         freeToBundle {}
39         set {}
40         controlNames { ^nil }
42         play { this.subclassResponsibility(thisMethod) }
43         stop { this.subclassResponsibility(thisMethod) }
46         wakeUpParentsToBundle {}
47         addParent { "wrong object in NodeProxy.buildControl".error } // for now.
48         parents { ^nil }
49         store {}
55 // The stream is a pause stream. This is meant for running in the proxy to control inner properties so it is stopped when removed.
58 StreamControl : AbstractPlayControl {
60         var stream, clock;
62         playToBundle { | bundle |
63                 // no latency (latency is in stream already)
64                 if(paused.not) { bundle.addOnSendMessage(this, \play) }
65                 ^nil // return a nil object instead of a synth
66         }
68         build { | proxy, orderIndex = 0 |
69                 clock = proxy.clock;
70                 paused = proxy.paused;
71                 stream = source.buildForProxy(proxy, channelOffset, orderIndex);
72                 ^true;
73         }
75         pause { stream.pause; paused=true }
76         resume { | clock, quant = 1.0 |
77                 stream.resume(clock, quant);
78                 paused = false;
79         }
81         readyForPlay { ^stream.notNil }
83         play {
84                 if(stream.isPlaying.not) {
85                         stream.play(clock, false, 0.0)
86                 }
87         }
88         stop { stream.stop }
93 PatternControl : StreamControl {
95         var fadeTime, <array;
97         playStream { | str |
98                 var dt;
99                 dt = fadeTime.value;
100                 array = array.add(str);
101                 if(dt <= 0.02) {
102                         str.play(clock, false, 0.0)
103                 } {
104                         str.xplay(dt / clock.beatDur, clock, false, 0.0)
105                 }
106         }
108         stopStreams { | streams, dt |
110                 dt = (dt ? fadeTime).value;
111                 if(dt <= 0.02) {
112                         streams.do { arg item; item.stop  }
113                 } {
114                         dt = dt / clock.beatDur;
115                         streams.do { arg item; item.xstop(dt) };
116                         // make sure it is stopped, in case next is never called
117                         SystemClock.sched(dt, { streams.do(_.stop) });
118                 }
119         }
121         build { | proxy, orderIndex = 0 |
122                 fadeTime = { proxy.fadeTime }; // needed for pattern xfade
123                 stream = source.buildForProxy(proxy, channelOffset, orderIndex);                clock = proxy.clock ? TempoClock.default;
124                 paused = proxy.paused;
125                 ^stream.notNil
126         }
128         stopToBundle { | bundle, dt |
129                 var streams;
130                 streams = array.copy;
131                 array = nil;
132                 bundle.onSend({ this.stopStreams(streams, dt) });
133         }
135         pause {
136                 array.do(_.pause);
137                 paused = true;
138         }
140         resume { | clock, quant = 1.0 |
141                 array.do(_.resume(clock, quant));
142                 paused = false;
143         }
145         playToBundle { | bundle, args, proxy, addAction = 1 |
146                 if(paused.not and: { stream.isPlaying.not })
147                 {
148                         // no latency (latency is in stream already)
149                         bundle.onSend {
150                                 var str, event;
151                                 str = source.buildForProxy(proxy, channelOffset);
152                                 if(args.notNil) {
153                                         event = str.event;
154                                         args.pairsDo { arg key, val; event[key] = val }
155                                 };
156                                 this.playStream(str)
157                         }
158                 }
159                 ^nil // return a nil object instead of a synth
160         }
162         stop {
163                 this.stopStreams(array.copy);
164                 array = nil;
165         }
171 SynthControl : AbstractPlayControl {
173         var <server, <>nodeID;
174         var <canReleaseSynth=false, <canFreeSynth=false;
176         loadToBundle {} // assumes that SynthDef is loaded in the server
178         asDefName { ^source }
180         distributable { ^canReleaseSynth } // n_free not implemented in shared node proxy
182         build { | proxy |       // assumes audio rate proxy if not initialized
183                 var rate, desc;
184                 desc = this.synthDesc;
185                 if(desc.notNil) {
186                         canFreeSynth = desc.canFreeSynth;
187                         canReleaseSynth = desc.hasGate && canFreeSynth;
188                 };
189                 if(proxy.isNeutral) { rate = \audio };
190                 ^proxy.initBus(rate, proxy.numChannels ? 2)
191         }
193         spawnToBundle { | bundle, extraArgs, target, addAction = 0 | // assumes self freeing
194                 var synthMsg, msg, targetID = target.asTarget.nodeID;
195                 if(extraArgs.notNil and: { extraArgs.any { |x| x.size > 1 } }) {
196                         synthMsg = [9, this.asDefName, -1, addAction, targetID];
197                         bundle.add(synthMsg);
198                         synthMsg = ["/n_setn", -1];
199                         extraArgs.pairsDo { |key, val|
200                                 synthMsg = synthMsg.add(key);
201                                 synthMsg = synthMsg.add(val.size.max(1));
202                                 synthMsg = synthMsg.addAll(val);
203                         };
204                         bundle.add(synthMsg);
206                 } {
207                         synthMsg = [9, this.asDefName, -1, addAction, targetID] ++ extraArgs;
208                         bundle.add(synthMsg);
209                 }
210         }
212         playToBundle { | bundle, extraArgs, target, addAction = 1 |
213                 var group, synthMsg;
214                 server = target.server;
215                 group = target.asTarget;
216                 nodeID = server.nextNodeID;
217                 if(extraArgs.notNil and: { extraArgs.any { |x| x.size > 1 } }) {
218                         synthMsg = [9, this.asDefName, nodeID, addAction, group.nodeID];
219                         bundle.add(synthMsg);
220                         synthMsg = ["/n_setn", -1];
221                         extraArgs.pairsDo { |key, val|
222                                 synthMsg = synthMsg.add(key);
223                                 synthMsg = synthMsg.add(val.size.max(1));
224                                 synthMsg = synthMsg.addAll(val);
225                         };
226                         bundle.add(synthMsg);
227                 } {
228                         bundle.add([9, this.asDefName, nodeID, addAction, group.nodeID]++extraArgs)
229                 };
230                 if(paused) { bundle.add(["/n_run", nodeID, 0]) };
231                 ^nodeID
232         }
234         stopToBundle { | bundle, fadeTime |
235                 if(nodeID.notNil) {
236                         if(canReleaseSynth) {
237                                         bundle.addAll([['/error', -1], [15, nodeID, \gate, 0.0, \fadeTime, fadeTime], ['/error', -2]]);
238                         } {
239                                         if(canFreeSynth.not) { //"/n_free"
240                                                 bundle.addAll([['/error', -1], [11, nodeID], ['/error', -2]]);
241                                         };
242                                         // otherwise it is self freeing by some inner mechanism.
243                         };
244                         nodeID = nil;
245                 }
246         }
248         set { | ... args |
249                 server.sendBundle(server.latency, ["/n_set", nodeID] ++ args);
250         }
252         pause { | clock, quant = 1 |
253                 this.run(clock, quant, false);
254         }
256         resume { | clock, quant = 1 |
257                 this.run(clock, quant, true);
258         }
260         run { | clock, quant, flag = true |
261                 if(nodeID.notNil) {
262                         (clock ? SystemClock).play({
263                                 server.sendMsg("/n_run", nodeID, flag.binaryValue);
264                                 paused = flag.not;
265                                 nil;
266                         }, quant)
267                 } { paused = flag.not; }
268         }
270         synthDesc {
271                 var dict = SynthDescLib.global.synthDescs;
272                 ^if(dict.notNil) { dict.at(this.asDefName.asSymbol) } { nil };
273         }
275         controlNames {
276                 var desc = this.synthDesc;
277                 ^if(desc.notNil) { desc.controls } { nil }
278         }
280         synthDefPath { ^SynthDef.synthDefDir ++ this.asDefName ++ ".scsyndef" }
282         store { SynthDescLib.global.read(this.synthDefPath) }
287 SynthDefControl : SynthControl {
289         var <synthDef, <parents;
291         readyForPlay { ^synthDef.notNil }
293         build { | proxy, orderIndex = 0 |
294                 var ok, rate, numChannels;
296                 NodeProxy.buildProxyControl = this;
297                 synthDef = source.buildForProxy(proxy, channelOffset, orderIndex);
298                 NodeProxy.buildProxyControl = nil;
300                 rate = synthDef.rate ?? { if(proxy.rate !== \control) { \audio } { \control } };
301                 numChannels = synthDef.numChannels ? proxy.numChannels ? 2;
302                 ok = proxy.initBus(rate, numChannels);
304                 if(ok and: { synthDef.notNil}) {
305                         paused = proxy.paused;
306                         canReleaseSynth = synthDef.canReleaseSynth;
307                         canFreeSynth = synthDef.canFreeSynth;
308                 } {
309                         synthDef = nil;
310                 }
311         }
313         loadToBundle { | bundle, server |
314                 var bytes, size, path;
316                 bytes = synthDef.asBytes;
317                 size = bytes.size;
318                 size = size - (size bitAnd: 3) + 84; // 4 + 4 + 16 + 16 // appx path length size + overhead
319                 if(server.options.protocol === \tcp or: { size < 16383}) {
320                         // full size: 65535, but does not work.
321                         bundle.addPrepare([5, bytes]); // "/d_recv"
322                 } {
323                         // bridge exceeding bundle size by writing to disk
324                         if(server.isLocal.not) {
325                                 Error("SynthDef too large (" ++ size
326                                 ++ " bytes) to be sent to remote server via udp").throw;
327                         };
328                         path = this.synthDefPath;
329                         this.writeSynthDefFile(path, bytes);
330                         bundle.addPrepare([6, path]); // "/d_load"
331                 }
332         }
334         freeToBundle { | bundle |
335                 if(synthDef.notNil) { bundle.addPrepare([53, synthDef.name]) } // "/d_free"
336         }
338         writeSynthDefFile { | path, bytes |
339                 var file = File(path, "w");
340                 protect { file.putAll(bytes) } { file.close }
341         }
343         asDefName { ^synthDef.name }
345         wakeUpParentsToBundle { | bundle, checkedAlready |
346                 parents.do { | proxy |
347                         proxy.wakeUpToBundle(bundle, checkedAlready)
348                 }
349         }
351         addParent { | proxy |
352                 if(parents.isNil) { parents = IdentitySet.new };
353                 parents.add(proxy);
354         }
356         controlNames { ^synthDef.allControlNames }