3 // lightweight objects that insulate different ways of playing/stopping.
4 // the bundle that is passed in is a MixedBundle
9 var <source, <>channelOffset;
12 classvar <>buildMethods, <>proxyControlClasses; // see wrapForNodeProxy for methods
14 *new { | source, channelOffset = 0 |
15 ^super.newCopyArgs(source, channelOffset);
23 readyForPlay { ^true }
24 distributable { ^false } // shared proxy support
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
35 stopToBundle { | bundle |
36 bundle.addOnSendMessage(this, \stop);
44 play { this.subclassResponsibility(thisMethod) }
45 stop { this.subclassResponsibility(thisMethod) }
48 wakeUpParentsToBundle {}
49 addParent { "wrong object in NodeProxy.buildControl".error } // for now.
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 {
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
70 build { | proxy, orderIndex = 0 |
72 paused = proxy.paused;
73 stream = source.buildForProxy(proxy, channelOffset, orderIndex);
77 pause { stream.pause; paused=true }
78 resume { | clock, quant = 1.0 |
79 stream.resume(clock, quant);
83 readyForPlay { ^stream.notNil }
86 if(stream.isPlaying.not) {
87 stream.play(clock, false, 0.0)
95 PatternControl : StreamControl {
102 array = array.add(str);
104 str.play(clock, false, 0.0)
106 str.xplay(dt / clock.beatDur, clock, false, 0.0)
110 stopStreams { | streams |
114 streams.do { arg item; item.stop }
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) });
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;
130 stopToBundle { | bundle |
132 streams = array.copy;
134 bundle.onSend({ this.stopStreams(streams) });
142 resume { | clock, quant = 1.0 |
143 array.do(_.resume(clock, quant));
147 playToBundle { | bundle, args, proxy, addAction = 1 |
148 if(paused.not and: { stream.isPlaying.not })
150 // no latency (latency is in stream already)
153 str = source.buildForProxy(proxy, channelOffset);
156 args.pairsDo { arg key, val; event[key] = val }
161 ^nil // return a nil object instead of a synth
165 this.stopStreams(array.copy);
173 SynthControl : AbstractPlayControl {
175 var <server, <>nodeID;
176 var <canReleaseSynth=false, <canFreeSynth=false;
178 loadToBundle {} // assumes that SynthDef is loaded in the server
180 asDefName { ^source }
182 distributable { ^canReleaseSynth } // n_free not implemented in shared node proxy
184 build { | proxy | // assumes audio rate proxy if not initialized
186 desc = this.synthDesc;
188 canFreeSynth = desc.canFreeSynth;
189 canReleaseSynth = desc.hasGate && canFreeSynth;
191 if(proxy.isNeutral) { rate = \audio };
192 ^proxy.initBus(rate, proxy.numChannels ? 2)
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);
206 bundle.add(synthMsg);
209 synthMsg = [9, this.asDefName, -1, addAction, targetID] ++ extraArgs;
210 bundle.add(synthMsg);
214 playToBundle { | bundle, extraArgs, target, addAction = 1 |
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);
228 bundle.add(synthMsg);
230 bundle.add([9, this.asDefName, nodeID, addAction, group.nodeID]++extraArgs)
232 if(paused) { bundle.add(["/n_run", nodeID, 0]) };
236 stopToBundle { | bundle |
238 if(canReleaseSynth) {
239 bundle.addAll([['/error', -1], [15, nodeID, \gate, 0.0], ['/error', -2]]);
241 if(canFreeSynth.not) { //"/n_free"
242 bundle.addAll([['/error', -1], [11, nodeID], ['/error', -2]]);
244 // otherwise it is self freeing by some inner mechanism.
251 server.sendBundle(server.latency, ["/n_set", nodeID] ++ args);
254 pause { | clock, quant = 1 |
255 this.run(clock, quant, false);
258 resume { | clock, quant = 1 |
259 this.run(clock, quant, true);
262 run { | clock, quant, flag = true |
264 (clock ? SystemClock).play({
265 server.sendMsg("/n_run", nodeID, flag.binaryValue);
269 } { paused = flag.not; }
273 var dict = SynthDescLib.global.synthDescs;
274 ^if(dict.notNil) { dict.at(this.asDefName.asSymbol) } { nil };
278 var desc = this.synthDesc;
279 ^if(desc.notNil) { desc.controls } { nil }
282 synthDefPath { ^SynthDef.synthDefDir ++ this.asDefName ++ ".scsyndef" }
284 store { SynthDescLib.global.read(this.synthDefPath) }
289 SynthDefControl : SynthControl {
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;
315 loadToBundle { | bundle, server |
316 var bytes, size, path;
318 bytes = synthDef.asBytes;
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"
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;
330 path = this.synthDefPath;
331 this.writeSynthDefFile(path, bytes);
332 bundle.addPrepare([6, path]); // "/d_load"
336 freeToBundle { | bundle |
337 if(synthDef.notNil) { bundle.addPrepare([53, synthDef.name]) } // "/d_free"
340 writeSynthDefFile { | path, bytes |
341 var file = File(path, "w");
342 protect { file.putAll(bytes) } { file.close }
345 asDefName { ^synthDef.name }
347 wakeUpParentsToBundle { | bundle, checkedAlready |
348 parents.do { | proxy |
349 proxy.wakeUpToBundle(bundle, checkedAlready)
353 addParent { | proxy |
354 if(parents.isNil) { parents = IdentitySet.new };
358 controlNames { ^synthDef.allControlNames }