HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / Common / Control / Node.sc
blob10c2f6b616cbf53cabd6d41d480356b03130380c
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                 ^f;
184         }
185         waitForFree {
186                 var c = Condition.new;
187                 this.onFree({c.unhang});
188                 c.hang;
189         }
191         moveBefore { arg aNode;
192                 group = aNode.group;
193                 server.sendMsg(18, nodeID, aNode.nodeID); //"/n_before"
194         }
195         moveAfter { arg aNode;
196                 group = aNode.group;
197                 server.sendMsg(19, nodeID, aNode.nodeID); //"/n_after"
198         }
199         moveToHead { arg aGroup;
200                 (aGroup ? server.defaultGroup).moveNodeToHead(this);
201         }
202         moveToTail { arg aGroup;
203                 (aGroup ? server.defaultGroup).moveNodeToTail(this);
204         }
206         // message creating methods
208         moveBeforeMsg { arg aNode;
209                 group = aNode.group;
210                 ^[18, nodeID, aNode.nodeID];//"/n_before"
211         }
212         moveAfterMsg { arg aNode;
213                 group = aNode.group;
214                 ^[19, nodeID, aNode.nodeID]; //"/n_after"
215         }
216         moveToHeadMsg { arg aGroup;
217                 ^(aGroup ? server.defaultGroup).moveNodeToHeadMsg(this);
218         }
219         moveToTailMsg { arg aGroup;
220                 ^(aGroup ? server.defaultGroup).moveNodeToTailMsg(this);
221         }
223         *orderNodesMsg { arg nodes;
224                 var msg = [18]; // "/n_after"
225                 nodes.doAdjacentPairs { |first, toMoveAfter|
226                         msg = msg.add(toMoveAfter.nodeID);
227                         msg = msg.add(first.nodeID);
228                 };
229                 ^msg
230         }
232         hash {  ^server.hash bitXor: nodeID.hash        }
234         == { arg aNode;
235                 ^aNode respondsTo: #[\nodeID, \server]
236                         and: { aNode.nodeID == nodeID and: { aNode.server === server }}
237         }
238         printOn { arg stream; stream << this.class.name << "(" << nodeID <<")" }
239         asUGenInput { Error("should not use a % inside a SynthDef".format(this.class.name)).throw }
240         asControlInput { ^this.nodeID }
244 // common base for Group and ParGroup classes
245 AbstractGroup : Node {
247         /** immediately sends **/
248         *new { arg target, addAction=\addToHead;
249                 var group, server, addNum, inTarget;
250                 inTarget = target.asTarget;
251                 server = inTarget.server;
252                 group = this.basicNew(server);
253                 addNum = addActions[addAction];
254                 if((addNum < 2), { group.group = inTarget; }, { group.group = inTarget.group; });
255                 server.sendMsg(this.creationCmd, group.nodeID, addNum, inTarget.nodeID);
256                 ^group
257         }
258         newMsg { arg target, addAction = \addToHead;
259                 var addNum, inTarget;
260                 // if target is nil set to default group of server specified when basicNew was called
261                 inTarget = (target ? server.defaultGroup).asTarget;
262                 addNum = addActions[addAction];
263                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
264                 ^[this.class.creationCmd, nodeID, addNum, inTarget.nodeID]
265         }
267         // for bundling
268         addToHeadMsg { arg aGroup;
269                 // if aGroup is nil set to default group of server specified when basicNew was called
270                 group = (aGroup ? server.defaultGroup);
271                 ^[this.class.creationCmd, nodeID, 0, group.nodeID]
272         }
273         addToTailMsg { arg aGroup;
274                 // if aGroup is nil set to default group of server specified when basicNew was called
275                 group = (aGroup ? server.defaultGroup);
276                 ^[this.class.creationCmd, nodeID, 1, group.nodeID]
277         }
278         addAfterMsg {  arg aNode;
279                 group = aNode.group;
280                 ^[this.class.creationCmd, nodeID, 3, aNode.nodeID]
281         }
282         addBeforeMsg {  arg aNode;
283                 group = aNode.group;
284                 ^[this.class.creationCmd, nodeID, 2, aNode.nodeID]
285         }
286         addReplaceMsg { arg nodeToReplace;
287                 group = nodeToReplace.group;
288                 ^[this.class.creationCmd, nodeID, 4, nodeToReplace.nodeID]
289         }
292         *after { arg aNode;    ^this.new(aNode, \addAfter) }
293         *before {  arg aNode;   ^this.new(aNode, \addBefore) }
294         *head { arg aGroup;     ^this.new(aGroup, \addToHead) }
295         *tail { arg aGroup;     ^this.new(aGroup, \addToTail) }
296         *replace { arg nodeToReplace; ^this.new(nodeToReplace, \addReplace) }
298     // move Nodes to this group
299     moveNodeToHead { arg aNode;
300         aNode.group = this;
301         server.sendMsg(22, nodeID, aNode.nodeID); //"/g_head"
302     }
303     moveNodeToTail { arg aNode;
304         aNode.group = this;
305         server.sendMsg(23, nodeID, aNode.nodeID); //"/g_tail"
306     }
307     moveNodeToHeadMsg { arg aNode;
308         aNode.group = this;
309         ^[22, nodeID, aNode.nodeID];            //"/g_head"
310     }
311     moveNodeToTailMsg { arg aNode;
312         aNode.group = this;
313         ^[23, nodeID, aNode.nodeID];            //g_tail
314     }
316         freeAll {
317                 // free my children, but this node is still playing
318                 server.sendMsg(24, nodeID); //"/g_freeAll"
319         }
320         freeAllMsg {
321                 // free my children, but this node is still playing
322                 ^[24, nodeID]; //"/g_freeAll"
323         }
324         deepFree {
325                 server.sendMsg(50, nodeID) //"/g_deepFree"
326         }
327         deepFreeMsg {
328                 ^[50, nodeID] //"/g_deepFree"
329         }
331         // Introspection
332         dumpTree { arg postControls = false;
333                 server.sendMsg("/g_dumpTree", nodeID, postControls.binaryValue)
334         }
336         queryTree { //|action|
337                 var resp, done = false;
338                 resp = OSCFunc({ arg msg;
339                         var i = 2, tabs = 0, printControls = false, dumpFunc;
340                         if(msg[1] != 0, {printControls = true});
341                         ("NODE TREE Group" + msg[2]).postln;
342                         if(msg[3] > 0, {
343                                 dumpFunc = {|numChildren|
344                                         var j;
345                                         tabs = tabs + 1;
346                                         numChildren.do({
347                                                 if(msg[i + 1] >=0, {i = i + 2}, {
348                                                         i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
349                                                 });
350                                                 tabs.do({ "   ".post });
351                                                 msg[i].post; // nodeID
352                                                 if(msg[i + 1] >=0, {
353                                                         " group".postln;
354                                                         if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
355                                                         }, {
356                                                                 (" " ++ msg[i + 2]).postln; // defname
357                                                                 if(printControls, {
358                                                                         if(msg[i + 3] > 0, {
359                                                                                 " ".post;
360                                                                                 tabs.do({ "   ".post });
361                                                                         });
362                                                                         j = 0;
363                                                                         msg[i + 3].do({
364                                                                                 " ".post;
365                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
366                                                                                         (msg[i + 4 + j] ++ ": ").post;
367                                                                                 });
368                                                                                 msg[i + 5 + j].post;
369                                                                                 j = j + 2;
370                                                                         });
371                                                                         "\n".post;
372                                                                 });
373                                                         });
374                                         });
375                                         tabs = tabs - 1;
376                                 };
377                                 dumpFunc.value(msg[3]);
378                         });
380                         //                              action.value(msg);
381                         done = true;
382                 }, '/g_queryTree.reply', server.addr).oneShot;
383                 server.sendMsg("/g_queryTree", nodeID);
384                 SystemClock.sched(3, {
385                         done.not.if({
386                                 resp.free;
387                                 "Server failed to respond to Group:queryTree!".warn;
388                         });
389                 });
390         }
392         *creationCmd { ^this.subclassMustImplementThisMethod }
394 //      queryTree { |action|
395 //              var resp, done = false;
396 //              resp = OSCresponderNode(server.addr, '/g_queryTree.reply', { arg time, responder, msg;
397 //                              action.value(msg);
398 //                              done = true;
399 //                      }).add.removeWhenDone;
400 //              server.sendMsg("/g_queryTree", nodeID);
401 //              SystemClock.sched(3, {
402 //                      done.not.if({
403 //                              resp.remove;
404 //                              "Server failed to respond to Group:queryTree!".warn;
405 //                      });
406 //              });
407 //      }
410 Group : AbstractGroup {
411         *creationCmd { ^21 }    //"/g_new"
414 Synth : Node {
416         var <>defName;
418         /** immediately sends **/
419         *new { arg defName, args, target, addAction=\addToHead;
420                 var synth, server, addNum, inTarget;
421                 inTarget = target.asTarget;
422                 server = inTarget.server;
423                 addNum = addActions[addAction];
424                 synth = this.basicNew(defName, server);
426                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
427 //              server.sendMsg(59, //"s_newargs"
428 //                      defName, synth.nodeID, addNum, inTarget.nodeID,
429 //                      *Node.setnMsgArgs(*args));
430                 server.sendMsg(9, //"s_new"
431                         defName, synth.nodeID, addNum, inTarget.nodeID,
432                         *(args.asOSCArgArray)
433                 );
434                 ^synth
435         }
436         *newPaused { arg defName, args, target, addAction=\addToHead;
437                 var synth, server, addNum, inTarget;
438                 inTarget = target.asTarget;
439                 server = inTarget.server;
440                 addNum = addActions[addAction];
441                 synth = this.basicNew(defName, server);
442                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
443                 server.sendBundle(nil, [9, defName, synth.nodeID, addNum, inTarget.nodeID] ++
444                         args.asOSCArgArray, [12, synth.nodeID, 0]); // "s_new" + "/n_run"
445                 ^synth
446         }
447                 /** does not send       (used for bundling) **/
448         *basicNew { arg defName, server, nodeID;
449                 ^super.basicNew(server, nodeID).defName_(defName.asDefName)
450         }
452         newMsg { arg target, args, addAction = \addToHead;
453                 var addNum, inTarget;
454                 addNum = addActions[addAction];
455                 // if target is nil set to default group of server specified when basicNew was called
456                 inTarget = (target ? server.defaultGroup).asTarget;
457                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
458                 ^[9, defName, nodeID, addNum, inTarget.nodeID] ++ args.asOSCArgArray; //"/s_new"
459         }
460         *after { arg aNode, defName, args;
461                 ^this.new(defName, args, aNode, \addAfter);
462         }
463         *before {  arg aNode, defName, args;
464                 ^this.new(defName, args, aNode, \addBefore);
465         }
466         *head { arg aGroup, defName, args;
467                 ^this.new(defName, args, aGroup, \addToHead);
468         }
469         *tail { arg aGroup, defName, args;
470                 ^this.new(defName, args, aGroup, \addToTail);
471         }
472         *replace { arg nodeToReplace, defName, args;
473                 ^this.new(defName, args, nodeToReplace, \addReplace)
474         }
475         // for bundling
476         addToHeadMsg { arg aGroup, args;
477                 // if aGroup is nil set to default group of server specified when basicNew was called
478                 group = (aGroup ? server.defaultGroup);
479                 ^[9, defName, nodeID, 0, group.nodeID] ++ args.asOSCArgArray    // "/s_new"
480         }
481         addToTailMsg { arg aGroup, args;
482                 // if aGroup is nil set to default group of server specified when basicNew was called
483                 group = (aGroup ? server.defaultGroup);
484                 ^[9, defName, nodeID, 1, group.nodeID] ++ args.asOSCArgArray // "/s_new"
485         }
486         addAfterMsg {  arg aNode, args;
487                 group = aNode.group;
488                 ^[9, defName, nodeID, 3, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
489         }
490         addBeforeMsg {  arg aNode, args;
491                 group = aNode.group;
492                 ^[9, defName, nodeID, 2, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
493         }
494         addReplaceMsg { arg nodeToReplace, args;
495                 group = nodeToReplace.group;
496                 ^[9, defName, nodeID, 4, nodeToReplace.nodeID] ++ args.asOSCArgArray // "/s_new"
497         }
499         // nodeID -1
500         *grain { arg defName, args, target, addAction=\addToHead;
501                 var server;
502                 target = target.asTarget;
503                 server = target.server;
504                 server.sendMsg(9, defName.asDefName, -1, addActions[addAction], target.nodeID,
505                         *(args.asOSCArgArray)); //"/s_new"
506                 ^nil;
507         }
509         get { arg index, action;
510                 OSCpathResponder(server.addr,['/n_set',nodeID,index],{ arg time, r, msg;
511                         action.value(msg.at(3)); r.remove }).add;
512                 server.sendMsg(44, nodeID, index);      //"/s_get"
513         }
514         getMsg { arg index;
515                 ^[44, nodeID, index];   //"/s_get"
516         }
518         getn { arg index, count, action;
519                 OSCpathResponder(server.addr,['/n_setn',nodeID,index],{arg time, r, msg;
520                         action.value(msg.copyToEnd(4)); r.remove } ).add;
521                 server.sendMsg(45, nodeID, index, count); //"/s_getn"
522         }
523         getnMsg { arg index, count;
524                 ^[45, nodeID, index, count]; //"/s_getn"
525         }
527         printOn { arg stream; stream << this.class.name << "(" <<< defName << " : " << nodeID <<")" }
530 RootNode : Group {
532         classvar <roots;
534         *new { arg server;
535                 server = server ? Server.default;
536                 ^(roots.at(server.name) ?? {
537                         ^super.basicNew(server, 0).rninit
538                 })
539         }
540         rninit {
541                 roots.put(server.name, this);
542                 isPlaying = isRunning = true;
543                 group = this; // self
544         }
545         *initClass {  roots = IdentityDictionary.new; }
547         run { "run has no effect on RootNode".warn; }
548         free { "free has no effect on RootNode".warn; }
549         moveBefore { "moveBefore has no effect on RootNode".warn; }
550         moveAfter { "moveAfter has no effect on RootNode".warn; }
551         moveToHead { "moveToHead has no effect on RootNode".warn; }
552         moveToTail{ "moveToTail has no effect on RootNode".warn; }
554         *freeAll {
555                 roots.do({ arg rn; rn.freeAll })
556         }
559 ParGroup : AbstractGroup {
560         *creationCmd { ^63 }    //"/p_new"