cmake build system: visiblity support for clang
[supercollider.git] / SCClassLibrary / Common / Control / Node.sc
blobb16e883743144ad7fe6bacfd0df5cbec50521e8a
1 Node {
3         var <>nodeID, <>server, <>group;
4         var <>isPlaying = false, <>isRunning = false;
5         classvar <addActions;
7         *initClass {
8                 addActions = (
9                         addToHead: 0,
10                         addToTail: 1,
11                         addBefore: 2,
12                         addAfter: 3,
13                         addReplace: 4,
14                         h: 0,
15                         t: 1,
16                                 // valid action numbers should stay the same
17                         0: 0, 1: 1, 2: 2, 3: 3, 4: 4
18                 );
19         }
21         *basicNew { arg server, nodeID;
22                 server = server ? Server.default;
23                 ^super.newCopyArgs(nodeID ?? { server.nextNodeID }, server)
24         }
26         *actionNumberFor { |addAction = (\addToHead)| ^addActions[addAction] }
28         free { arg sendFlag=true;
29                 if(sendFlag, {
30                         server.sendMsg(11, nodeID);  //"/n_free"
31                 });
32                 group = nil;
33                 isPlaying = false;
34                 isRunning = false;
35         }
36         freeMsg { ^[11, nodeID] }
38         run { arg flag=true;
39                 server.sendMsg(12, nodeID,flag.binaryValue); //"/n_run"
40         }
42         runMsg { arg flag=true;
43                 ^[12, nodeID,flag.binaryValue]; //"/n_run"
44         }
46         map { arg ... args;
47                 var     bundle = this.mapMsg(*args);
48                 if(bundle[0].isString) {
49                         server.sendBundle(nil, bundle);
50                 } {
51                         server.sendBundle(nil, *bundle);
52                 };
53         }
55         mapMsg { arg ... args;
56                 var krVals, arVals, result;
57                 krVals = List.new;
58                 arVals = List.new;
59                 result = Array.new(2);
60                 args.pairsDo({ arg control, bus;
61                         bus = bus.asBus;
62                         switch(bus.rate)
63                                 { \control } {
64                                         krVals.addAll([control.asControlInput, bus.index, bus.numChannels])
65                                 }
66                                 { \audio } {
67                                         arVals.addAll([control.asControlInput, bus.index, bus.numChannels])
68                                 };
69                                 // no default case, ignore others
70                 });
71                 if(krVals.size > 0, { result = result.add(["/n_mapn", nodeID] ++ krVals) });
72                 if(arVals.size > 0, { result = result.add(["/n_mapan",nodeID] ++ arVals) });
73                 if(result.size < 2, { result = result.flatten; });
74                 ^result;
75         }
76         mapn { arg ... args;
77                 server.sendMsg(48, nodeID, *(args.asControlInput)); //"/n_mapn"
78         }
79         mapnMsg { arg ... args;
80                 ^[48, nodeID] ++ args.asControlInput; //"/n_mapn"
81         }
82         // map to Bus objects
83         busMap { arg firstControl, aBus ... args;
84                 var values;
85                 this.deprecated(thisMethod, Node.findMethod(\map));
86                 values = List.new;
87                 args.pairsDo({ arg control, bus; values.addAll([control, bus.index, bus.numChannels])});
88                 server.sendMsg(48, nodeID, firstControl, aBus.index, aBus.numChannels, *values);
89                 //"/n_mapn"
90         }
91         busMapMsg { arg firstControl, aBus ... args;
92                 var values;
93                 this.deprecated(thisMethod, Node.findMethod(\mapMsg));
94                 values = List.new;
95                 args.pairsDo({ arg control, bus; values.addAll([control, bus.index, bus.numChannels])});
96                 ^[48, nodeID, firstControl, aBus.index, aBus.numChannels] ++ values;
97                 //"/n_mapn"
98         }
100         set { arg ... args;
101                 server.sendMsg(15, nodeID, *(args.asOSCArgArray));  //"/n_set"
102         }
104         setMsg { arg ... args;
105 //              ^[15, nodeID] ++ args.unbubble.asControlInput;
106                 ^[15, nodeID] ++ args.asOSCArgArray;     //"/n_set"
107         }
109         setn { arg ... args;
110                 server.sendMsg(*this.setnMsg(*args));
111         }
113         *setnMsgArgs{ arg ... args;
114                 var nargs=List.new;
115                 args = args.asControlInput;
116                 args.pairsDo { arg control, moreVals;
117                         if(moreVals.isArray,{
118                                 nargs.addAll([control, moreVals.size]++ moreVals);
119                         },{
120                                 nargs.addAll([control, 1, moreVals]);
121                         });
122                 };
123                 ^nargs;
124         }
126         setnMsg { arg ... args;
127                 ^[16, nodeID] ++ Node.setnMsgArgs(*args);
128                 // "n_setn"
129         }
131         fill { arg controlName, numControls, value ... args;
132                 server.sendMsg(17, nodeID, controlName, numControls, value, *(args.asControlInput));//"n_fill"
133         }
135         fillMsg { arg controlName, numControls, value ... args;
136                 ^[17, nodeID, controlName, numControls, value] ++ args.asControlInput; //"n_fill"
137         }
139         release { arg releaseTime;
140                 server.sendMsg(*this.releaseMsg(releaseTime))
141         }
142         releaseMsg { arg releaseTime;
143                 //assumes a control called 'gate' in the synth
144                 if(releaseTime.isNil, {
145                         releaseTime = 0.0;
146                 },{
147                         releaseTime = -1.0 - releaseTime;
148                 });
149                 ^[15, nodeID, \gate, releaseTime]
150         }
151         trace {
152                 server.sendMsg(10, nodeID);//"/n_trace"
153         }
154         query {
155                 OSCFunc({ arg msg;
156                         var cmd,argnodeID,parent,prev,next,isGroup,head,tail;
157                         # cmd,argnodeID,parent,prev,next,isGroup,head,tail = msg;
158                         // assuming its me ... if(nodeID == argnodeID)
159                         Post << if(isGroup == 1, "Group:" , "Synth:") << nodeID << Char.nl
160                                 << "parent   : " << parent << Char.nl
161                                 << "prev : " << prev << Char.nl
162                                 << "next  :" << next << Char.nl;
163                         if(isGroup==1,{
164                                 Post << "head :" << head << Char.nl
165                                  << "tail :" << tail << Char.nl << Char.nl;
166                         });
167                 }, '/n_info', server.addr).oneShot;
168                 server.sendMsg(46, nodeID)  //"/n_query"
169         }
170         register { arg assumePlaying=false;
171                 NodeWatcher.register(this, assumePlaying)
172         }
174         onFree {|func|
175                 var f = {|n,m|
176                         if(m==\n_end) {
177                                 func.value(this,m);
178                                 this.removeDependant(f);
179                         }
180                 };
181                 this.register;
182                 this.addDependant(f);
183         }
184         waitForFree {
185                 var c = Condition.new;
186                 this.onFree({c.unhang});
187                 c.hang;
188         }
190         moveBefore { arg aNode;
191                 group = aNode.group;
192                 server.sendMsg(18, nodeID, aNode.nodeID); //"/n_before"
193         }
194         moveAfter { arg aNode;
195                 group = aNode.group;
196                 server.sendMsg(19, nodeID, aNode.nodeID); //"/n_after"
197         }
198         moveToHead { arg aGroup;
199                 (aGroup ? server.defaultGroup).moveNodeToHead(this);
200         }
201         moveToTail { arg aGroup;
202                 (aGroup ? server.defaultGroup).moveNodeToTail(this);
203         }
205         // message creating methods
207         moveBeforeMsg { arg aNode;
208                 group = aNode.group;
209                 ^[18, nodeID, aNode.nodeID];//"/n_before"
210         }
211         moveAfterMsg { arg aNode;
212                 group = aNode.group;
213                 ^[19, nodeID, aNode.nodeID]; //"/n_after"
214         }
215         moveToHeadMsg { arg aGroup;
216                 ^(aGroup ? server.defaultGroup).moveNodeToHeadMsg(this);
217         }
218         moveToTailMsg { arg aGroup;
219                 ^(aGroup ? server.defaultGroup).moveNodeToTailMsg(this);
220         }
222         *orderNodesMsg { arg nodes;
223                 var msg = [18]; // "/n_after"
224                 nodes.doAdjacentPairs { |first, toMoveAfter|
225                         msg = msg.add(toMoveAfter.nodeID);
226                         msg = msg.add(first.nodeID);
227                 };
228                 ^msg
229         }
231         hash {  ^server.hash bitXor: nodeID.hash        }
233         == { arg aNode;
234                 ^aNode respondsTo: #[\nodeID, \server]
235                         and: { aNode.nodeID == nodeID and: { aNode.server === server }}
236         }
237         printOn { arg stream; stream << this.class.name << "(" << nodeID <<")" }
238         asUGenInput { Error("should not use a % inside a SynthDef".format(this.class.name)).throw }
239         asControlInput { ^this.nodeID }
243 // common base for Group and ParGroup classes
244 AbstractGroup : Node {
246         /** immediately sends **/
247         *new { arg target, addAction=\addToHead;
248                 var group, server, addNum, inTarget;
249                 inTarget = target.asTarget;
250                 server = inTarget.server;
251                 group = this.basicNew(server);
252                 addNum = addActions[addAction];
253                 if((addNum < 2), { group.group = inTarget; }, { group.group = inTarget.group; });
254                 server.sendMsg(this.creationCmd, group.nodeID, addNum, inTarget.nodeID);
255                 ^group
256         }
257         newMsg { arg target, addAction = \addToHead;
258                 var addNum, inTarget;
259                 // if target is nil set to default group of server specified when basicNew was called
260                 inTarget = (target ? server.defaultGroup).asTarget;
261                 addNum = addActions[addAction];
262                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
263                 ^[this.class.creationCmd, nodeID, addNum, inTarget.nodeID]
264         }
266         // for bundling
267         addToHeadMsg { arg aGroup;
268                 // if aGroup is nil set to default group of server specified when basicNew was called
269                 group = (aGroup ? server.defaultGroup);
270                 ^[this.class.creationCmd, nodeID, 0, group.nodeID]
271         }
272         addToTailMsg { arg aGroup;
273                 // if aGroup is nil set to default group of server specified when basicNew was called
274                 group = (aGroup ? server.defaultGroup);
275                 ^[this.class.creationCmd, nodeID, 1, group.nodeID]
276         }
277         addAfterMsg {  arg aNode;
278                 group = aNode.group;
279                 ^[this.class.creationCmd, nodeID, 3, aNode.nodeID]
280         }
281         addBeforeMsg {  arg aNode;
282                 group = aNode.group;
283                 ^[this.class.creationCmd, nodeID, 2, aNode.nodeID]
284         }
285         addReplaceMsg { arg nodeToReplace;
286                 group = nodeToReplace.group;
287                 ^[this.class.creationCmd, nodeID, 4, nodeToReplace.nodeID]
288         }
291         *after { arg aNode;    ^this.new(aNode, \addAfter) }
292         *before {  arg aNode;   ^this.new(aNode, \addBefore) }
293         *head { arg aGroup;     ^this.new(aGroup, \addToHead) }
294         *tail { arg aGroup;     ^this.new(aGroup, \addToTail) }
295         *replace { arg nodeToReplace; ^this.new(nodeToReplace, \addReplace) }
297     // move Nodes to this group
298     moveNodeToHead { arg aNode;
299         aNode.group = this;
300         server.sendMsg(22, nodeID, aNode.nodeID); //"/g_head"
301     }
302     moveNodeToTail { arg aNode;
303         aNode.group = this;
304         server.sendMsg(23, nodeID, aNode.nodeID); //"/g_tail"
305     }
306     moveNodeToHeadMsg { arg aNode;
307         aNode.group = this;
308         ^[22, nodeID, aNode.nodeID];            //"/g_head"
309     }
310     moveNodeToTailMsg { arg aNode;
311         aNode.group = this;
312         ^[23, nodeID, aNode.nodeID];            //g_tail
313     }
315         freeAll {
316                 // free my children, but this node is still playing
317                 server.sendMsg(24, nodeID); //"/g_freeAll"
318         }
319         freeAllMsg {
320                 // free my children, but this node is still playing
321                 ^[24, nodeID]; //"/g_freeAll"
322         }
323         deepFree {
324                 server.sendMsg(50, nodeID) //"/g_deepFree"
325         }
326         deepFreeMsg {
327                 ^[50, nodeID] //"/g_deepFree"
328         }
330         // Introspection
331         dumpTree { arg postControls = false;
332                 server.sendMsg("/g_dumpTree", nodeID, postControls.binaryValue)
333         }
335         queryTree { //|action|
336                 var resp, done = false;
337                 resp = OSCFunc({ arg msg;
338                         var i = 2, tabs = 0, printControls = false, dumpFunc;
339                         if(msg[1] != 0, {printControls = true});
340                         ("NODE TREE Group" + msg[2]).postln;
341                         if(msg[3] > 0, {
342                                 dumpFunc = {|numChildren|
343                                         var j;
344                                         tabs = tabs + 1;
345                                         numChildren.do({
346                                                 if(msg[i + 1] >=0, {i = i + 2}, {
347                                                         i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
348                                                 });
349                                                 tabs.do({ "   ".post });
350                                                 msg[i].post; // nodeID
351                                                 if(msg[i + 1] >=0, {
352                                                         " group".postln;
353                                                         if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
354                                                         }, {
355                                                                 (" " ++ msg[i + 2]).postln; // defname
356                                                                 if(printControls, {
357                                                                         if(msg[i + 3] > 0, {
358                                                                                 " ".post;
359                                                                                 tabs.do({ "   ".post });
360                                                                         });
361                                                                         j = 0;
362                                                                         msg[i + 3].do({
363                                                                                 " ".post;
364                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
365                                                                                         (msg[i + 4 + j] ++ ": ").post;
366                                                                                 });
367                                                                                 msg[i + 5 + j].post;
368                                                                                 j = j + 2;
369                                                                         });
370                                                                         "\n".post;
371                                                                 });
372                                                         });
373                                         });
374                                         tabs = tabs - 1;
375                                 };
376                                 dumpFunc.value(msg[3]);
377                         });
379                         //                              action.value(msg);
380                         done = true;
381                 }, '/g_queryTree.reply', server.addr).oneShot;
382                 server.sendMsg("/g_queryTree", nodeID);
383                 SystemClock.sched(3, {
384                         done.not.if({
385                                 resp.free;
386                                 "Server failed to respond to Group:queryTree!".warn;
387                         });
388                 });
389         }
391         *creationCmd { ^this.subclassMustImplementThisMethod }
393 //      queryTree { |action|
394 //              var resp, done = false;
395 //              resp = OSCresponderNode(server.addr, '/g_queryTree.reply', { arg time, responder, msg;
396 //                              action.value(msg);
397 //                              done = true;
398 //                      }).add.removeWhenDone;
399 //              server.sendMsg("/g_queryTree", nodeID);
400 //              SystemClock.sched(3, {
401 //                      done.not.if({
402 //                              resp.remove;
403 //                              "Server failed to respond to Group:queryTree!".warn;
404 //                      });
405 //              });
406 //      }
409 Group : AbstractGroup {
410         *creationCmd { ^21 }    //"/g_new"
413 Synth : Node {
415         var <>defName;
417         /** immediately sends **/
418         *new { arg defName, args, target, addAction=\addToHead;
419                 var synth, server, addNum, inTarget;
420                 inTarget = target.asTarget;
421                 server = inTarget.server;
422                 addNum = addActions[addAction];
423                 synth = this.basicNew(defName, server);
425                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
426 //              server.sendMsg(59, //"s_newargs"
427 //                      defName, synth.nodeID, addNum, inTarget.nodeID,
428 //                      *Node.setnMsgArgs(*args));
429                 server.sendMsg(9, //"s_new"
430                         defName, synth.nodeID, addNum, inTarget.nodeID,
431                         *(args.asOSCArgArray)
432                 );
433                 ^synth
434         }
435         *newPaused { arg defName, args, target, addAction=\addToHead;
436                 var synth, server, addNum, inTarget;
437                 inTarget = target.asTarget;
438                 server = inTarget.server;
439                 addNum = addActions[addAction];
440                 synth = this.basicNew(defName, server);
441                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
442                 server.sendBundle(nil, [9, defName, synth.nodeID, addNum, inTarget.nodeID] ++
443                         args.asOSCArgArray, [12, synth.nodeID, 0]); // "s_new" + "/n_run"
444                 ^synth
445         }
446                 /** does not send       (used for bundling) **/
447         *basicNew { arg defName, server, nodeID;
448                 ^super.basicNew(server, nodeID).defName_(defName.asDefName)
449         }
451         newMsg { arg target, args, addAction = \addToHead;
452                 var addNum, inTarget;
453                 addNum = addActions[addAction];
454                 // if target is nil set to default group of server specified when basicNew was called
455                 inTarget = (target ? server.defaultGroup).asTarget;
456                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
457                 ^[9, defName, nodeID, addNum, inTarget.nodeID] ++ args.asOSCArgArray; //"/s_new"
458         }
459         *after { arg aNode, defName, args;
460                 ^this.new(defName, args, aNode, \addAfter);
461         }
462         *before {  arg aNode, defName, args;
463                 ^this.new(defName, args, aNode, \addBefore);
464         }
465         *head { arg aGroup, defName, args;
466                 ^this.new(defName, args, aGroup, \addToHead);
467         }
468         *tail { arg aGroup, defName, args;
469                 ^this.new(defName, args, aGroup, \addToTail);
470         }
471         *replace { arg nodeToReplace, defName, args;
472                 ^this.new(defName, args, nodeToReplace, \addReplace)
473         }
474         // for bundling
475         addToHeadMsg { arg aGroup, args;
476                 // if aGroup is nil set to default group of server specified when basicNew was called
477                 group = (aGroup ? server.defaultGroup);
478                 ^[9, defName, nodeID, 0, group.nodeID] ++ args.asOSCArgArray    // "/s_new"
479         }
480         addToTailMsg { arg aGroup, args;
481                 // if aGroup is nil set to default group of server specified when basicNew was called
482                 group = (aGroup ? server.defaultGroup);
483                 ^[9, defName, nodeID, 1, group.nodeID] ++ args.asOSCArgArray // "/s_new"
484         }
485         addAfterMsg {  arg aNode, args;
486                 group = aNode.group;
487                 ^[9, defName, nodeID, 3, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
488         }
489         addBeforeMsg {  arg aNode, args;
490                 group = aNode.group;
491                 ^[9, defName, nodeID, 2, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
492         }
493         addReplaceMsg { arg nodeToReplace, args;
494                 group = nodeToReplace.group;
495                 ^[9, defName, nodeID, 4, nodeToReplace.nodeID] ++ args.asOSCArgArray // "/s_new"
496         }
498         // nodeID -1
499         *grain { arg defName, args, target, addAction=\addToHead;
500                 var server;
501                 target = target.asTarget;
502                 server = target.server;
503                 server.sendMsg(9, defName.asDefName, -1, addActions[addAction], target.nodeID,
504                         *(args.asOSCArgArray)); //"/s_new"
505                 ^nil;
506         }
508         get { arg index, action;
509                 OSCpathResponder(server.addr,['/n_set',nodeID,index],{ arg time, r, msg;
510                         action.value(msg.at(3)); r.remove }).add;
511                 server.sendMsg(44, nodeID, index);      //"/s_get"
512         }
513         getMsg { arg index;
514                 ^[44, nodeID, index];   //"/s_get"
515         }
517         getn { arg index, count, action;
518                 OSCpathResponder(server.addr,['/n_setn',nodeID,index],{arg time, r, msg;
519                         action.value(msg.copyToEnd(4)); r.remove } ).add;
520                 server.sendMsg(45, nodeID, index, count); //"/s_getn"
521         }
522         getnMsg { arg index, count;
523                 ^[45, nodeID, index, count]; //"/s_getn"
524         }
526         printOn { arg stream; stream << this.class.name << "(" <<< defName << " : " << nodeID <<")" }
529 RootNode : Group {
531         classvar <roots;
533         *new { arg server;
534                 server = server ? Server.default;
535                 ^(roots.at(server.name) ?? {
536                         ^super.basicNew(server, 0).rninit
537                 })
538         }
539         rninit {
540                 roots.put(server.name, this);
541                 isPlaying = isRunning = true;
542                 group = this; // self
543         }
544         *initClass {  roots = IdentityDictionary.new; }
546         run { "run has no effect on RootNode".warn; }
547         free { "free has no effect on RootNode".warn; }
548         moveBefore { "moveBefore has no effect on RootNode".warn; }
549         moveAfter { "moveAfter has no effect on RootNode".warn; }
550         moveToHead { "moveToHead has no effect on RootNode".warn; }
551         moveToTail{ "moveToTail has no effect on RootNode".warn; }
553         *freeAll {
554                 roots.do({ arg rn; rn.freeAll })
555         }
558 ParGroup : AbstractGroup {
559         *creationCmd { ^63 }    //"/p_new"