1 // lightweight objects that insulate different ways of playing/stopping.
2 // the bundle that is passed in is a MixedBundle
7 var <source, <>channelOffset;
10 classvar <>buildMethods, <>proxyControlClasses; // see wrapForNodeProxy for methods
12 *new { | source, channelOffset = 0 |
13 ^super.newCopyArgs(source, channelOffset);
21 readyForPlay { ^true }
22 distributable { ^false } // shared proxy support
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
33 stopToBundle { | bundle |
34 bundle.addOnSendMessage(this, \stop);
42 play { this.subclassResponsibility(thisMethod) }
43 stop { this.subclassResponsibility(thisMethod) }
46 wakeUpParentsToBundle {}
47 addParent { "wrong object in NodeProxy.buildControl".error } // for now.
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 {
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
68 build { | proxy, orderIndex = 0 |
70 paused = proxy.paused;
71 stream = source.buildForProxy(proxy, channelOffset, orderIndex);
75 pause { stream.pause; paused=true }
76 resume { | clock, quant = 1.0 |
77 stream.resume(clock, quant);
81 readyForPlay { ^stream.notNil }
84 if(stream.isPlaying.not) {
85 stream.play(clock, false, 0.0)
93 PatternControl : StreamControl {
100 array = array.add(str);
102 str.play(clock, false, 0.0)
104 str.xplay(dt / clock.beatDur, clock, false, 0.0)
108 stopStreams { | streams, dt |
110 dt = (dt ? fadeTime).value;
112 streams.do { arg item; item.stop }
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) });
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;
128 stopToBundle { | bundle, dt |
130 streams = array.copy;
132 bundle.onSend({ this.stopStreams(streams, dt) });
140 resume { | clock, quant = 1.0 |
141 array.do(_.resume(clock, quant));
145 playToBundle { | bundle, args, proxy, addAction = 1 |
146 if(paused.not and: { stream.isPlaying.not })
148 // no latency (latency is in stream already)
151 str = source.buildForProxy(proxy, channelOffset);
154 args.pairsDo { arg key, val; event[key] = val }
159 ^nil // return a nil object instead of a synth
163 this.stopStreams(array.copy);
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
184 desc = this.synthDesc;
186 canFreeSynth = desc.canFreeSynth;
187 canReleaseSynth = desc.hasGate && canFreeSynth;
189 if(proxy.isNeutral) { rate = \audio };
190 ^proxy.initBus(rate, proxy.numChannels ? 2)
193 spawnToBundle { | bundle, extraArgs, target, addAction = 0 | // assumes self freeing
194 var targetID = target.asTarget.nodeID;
195 bundle.add([9, this.asDefName, -1, addAction, targetID]++extraArgs.asOSCArgArray);
198 playToBundle { | bundle, extraArgs, target, addAction = 1 |
200 server = target.server;
201 group = target.asTarget;
202 nodeID = server.nextNodeID;
203 bundle.add([9, this.asDefName, nodeID, addAction, group.nodeID]++extraArgs.asOSCArgArray);
204 if(paused) { bundle.add(["/n_run", nodeID, 0]) };
208 stopToBundle { | bundle, fadeTime |
210 if(canReleaseSynth) {
211 bundle.addAll([['/error', -1], [15, nodeID, \gate, 0.0, \fadeTime, fadeTime], ['/error', -2]]);
213 if(canFreeSynth.not) { //"/n_free"
214 bundle.addAll([['/error', -1], [11, nodeID], ['/error', -2]]);
216 // otherwise it is self freeing by some inner mechanism.
223 server.sendBundle(server.latency, ["/n_set", nodeID] ++ args);
226 pause { | clock, quant = 1 |
227 this.run(clock, quant, false);
230 resume { | clock, quant = 1 |
231 this.run(clock, quant, true);
234 run { | clock, quant, flag = true |
236 (clock ? SystemClock).play({
237 server.sendMsg("/n_run", nodeID, flag.binaryValue);
241 } { paused = flag.not; }
245 var dict = SynthDescLib.global.synthDescs;
246 ^if(dict.notNil) { dict.at(this.asDefName.asSymbol) } { nil };
250 var desc = this.synthDesc;
251 ^if(desc.notNil) { desc.controls } { nil }
254 synthDefPath { ^SynthDef.synthDefDir ++ this.asDefName ++ ".scsyndef" }
256 store { SynthDescLib.global.read(this.synthDefPath) }
261 SynthDefControl : SynthControl {
263 var <synthDef, <parents;
265 readyForPlay { ^synthDef.notNil }
267 build { | proxy, orderIndex = 0 |
268 var ok, rate, numChannels;
270 NodeProxy.buildProxyControl = this;
271 synthDef = source.buildForProxy(proxy, channelOffset, orderIndex);
272 NodeProxy.buildProxyControl = nil;
274 rate = synthDef.rate ?? { if(proxy.rate !== \control) { \audio } { \control } };
275 numChannels = synthDef.numChannels ? proxy.numChannels ? 2;
276 ok = proxy.initBus(rate, numChannels);
278 if(ok and: { synthDef.notNil}) {
279 paused = proxy.paused;
280 canReleaseSynth = synthDef.canReleaseSynth;
281 canFreeSynth = synthDef.canFreeSynth;
287 loadToBundle { | bundle, server |
288 var bytes, size, path;
290 bytes = synthDef.asBytes;
292 size = size - (size bitAnd: 3) + 84; // 4 + 4 + 16 + 16 // appx path length size + overhead
293 if(server.options.protocol === \tcp or: { size < 16383}) {
294 // full size: 65535, but does not work.
295 bundle.addPrepare([5, bytes]); // "/d_recv"
297 // bridge exceeding bundle size by writing to disk
298 if(server.isLocal.not) {
299 Error("SynthDef too large (" ++ size
300 ++ " bytes) to be sent to remote server via udp").throw;
302 path = this.synthDefPath;
303 this.writeSynthDefFile(path, bytes);
304 bundle.addPrepare([6, path]); // "/d_load"
308 freeToBundle { | bundle |
309 if(synthDef.notNil) { bundle.addPrepare([53, synthDef.name]) } // "/d_free"
312 writeSynthDefFile { | path, bytes |
313 var file = File(path, "w");
314 protect { file.putAll(bytes) } { file.close }
317 asDefName { ^synthDef.name }
319 wakeUpParentsToBundle { | bundle, checkedAlready |
320 parents.do { | proxy |
321 proxy.wakeUpToBundle(bundle, checkedAlready)
325 addParent { | proxy |
326 if(parents.isNil) { parents = IdentitySet.new };
330 controlNames { ^synthDef.allControlNames }