scide: LookupDialog - redo lookup on classes after partial lookup
[supercollider.git] / SCClassLibrary / Common / Control / Node.sc
blob6179abd8ec8dad936c6eda3ed412d3625bb85dbe
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         }
83         set { arg ... args;
84                 server.sendMsg(15, nodeID, *(args.asOSCArgArray));  //"/n_set"
85         }
87         setMsg { arg ... args;
88 //              ^[15, nodeID] ++ args.unbubble.asControlInput;
89                 ^[15, nodeID] ++ args.asOSCArgArray;     //"/n_set"
90         }
92         setn { arg ... args;
93                 server.sendMsg(*this.setnMsg(*args));
94         }
96         *setnMsgArgs{ arg ... args;
97                 var nargs=List.new;
98                 args = args.asControlInput;
99                 args.pairsDo { arg control, moreVals;
100                         if(moreVals.isArray,{
101                                 nargs.addAll([control, moreVals.size]++ moreVals);
102                         },{
103                                 nargs.addAll([control, 1, moreVals]);
104                         });
105                 };
106                 ^nargs;
107         }
109         setnMsg { arg ... args;
110                 ^[16, nodeID] ++ Node.setnMsgArgs(*args);
111                 // "n_setn"
112         }
114         fill { arg controlName, numControls, value ... args;
115                 server.sendMsg(17, nodeID, controlName, numControls, value, *(args.asControlInput));//"n_fill"
116         }
118         fillMsg { arg controlName, numControls, value ... args;
119                 ^[17, nodeID, controlName, numControls, value] ++ args.asControlInput; //"n_fill"
120         }
122         release { arg releaseTime;
123                 server.sendMsg(*this.releaseMsg(releaseTime))
124         }
125         releaseMsg { arg releaseTime;
126                 //assumes a control called 'gate' in the synth
127                 if(releaseTime.isNil, {
128                         releaseTime = 0.0;
129                 },{
130                         releaseTime = -1.0 - releaseTime;
131                 });
132                 ^[15, nodeID, \gate, releaseTime]
133         }
134         trace {
135                 server.sendMsg(10, nodeID);//"/n_trace"
136         }
137         query {
138                 OSCFunc({ arg msg;
139                         var cmd,argnodeID,parent,prev,next,isGroup,head,tail;
140                         # cmd,argnodeID,parent,prev,next,isGroup,head,tail = msg;
141                         // assuming its me ... if(nodeID == argnodeID)
142                         Post << if(isGroup == 1, "Group:" , "Synth:") << nodeID << Char.nl
143                                 << "parent   : " << parent << Char.nl
144                                 << "prev : " << prev << Char.nl
145                                 << "next  :" << next << Char.nl;
146                         if(isGroup==1,{
147                                 Post << "head :" << head << Char.nl
148                                  << "tail :" << tail << Char.nl << Char.nl;
149                         });
150                 }, '/n_info', server.addr).oneShot;
151                 server.sendMsg(46, nodeID)  //"/n_query"
152         }
153         register { arg assumePlaying=false;
154                 NodeWatcher.register(this, assumePlaying)
155         }
157         onFree {|func|
158                 var f = {|n,m|
159                         if(m==\n_end) {
160                                 func.value(this,m);
161                                 this.removeDependant(f);
162                         }
163                 };
164                 this.register;
165                 this.addDependant(f);
166         }
167         waitForFree {
168                 var c = Condition.new;
169                 this.onFree({c.unhang});
170                 c.hang;
171         }
173         moveBefore { arg aNode;
174                 group = aNode.group;
175                 server.sendMsg(18, nodeID, aNode.nodeID); //"/n_before"
176         }
177         moveAfter { arg aNode;
178                 group = aNode.group;
179                 server.sendMsg(19, nodeID, aNode.nodeID); //"/n_after"
180         }
181         moveToHead { arg aGroup;
182                 (aGroup ? server.defaultGroup).moveNodeToHead(this);
183         }
184         moveToTail { arg aGroup;
185                 (aGroup ? server.defaultGroup).moveNodeToTail(this);
186         }
188         // message creating methods
190         moveBeforeMsg { arg aNode;
191                 group = aNode.group;
192                 ^[18, nodeID, aNode.nodeID];//"/n_before"
193         }
194         moveAfterMsg { arg aNode;
195                 group = aNode.group;
196                 ^[19, nodeID, aNode.nodeID]; //"/n_after"
197         }
198         moveToHeadMsg { arg aGroup;
199                 ^(aGroup ? server.defaultGroup).moveNodeToHeadMsg(this);
200         }
201         moveToTailMsg { arg aGroup;
202                 ^(aGroup ? server.defaultGroup).moveNodeToTailMsg(this);
203         }
205         *orderNodesMsg { arg nodes;
206                 var msg = [18]; // "/n_after"
207                 nodes.doAdjacentPairs { |first, toMoveAfter|
208                         msg = msg.add(toMoveAfter.nodeID);
209                         msg = msg.add(first.nodeID);
210                 };
211                 ^msg
212         }
214         == { arg that;
215                 ^this.compareObject(that, #[\nodeID, \server])
216         }
218         hash {
219                 ^this.instVarHash(#[\nodeID, \server])
220         }
222         printOn { arg stream; stream << this.class.name << "(" << nodeID <<")" }
223         asUGenInput { Error("should not use a % inside a SynthDef".format(this.class.name)).throw }
224         asControlInput { ^this.nodeID }
228 // common base for Group and ParGroup classes
229 AbstractGroup : Node {
231         /** immediately sends **/
232         *new { arg target, addAction=\addToHead;
233                 var group, server, addNum, inTarget;
234                 inTarget = target.asTarget;
235                 server = inTarget.server;
236                 group = this.basicNew(server);
237                 addNum = addActions[addAction];
238                 if((addNum < 2), { group.group = inTarget; }, { group.group = inTarget.group; });
239                 server.sendMsg(this.creationCmd, group.nodeID, addNum, inTarget.nodeID);
240                 ^group
241         }
242         newMsg { arg target, addAction = \addToHead;
243                 var addNum, inTarget;
244                 // if target is nil set to default group of server specified when basicNew was called
245                 inTarget = (target ? server.defaultGroup).asTarget;
246                 addNum = addActions[addAction];
247                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
248                 ^[this.class.creationCmd, nodeID, addNum, inTarget.nodeID]
249         }
251         // for bundling
252         addToHeadMsg { arg aGroup;
253                 // if aGroup is nil set to default group of server specified when basicNew was called
254                 group = (aGroup ? server.defaultGroup);
255                 ^[this.class.creationCmd, nodeID, 0, group.nodeID]
256         }
257         addToTailMsg { arg aGroup;
258                 // if aGroup is nil set to default group of server specified when basicNew was called
259                 group = (aGroup ? server.defaultGroup);
260                 ^[this.class.creationCmd, nodeID, 1, group.nodeID]
261         }
262         addAfterMsg {  arg aNode;
263                 group = aNode.group;
264                 ^[this.class.creationCmd, nodeID, 3, aNode.nodeID]
265         }
266         addBeforeMsg {  arg aNode;
267                 group = aNode.group;
268                 ^[this.class.creationCmd, nodeID, 2, aNode.nodeID]
269         }
270         addReplaceMsg { arg nodeToReplace;
271                 group = nodeToReplace.group;
272                 ^[this.class.creationCmd, nodeID, 4, nodeToReplace.nodeID]
273         }
276         *after { arg aNode;    ^this.new(aNode, \addAfter) }
277         *before {  arg aNode;   ^this.new(aNode, \addBefore) }
278         *head { arg aGroup;     ^this.new(aGroup, \addToHead) }
279         *tail { arg aGroup;     ^this.new(aGroup, \addToTail) }
280         *replace { arg nodeToReplace; ^this.new(nodeToReplace, \addReplace) }
282     // move Nodes to this group
283     moveNodeToHead { arg aNode;
284         aNode.group = this;
285         server.sendMsg(22, nodeID, aNode.nodeID); //"/g_head"
286     }
287     moveNodeToTail { arg aNode;
288         aNode.group = this;
289         server.sendMsg(23, nodeID, aNode.nodeID); //"/g_tail"
290     }
291     moveNodeToHeadMsg { arg aNode;
292         aNode.group = this;
293         ^[22, nodeID, aNode.nodeID];            //"/g_head"
294     }
295     moveNodeToTailMsg { arg aNode;
296         aNode.group = this;
297         ^[23, nodeID, aNode.nodeID];            //g_tail
298     }
300         freeAll {
301                 // free my children, but this node is still playing
302                 server.sendMsg(24, nodeID); //"/g_freeAll"
303         }
304         freeAllMsg {
305                 // free my children, but this node is still playing
306                 ^[24, nodeID]; //"/g_freeAll"
307         }
308         deepFree {
309                 server.sendMsg(50, nodeID) //"/g_deepFree"
310         }
311         deepFreeMsg {
312                 ^[50, nodeID] //"/g_deepFree"
313         }
315         // Introspection
316         dumpTree { arg postControls = false;
317                 server.sendMsg("/g_dumpTree", nodeID, postControls.binaryValue)
318         }
320         queryTree { //|action|
321                 var resp, done = false;
322                 resp = OSCFunc({ arg msg;
323                         var i = 2, tabs = 0, printControls = false, dumpFunc;
324                         if(msg[1] != 0, {printControls = true});
325                         ("NODE TREE Group" + msg[2]).postln;
326                         if(msg[3] > 0, {
327                                 dumpFunc = {|numChildren|
328                                         var j;
329                                         tabs = tabs + 1;
330                                         numChildren.do({
331                                                 if(msg[i + 1] >=0, {i = i + 2}, {
332                                                         i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
333                                                 });
334                                                 tabs.do({ "   ".post });
335                                                 msg[i].post; // nodeID
336                                                 if(msg[i + 1] >=0, {
337                                                         " group".postln;
338                                                         if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
339                                                         }, {
340                                                                 (" " ++ msg[i + 2]).postln; // defname
341                                                                 if(printControls, {
342                                                                         if(msg[i + 3] > 0, {
343                                                                                 " ".post;
344                                                                                 tabs.do({ "   ".post });
345                                                                         });
346                                                                         j = 0;
347                                                                         msg[i + 3].do({
348                                                                                 " ".post;
349                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
350                                                                                         (msg[i + 4 + j] ++ ": ").post;
351                                                                                 });
352                                                                                 msg[i + 5 + j].post;
353                                                                                 j = j + 2;
354                                                                         });
355                                                                         "\n".post;
356                                                                 });
357                                                         });
358                                         });
359                                         tabs = tabs - 1;
360                                 };
361                                 dumpFunc.value(msg[3]);
362                         });
364                         //                              action.value(msg);
365                         done = true;
366                 }, '/g_queryTree.reply', server.addr).oneShot;
367                 server.sendMsg("/g_queryTree", nodeID);
368                 SystemClock.sched(3, {
369                         done.not.if({
370                                 resp.free;
371                                 "Server failed to respond to Group:queryTree!".warn;
372                         });
373                 });
374         }
376         *creationCmd { ^this.subclassMustImplementThisMethod }
378 //      queryTree { |action|
379 //              var resp, done = false;
380 //              resp = OSCresponderNode(server.addr, '/g_queryTree.reply', { arg time, responder, msg;
381 //                              action.value(msg);
382 //                              done = true;
383 //                      }).add.removeWhenDone;
384 //              server.sendMsg("/g_queryTree", nodeID);
385 //              SystemClock.sched(3, {
386 //                      done.not.if({
387 //                              resp.remove;
388 //                              "Server failed to respond to Group:queryTree!".warn;
389 //                      });
390 //              });
391 //      }
394 Group : AbstractGroup {
395         *creationCmd { ^21 }    //"/g_new"
398 Synth : Node {
400         var <>defName;
402         /** immediately sends **/
403         *new { arg defName, args, target, addAction=\addToHead;
404                 var synth, server, addNum, inTarget;
405                 inTarget = target.asTarget;
406                 server = inTarget.server;
407                 addNum = addActions[addAction];
408                 synth = this.basicNew(defName, server);
410                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
411 //              server.sendMsg(59, //"s_newargs"
412 //                      defName, synth.nodeID, addNum, inTarget.nodeID,
413 //                      *Node.setnMsgArgs(*args));
414                 server.sendMsg(9, //"s_new"
415                         defName, synth.nodeID, addNum, inTarget.nodeID,
416                         *(args.asOSCArgArray)
417                 );
418                 ^synth
419         }
420         *newPaused { arg defName, args, target, addAction=\addToHead;
421                 var synth, server, addNum, inTarget;
422                 inTarget = target.asTarget;
423                 server = inTarget.server;
424                 addNum = addActions[addAction];
425                 synth = this.basicNew(defName, server);
426                 if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
427                 server.sendBundle(nil, [9, defName, synth.nodeID, addNum, inTarget.nodeID] ++
428                         args.asOSCArgArray, [12, synth.nodeID, 0]); // "s_new" + "/n_run"
429                 ^synth
430         }
431                 /** does not send       (used for bundling) **/
432         *basicNew { arg defName, server, nodeID;
433                 ^super.basicNew(server, nodeID).defName_(defName.asDefName)
434         }
436         newMsg { arg target, args, addAction = \addToHead;
437                 var addNum, inTarget;
438                 addNum = addActions[addAction];
439                 // if target is nil set to default group of server specified when basicNew was called
440                 inTarget = (target ? server.defaultGroup).asTarget;
441                 (addNum < 2).if({ group = inTarget; }, { group = inTarget.group; });
442                 ^[9, defName, nodeID, addNum, inTarget.nodeID] ++ args.asOSCArgArray; //"/s_new"
443         }
444         *after { arg aNode, defName, args;
445                 ^this.new(defName, args, aNode, \addAfter);
446         }
447         *before {  arg aNode, defName, args;
448                 ^this.new(defName, args, aNode, \addBefore);
449         }
450         *head { arg aGroup, defName, args;
451                 ^this.new(defName, args, aGroup, \addToHead);
452         }
453         *tail { arg aGroup, defName, args;
454                 ^this.new(defName, args, aGroup, \addToTail);
455         }
456         *replace { arg nodeToReplace, defName, args;
457                 ^this.new(defName, args, nodeToReplace, \addReplace)
458         }
459         // for bundling
460         addToHeadMsg { arg aGroup, args;
461                 // if aGroup is nil set to default group of server specified when basicNew was called
462                 group = (aGroup ? server.defaultGroup);
463                 ^[9, defName, nodeID, 0, group.nodeID] ++ args.asOSCArgArray    // "/s_new"
464         }
465         addToTailMsg { arg aGroup, args;
466                 // if aGroup is nil set to default group of server specified when basicNew was called
467                 group = (aGroup ? server.defaultGroup);
468                 ^[9, defName, nodeID, 1, group.nodeID] ++ args.asOSCArgArray // "/s_new"
469         }
470         addAfterMsg {  arg aNode, args;
471                 group = aNode.group;
472                 ^[9, defName, nodeID, 3, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
473         }
474         addBeforeMsg {  arg aNode, args;
475                 group = aNode.group;
476                 ^[9, defName, nodeID, 2, aNode.nodeID] ++ args.asOSCArgArray // "/s_new"
477         }
478         addReplaceMsg { arg nodeToReplace, args;
479                 group = nodeToReplace.group;
480                 ^[9, defName, nodeID, 4, nodeToReplace.nodeID] ++ args.asOSCArgArray // "/s_new"
481         }
483         // nodeID -1
484         *grain { arg defName, args, target, addAction=\addToHead;
485                 var server;
486                 target = target.asTarget;
487                 server = target.server;
488                 server.sendMsg(9, defName.asDefName, -1, addActions[addAction], target.nodeID,
489                         *(args.asOSCArgArray)); //"/s_new"
490                 ^nil;
491         }
493         get { arg index, action;
494                 OSCpathResponder(server.addr,['/n_set',nodeID,index],{ arg time, r, msg;
495                         action.value(msg.at(3)); r.remove }).add;
496                 server.sendMsg(44, nodeID, index);      //"/s_get"
497         }
498         
499         getMsg { arg index;
500                 ^[44, nodeID, index];   //"/s_get"
501         }
503         getn { arg index, count, action;
504                 OSCpathResponder(server.addr,['/n_setn',nodeID,index],{arg time, r, msg;
505                         action.value(msg.copyToEnd(4)); r.remove } ).add;
506                 server.sendMsg(45, nodeID, index, count); //"/s_getn"
507         }
509         getnMsg { arg index, count;
510                 ^[45, nodeID, index, count]; //"/s_getn"
511         }
513         seti { arg ... args; // args are [key, index, value, key, index, value ...]
514                 var msg = Array.new(args.size div: 3 * 2);
515                 var synthDesc = SynthDescLib.at(defName.asSymbol);
516                 if(synthDesc.isNil) {
517                         "message seti failed, because SynthDef % was not added.".format(defName).warn; 
518                         ^this
519                 };
520                 forBy(0, args.size-1, 3, { |i|
521                         var key = args[i], offset = args[i+1], value = args[i+2];
522                         var controlName = synthDesc.controlDict[key];
523                         if(controlName.notNil and: { offset < controlName.numChannels }) {
524                                 msg.add(controlName.index+offset);
525                                 if(value.isArray) {
526                                         msg.add(value.keep(controlName.numChannels - offset))
527                                 } {
528                                         msg.add(value)
529                                 }
530                         }
531                 });
532                 server.sendMsg("/n_set", nodeID, *msg.asOSCArgArray);
533         }
535         printOn { arg stream; stream << this.class.name << "(" <<< defName << " : " << nodeID <<")" }
538 RootNode : Group {
540         classvar <roots;
542         *new { arg server;
543                 server = server ? Server.default;
544                 ^(roots.at(server.name) ?? {
545                         ^super.basicNew(server, 0).rninit
546                 })
547         }
548         rninit {
549                 roots.put(server.name, this);
550                 isPlaying = isRunning = true;
551                 group = this; // self
552         }
553         *initClass {  roots = IdentityDictionary.new; }
555         run { "run has no effect on RootNode".warn; }
556         free { "free has no effect on RootNode".warn; }
557         moveBefore { "moveBefore has no effect on RootNode".warn; }
558         moveAfter { "moveAfter has no effect on RootNode".warn; }
559         moveToHead { "moveToHead has no effect on RootNode".warn; }
560         moveToTail{ "moveToTail has no effect on RootNode".warn; }
562         *freeAll {
563                 roots.do({ arg rn; rn.freeAll })
564         }
567 ParGroup : AbstractGroup {
568         *creationCmd { ^63 }    //"/p_new"