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