scide: LookupDialog - redo lookup on classes after partial lookup
[supercollider.git] / SCClassLibrary / Common / Control / Server.sc
blobea05e233763f3732fe767c28617c9c50be94eddd
1 ServerOptions
3         // order of variables is important here. Only add new instance variables to the end.
4         var <numAudioBusChannels=128;
5         var <>numControlBusChannels=4096;
6         var <numInputBusChannels=8;
7         var <numOutputBusChannels=8;
8         var numBuffers=1026;
10         var <>maxNodes=1024;
11         var <>maxSynthDefs=1024;
12         var <>protocol = \udp;
13         var <>blockSize = 64;
14         var <>hardwareBufferSize = nil;
16         var <>memSize = 8192;
17         var <>numRGens = 64;
18         var <>numWireBufs = 64;
20         var <>sampleRate = nil;
21         var <>loadDefs = true;
23         var <>inputStreamsEnabled;
24         var <>outputStreamsEnabled;
26         var <>inDevice = nil;
27         var <>outDevice = nil;
29         var <>verbosity = 0;
30         var <>zeroConf = false; // Whether server publishes port to Bonjour, etc.
32         var <>restrictedPath = nil;
34         var <>initialNodeID = 1000;
35         var <>remoteControlVolume = false;
37         var <>memoryLocking = false;
38         var <>threads = nil; // for supernova
40         var <numPrivateAudioBusChannels=112;
42         device {
43                 ^if(inDevice == outDevice)
44                 {
45                         inDevice
46                 }
47                 {
48                         [inDevice, outDevice]
49                 }
50         }
52         device_ {
53                 |dev|
54                 inDevice = outDevice = dev;
55         }
57         // max logins
58         // session-password
60         // prevent buffer conflicts in Server-prepareForRecord and Server-scope by
61         // ensuring reserved buffers
63         numBuffers {
64                 ^numBuffers - 2
65         }
67         numBuffers_ { | argNumBuffers |
68                 numBuffers = argNumBuffers + 2
69         }
71         asOptionsString { | port = 57110 |
72                 var o;
73                 o = if (protocol == \tcp, " -t ", " -u ");
74                 o = o ++ port;
76             o = o ++ " -a " ++ (numPrivateAudioBusChannels + numInputBusChannels + numOutputBusChannels) ;
78                 if (numControlBusChannels != 4096, {
79                         o = o ++ " -c " ++ numControlBusChannels;
80                 });
81                 if (numInputBusChannels != 8, {
82                         o = o ++ " -i " ++ numInputBusChannels;
83                 });
84                 if (numOutputBusChannels != 8, {
85                         o = o ++ " -o " ++ numOutputBusChannels;
86                 });
87                 if (numBuffers != 1024, {
88                         o = o ++ " -b " ++ numBuffers;
89                 });
90                 if (maxNodes != 1024, {
91                         o = o ++ " -n " ++ maxNodes;
92                 });
93                 if (maxSynthDefs != 1024, {
94                         o = o ++ " -d " ++ maxSynthDefs;
95                 });
96                 if (blockSize != 64, {
97                         o = o ++ " -z " ++ blockSize;
98                 });
99                 if (hardwareBufferSize.notNil, {
100                         o = o ++ " -Z " ++ hardwareBufferSize;
101                 });
102                 if (memSize != 8192, {
103                         o = o ++ " -m " ++ memSize;
104                 });
105                 if (numRGens != 64, {
106                         o = o ++ " -r " ++ numRGens;
107                 });
108                 if (numWireBufs != 64, {
109                         o = o ++ " -w " ++ numWireBufs;
110                 });
111                 if (sampleRate.notNil, {
112                         o = o ++ " -S " ++ sampleRate;
113                 });
114                 if (loadDefs.not, {
115                         o = o ++ " -D 0";
116                 });
117                 if (inputStreamsEnabled.notNil, {
118                         o = o ++ " -I " ++ inputStreamsEnabled ;
119                 });
120                 if (outputStreamsEnabled.notNil, {
121                         o = o ++ " -O " ++ outputStreamsEnabled ;
122                 });
123                 if ((thisProcess.platform.name!=\osx) or: {inDevice == outDevice})
124                 {
125                         if (inDevice.notNil,
126                         {
127                                 o = o ++ " -H %".format(inDevice.quote);
128                         });
129                 }
130                 {
131                         o = o ++ " -H % %".format(inDevice.asString.quote, outDevice.asString.quote);
132                 };
133                 if (verbosity != 0, {
134                         o = o ++ " -v " ++ verbosity;
135                 });
136                 if (zeroConf.not, {
137                         o = o ++ " -R 0";
138                 });
139                 if (restrictedPath.notNil, {
140                         o = o ++ " -P " ++ restrictedPath;
141                 });
142                 if (memoryLocking, {
143                         o = o ++ " -L";
144                 });
145                 if (threads.notNil, {
146                         if (Server.program.asString.endsWith("supernova")) {
147                                 o = o ++ " -T " ++ threads;
148                         }
149                 });
150                 ^o
151         }
153         firstPrivateBus { // after the outs and ins
154                 ^numOutputBusChannels + numInputBusChannels
155         }
157         bootInProcess {
158                 _BootInProcessServer
159                 ^this.primitiveFailed
160         }
162         numPrivateAudioBusChannels_ {arg numChannels = 112;
163                 numPrivateAudioBusChannels = numChannels;
164                 this.recalcChannels;
165         }
167         numAudioBusChannels_ {arg numChannels=128;
168                 numAudioBusChannels = numChannels;
169                 numPrivateAudioBusChannels = numAudioBusChannels - numInputBusChannels - numOutputBusChannels;
170         }
172         numInputBusChannels_ {arg numChannels=8;
173                 numInputBusChannels = numChannels;
174                 this.recalcChannels;
175         }
177         numOutputBusChannels_ {arg numChannels=8;
178                 numOutputBusChannels = numChannels;
179                 this.recalcChannels;
180         }
182         recalcChannels {
183                 numAudioBusChannels = numPrivateAudioBusChannels + numInputBusChannels + numOutputBusChannels;
184         }
186         *prListDevices {
187                 arg in, out;
188                 _ListAudioDevices
189                 ^this.primitiveFailed
190         }
192         *devices {
193                 ^this.prListDevices(1, 1);
194         }
196         *inDevices {
197                 ^this.prListDevices(1, 0);
198         }
200         *outDevices {
201                 ^this.prListDevices(0, 1);
202         }
205 ServerShmInterface {
206         // order matters!
207         var ptr, finalizer;
209         *new {|port|
210                 ^super.new.connect(port)
211         }
213         copy {
214                 // never ever copy! will cause duplicate calls to the finalizer!
215                 ^this
216         }
218         connect {
219                 _ServerShmInterface_connectSharedMem
220                 ^this.primitiveFailed
221         }
223         disconnect {
224                 _ServerShmInterface_disconnectSharedMem
225                 ^this.primitiveFailed
226         }
228         getControlBusValue {
229                 _ServerShmInterface_getControlBusValue
230                 ^this.primitiveFailed
231         }
233         getControlBusValues {
234                 _ServerShmInterface_getControlBusValues
235                 ^this.primitiveFailed
236         }
238         setControlBusValue {
239                 _ServerShmInterface_setControlBusValue
240                 ^this.primitiveFailed
241         }
243         setControlBusValues {
244                 _ServerShmInterface_setControlBusValues
245                 ^this.primitiveFailed
246         }
249 Server {
250         classvar <>local, <>internal, <default, <>named, <>set, <>program, <>sync_s = true;
252         var <name, <>addr, <clientID=0;
253         var <isLocal, <inProcess, <>sendQuit, <>remoteControlled;
254         var <serverRunning = false, <serverBooting = false, bootNotifyFirst = false;
255         var <>options, <>latency = 0.2, <dumpMode = 0, <notify = true, <notified=false;
256         var <nodeAllocator;
257         var <controlBusAllocator;
258         var <audioBusAllocator;
259         var <bufferAllocator;
260         var <scopeBufferAllocator;
261         var <syncThread, <syncTasks;
263         var <numUGens=0, <numSynths=0, <numGroups=0, <numSynthDefs=0;
264         var <avgCPU, <peakCPU;
265         var <sampleRate, <actualSampleRate;
267         var alive = false, booting = false, aliveThread, <>aliveThreadPeriod = 0.7, statusWatcher;
268         var <>tree;
270         var <window, <>scopeWindow;
271         var <emacsbuf;
272         var recordBuf, <recordNode, <>recHeaderFormat="aiff", <>recSampleFormat="float";
273         var <>recChannels=2;
275         var <volume;
277         var <pid;
278         var serverInterface;
280         *default_ { |server|
281                 default = server; // sync with s?
282                 if (sync_s, { thisProcess.interpreter.s = server });
283                 this.all.do(_.changed(\default));
284         }
286         *new { arg name, addr, options, clientID=0;
287                 ^super.new.init(name, addr, options, clientID)
288         }
290         *all { ^set }
291         *all_ { arg dict; set = dict }
293         init { arg argName, argAddr, argOptions, argClientID;
294                 name = argName.asSymbol;
295                 addr = argAddr;
296                 clientID = argClientID;
297                 options = argOptions ? ServerOptions.new;
298                 if (addr.isNil, { addr = NetAddr("127.0.0.1", 57110) });
299                 inProcess = addr.addr == 0;
300                 isLocal = inProcess || { addr.addr == 2130706433 };
301                 remoteControlled = isLocal;
302                 serverRunning = false;
303                 named.put(name, this);
304                 set.add(this);
305                 this.newAllocators;
306                 Server.changed(\serverAdded, this);
307                 volume = Volume.new(server: this, persist: true);
308         }
310         initTree {
311                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
312                 this.sendMsg("/g_new", 1, 0, 0);
313                 tree.value(this);
314                 ServerTree.run(this);
315         }
316         newAllocators {
317                 this.newNodeAllocators;
318                 this.newBusAllocators;
319                 this.newBufferAllocators;
320                 this.newScopeBufferAllocators;
321                 NotificationCenter.notify(this, \newAllocators);
322         }
324         newNodeAllocators {
325                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
326         }
328         newBusAllocators {
329                 controlBusAllocator = ContiguousBlockAllocator.new(options.numControlBusChannels);
330                 audioBusAllocator = ContiguousBlockAllocator.new(options.numAudioBusChannels,
331                         options.firstPrivateBus);
332         }
334         newBufferAllocators {
335                 bufferAllocator = ContiguousBlockAllocator.new(options.numBuffers);
336         }
338         newScopeBufferAllocators {
339                 if (isLocal) {
340                         scopeBufferAllocator = StackNumberAllocator.new(0, 127);
341                 }
342         }
344         nextNodeID {
345                 ^nodeAllocator.alloc
346         }
347         nextPermNodeID {
348                 ^nodeAllocator.allocPerm
349         }
350         freePermNodeID { |id|
351                 ^nodeAllocator.freePerm(id)
352         }
354         *initClass {
355                 Class.initClassTree(ServerOptions);
356                 Class.initClassTree(NotificationCenter);
357                 named = IdentityDictionary.new;
358                 set = Set.new;
359                 default = local = Server.new(\localhost, NetAddr("127.0.0.1", 57110));
360                 internal = Server.new(\internal, NetAddr.new);
361         }
363         *fromName { arg name;
364                 ^Server.named[name] ?? {
365                         Server(name, NetAddr.new("127.0.0.1", 57110), ServerOptions.new, 0)
366                 }
367         }
369         // bundling support added
370         sendMsg { arg ... msg;
371                 addr.sendMsg(*msg);
372         }
373         sendBundle { arg time ... msgs;
374                 addr.sendBundle(time, *msgs)
375         }
377         sendRaw { arg rawArray;
378                 addr.sendRaw(rawArray);
379         }
381         sendMsgSync { arg condition ... args;
382                 var cmdName, resp;
383                 if (condition.isNil) { condition = Condition.new };
384                 cmdName = args[0].asString;
385                 if (cmdName[0] != $/) { cmdName = cmdName.insert(0, $/) };
386                 resp = OSCFunc({|msg|
387                         if (msg[1].asString == cmdName) {
388                                 resp.free;
389                                 condition.test = true;
390                                 condition.signal;
391                         };
392                 }, '/done', addr);
393                 condition.test = false;
394                 addr.sendBundle(nil, args);
395                 condition.wait;
396         }
398         sync { arg condition, bundles, latency; // array of bundles that cause async action
399                 addr.sync(condition, bundles, latency)
400         }
402         schedSync { arg func;
403                 syncTasks = syncTasks.add(func);
404                 if(syncThread.isNil) {
405                         syncThread = Routine.run {
406                                 var c; c = Condition.new;
407                                 while { syncTasks.notEmpty } { syncTasks.removeAt(0).value(c) };
408                                 syncThread = nil;
409                         };
410                 };
412         }
414         // bundling support added
415         listSendMsg { arg msg;
416                 addr.sendMsg(*msg);
417         }
418         listSendBundle { arg time, msgs;
419                 addr.sendBundle(time, *(msgs.asArray));
420         }
422         // load from disk locally, send remote
423         sendSynthDef { arg name, dir;
424                 var file, buffer;
425                 dir = dir ? SynthDef.synthDefDir;
426                 file = File(dir ++ name ++ ".scsyndef","r");
427                 if (file.isNil, { ^nil });
428                 protect {
429                         buffer = Int8Array.newClear(file.length);
430                         file.read(buffer);
431                 }{
432                         file.close;
433                 };
434                 this.sendMsg("/d_recv", buffer);
435         }
436         // tell server to load from disk
437         loadSynthDef { arg name, completionMsg, dir;
438                 dir = dir ? SynthDef.synthDefDir;
439                 this.listSendMsg(
440                         ["/d_load", dir ++ name ++ ".scsyndef", completionMsg ]
441                 )
442         }
443         //loadDir
444         loadDirectory { arg dir, completionMsg;
445                 this.listSendMsg(["/d_loadDir", dir, completionMsg]);
446         }
448         serverRunning_ { arg val;
449                 if(addr.hasBundle) {
450                         { this.changed(\bundling); }.defer;
451                 } {
452                         if (val != serverRunning) {
453                                 if(thisProcess.platform.isSleeping.not) {
454                                         serverRunning = val;
455                                         if (serverRunning.not) {
457                                                 ServerQuit.run(this);
459                                                 if (serverInterface.notNil) {
460                                                         serverInterface.disconnect;
461                                                         serverInterface = nil;
462                                                 };
464                                                 AppClock.sched(5.0, {
465                                                         // still down after 5 seconds, assume server is really dead
466                                                         // if you explicitly shut down the server then newAllocators
467                                                         // and the \newAllocators notification will happen immediately
468                                                         if(serverRunning.not) {
469                                                                 NotificationCenter.notify(this,\didQuit);
470                                                         };
471                                                         recordNode = nil;
472                                                         if(this.isLocal.not){
473                                                                 notified = false;
474                                                         }
475                                                 })
477                                         }{
478                                                 ServerBoot.run(this);
479                                         };
480                                         { this.changed(\serverRunning); }.defer;
481                                 }
482                         }
483                 };
484         }
486         wait { arg responseName;
487                 var routine;
488                 routine = thisThread;
489                 OSCFunc({
490                         routine.resume(true);
491                 }, responseName, addr).oneShot;
492         }
494         waitForBoot { arg onComplete, limit=100, onFailure;
495                 // onFailure.true: why is this necessary?
496                 // this.boot also calls doWhenBooted.
497                 // doWhenBooted prints the normal boot failure message.
498                 // if the server fails to boot, the failure error gets posted TWICE.
499                 // So, we suppress one of them.
500                 if(serverRunning.not) { this.boot(onFailure: true) };
501                 this.doWhenBooted(onComplete, limit, onFailure);
502         }
504         doWhenBooted { arg onComplete, limit=100, onFailure;
505                 var mBootNotifyFirst = bootNotifyFirst, postError = true;
506                 bootNotifyFirst = false;
508                 ^Routine({
509                         while({
510                                 ((serverRunning.not
511                                   or: (serverBooting and: mBootNotifyFirst.not))
512                                  and: {(limit = limit - 1) > 0})
513                                 and: { pid.tryPerform(\pidRunning) == true }
514                         },{
515                                 0.2.wait;
516                         });
518                         if(serverRunning.not,{
519                                 if(onFailure.notNil) {
520                                         postError = (onFailure.value == false);
521                                 };
522                                 if(postError) {
523                                         "server failed to start".error;
524                                         "For advice: [http://supercollider.sf.net/wiki/index.php/ERROR:_server_failed_to_start]".postln;
525                                 };
526                                 serverBooting = false;
527                                 this.changed(\serverRunning);
528                         }, onComplete);
529                 }).play(AppClock);
530         }
532         bootSync { arg condition;
533                 condition ?? { condition = Condition.new };
534                 condition.test = false;
535                 this.waitForBoot({
536                         // Setting func to true indicates that our condition has become true and we can go when signaled.
537                         condition.test = true;
538                         condition.signal
539                 });
540                 condition.wait;
541         }
543         ping { arg n=1, wait=0.1, func;
544                 var result=0, pingFunc;
545                 if(serverRunning.not) { "server not running".postln; ^this };
546                 pingFunc = {
547                         Routine.run {
548                                         var t, dt;
549                                         t = Main.elapsedTime;
550                                         this.sync;
551                                         dt = Main.elapsedTime - t;
552                                         ("measured latency:" + dt + "s").postln;
553                                         result = max(result, dt);
554                                         n = n - 1;
555                                         if(n > 0) {
556                                                 SystemClock.sched(wait, {pingFunc.value; nil })
557                                         } {
558                                                 ("maximum determined latency of" + name + ":" + result + "s").postln;
559                                                 func.value(result)
560                                         }
561                                 };
562                 };
563                 pingFunc.value;
564         }
566         addStatusWatcher {
567                 if(statusWatcher.isNil) {
568                         statusWatcher =
569                                 OSCFunc({ arg msg;
570                                         var cmd, one;
571                                         if(notify){
572                                                 if(notified.not){
573                                                         this.sendNotifyRequest;
574                                                         "Receiving notification messages from server %\n".postf(this.name);
575                                                 }
576                                         };
577                                         alive = true;
578                                         #cmd, one, numUGens, numSynths, numGroups, numSynthDefs,
579                                                 avgCPU, peakCPU, sampleRate, actualSampleRate = msg;
580                                         {
581                                                 this.serverRunning_(true);
582                                                 this.changed(\counts);
583                                                 nil // no resched
584                                         }.defer;
585                                 }, '/status.reply', addr).fix;
586                 } {
587                         statusWatcher.enable;
588                 };
589         }
591         cachedBuffersDo { |func| Buffer.cachedBuffersDo(this, func) }
592         cachedBufferAt { |bufnum| ^Buffer.cachedBufferAt(this, bufnum) }
594         inputBus {
595                 ^Bus(\audio, this.options.numOutputBusChannels, this.options.numInputBusChannels, this);
596         }
598         outputBus {
599                 ^Bus(\audio, 0, this.options.numOutputBusChannels, this);
600         }
602         startAliveThread { arg delay=0.0;
603                 this.addStatusWatcher;
604                 ^aliveThread ?? {
605                         aliveThread = Routine({
606                                 // this thread polls the server to see if it is alive
607                                 delay.wait;
608                                 loop({
609                                         this.status;
610                                         aliveThreadPeriod.wait;
611                                         this.serverRunning = alive;
612                                         alive = false;
613                                 });
614                         });
615                         AppClock.play(aliveThread);
616                         aliveThread
617                 }
618         }
619         stopAliveThread {
620                 if( aliveThread.notNil, {
621                         aliveThread.stop;
622                         aliveThread = nil;
623                 });
624                 if( statusWatcher.notNil, {
625                         statusWatcher.free;
626                         statusWatcher = nil;
627                 });
628         }
629         aliveThreadIsRunning {
630                 ^aliveThread.notNil and: {aliveThread.isPlaying}
631         }
632         *resumeThreads {
633                 set.do({ arg server;
634                         server.stopAliveThread;
635                         server.startAliveThread(server.aliveThreadPeriod);
636                 });
637         }
639         boot { arg startAliveThread=true, recover=false, onFailure;
640                 var resp;
641                 if (serverRunning, { "server already running".inform; ^this });
642                 if (serverBooting, { "server already booting".inform; ^this });
644                 serverBooting = true;
645                 if(startAliveThread, { this.startAliveThread });
646                 if(recover) { this.newNodeAllocators } { this.newAllocators };
647                 bootNotifyFirst = true;
648                 this.doWhenBooted({
649                         serverBooting = false;
650                         if (sendQuit.isNil) {
651                                 sendQuit = this.inProcess or: {this.isLocal};
652                         };
654                         if (this.inProcess) {
655                                 serverInterface = ServerShmInterface(thisProcess.pid);
656                         } {
657                                 if (isLocal) {
658                                         serverInterface = ServerShmInterface(addr.port);
659                                 }
660                         };
662                         this.initTree;
663                 }, onFailure: onFailure ? false);
664                 if (remoteControlled.not, {
665                         "You will have to manually boot remote server.".inform;
666                 },{
667                         this.bootServerApp;
668                 });
669         }
671         bootServerApp {
672                 if (inProcess, {
673                         "booting internal".inform;
674                         this.bootInProcess;
675                         //alive = true;
676                         //this.serverRunning = true;
677                         pid = thisProcess.pid;
678                 },{
679                         if (serverInterface.notNil) {
680                                 serverInterface.disconnect;
681                                 serverInterface = nil;
682                         };
684                         pid = (program ++ options.asOptionsString(addr.port)).unixCmd;
685                         //unixCmd(program ++ options.asOptionsString(addr.port)).postln;
686                         ("booting " ++ addr.port.asString).inform;
687                 });
688         }
690         reboot { arg func; // func is evaluated when server is off
691                 if (isLocal.not) { "can't reboot a remote server".inform; ^this };
692                 if(serverRunning) {
693                         Routine.run {
694                                 this.quit;
695                                 this.wait(\done);
696                                 0.1.wait;
697                                 func.value;
698                                 this.boot;
699                         }
700                 } {
701                         func.value;
702                         this.boot
703                 }
704         }
706         status {
707                 addr.sendStatusMsg
708         }
710         notify_ { |flag = true|
711                 notify = flag;
712                 if(flag){
713                         if(serverRunning){
714                                 this.sendNotifyRequest(true);
715                                 notified = true;
716                                 "Receiving notification messages from server %\n".postf(this.name);
717                         }
718                 }{
719                         this.sendNotifyRequest(false);
720                         notified = false;
721                         "Switched off notification messages from server %\n".postf(this.name);
722                 }
723         }
725         sendNotifyRequest { arg flag=true;
726                 notified = flag;
727                 addr.sendMsg("/notify", flag.binaryValue);
728         }
730         dumpOSC { arg code=1;
731                 /*
732                         0 - turn dumping OFF.
733                         1 - print the parsed contents of the message.
734                         2 - print the contents in hexadecimal.
735                         3 - print both the parsed and hexadecimal representations of the contents.
736                 */
737                 dumpMode = code;
738                 this.sendMsg(\dumpOSC,code);
739         }
741         quit {
742                 var     serverReallyQuitWatcher, serverReallyQuit = false;
743                 statusWatcher !? {
744                         statusWatcher.disable;
745                         if(notified) {
746                                 serverReallyQuitWatcher = OSCFunc({ |msg|
747                                         if(msg[1] == '/quit') {
748                                                 statusWatcher.enable;
749                                                 serverReallyQuit = true;
750                                                 serverReallyQuitWatcher.free;
751                                         };
752                                 }, '/done', addr);
753                                 // don't accumulate quit-watchers if /done doesn't come back
754                                 AppClock.sched(3.0, {
755                                         if(serverReallyQuit.not) {
756                                                 "Server % failed to quit after 3.0 seconds.".format(this.name).warn;
757                                                 serverReallyQuitWatcher.free;
758                                         };
759                                 });
760                         };
761                 };
762                 addr.sendMsg("/quit");
763                 if (inProcess, {
764                         this.quitInProcess;
765                         "quit done\n".inform;
766                 },{
767                         "/quit sent\n".inform;
768                 });
769                 alive = false;
770                 notified = false;
771                 dumpMode = 0;
772                 pid = nil;
773                 serverBooting = false;
774                 sendQuit = nil;
775                 this.serverRunning = false;
776                 if(scopeWindow.notNil) { scopeWindow.quit };
777                 if(volume.isPlaying) {
778                         volume.free
779                 };
780                 RootNode(this).freeAll;
781                 this.newAllocators;
782         }
784         *quitAll {
785                 set.do({ arg server;
786                         if (server.sendQuit === true) {
787                                 server.quit
788                         };
789                 })
790         }
791         *killAll {
792                 // if you see Exception in World_OpenUDP: unable to bind udp socket
793                 // its because you have multiple servers running, left
794                 // over from crashes, unexpected quits etc.
795                 // you can't cause them to quit via OSC (the boot button)
797                 // this brutally kills them all off
798                 thisProcess.platform.killAll(this.program.basename);
799                 this.quitAll;
800         }
801         freeAll {
802                 this.sendMsg("/g_freeAll", 0);
803                 this.sendMsg("/clearSched");
804                 this.initTree;
805         }
807         *freeAll { arg evenRemote = false;
808                 if (evenRemote) {
809                         set.do { arg server;
810                                 if ( server.serverRunning ) { server.freeAll }
811                         }
812                 } {
813                         set.do { arg server;
814                                 if (server.isLocal and:{ server.serverRunning }) { server.freeAll }
815                         }
816                 }
817         }
819         *hardFreeAll { arg evenRemote = false;
820                 if (evenRemote) {
821                         set.do { arg server;
822                                 server.freeAll
823                         }
824                 } {
825                         set.do { arg server;
826                                 if (server.isLocal) { server.freeAll }
827                         }
828                 }
829         }
831         *allRunningServers {
832                 ^this.all.select(_.serverRunning)
833         }
835         // bundling support
838         openBundle { arg bundle;        // pass in a bundle that you want to
839                                                         // continue adding to, or nil for a new bundle.
840                 if(addr.hasBundle) {
841                         bundle = addr.bundle.addAll(bundle);
842                         addr.bundle = []; // debatable
843                 };
844                 addr = BundleNetAddr.copyFrom(addr, bundle);
845         }
846         closeBundle { arg time; // set time to false if you don't want to send.
847                 var bundle;
848                 if(addr.hasBundle) {
849                         bundle = addr.closeBundle(time);
850                         addr = addr.saveAddr;
851                 } {
852                         "there is no open bundle.".warn
853                 };
854                 ^bundle;
855         }
856         makeBundle { arg time, func, bundle;
857                 this.openBundle(bundle);
858                 try {
859                         func.value(this);
860                         bundle = this.closeBundle(time);
861                 }{|error|
862                         addr = addr.saveAddr; // on error restore the normal NetAddr
863                         error.throw
864                 }
865                 ^bundle
866         }
867         bind { arg func;
868                 ^this.makeBundle(this.latency, func)
869         }
871         // internal server commands
872         bootInProcess {
873                 ^options.bootInProcess;
874         }
875         quitInProcess {
876                 _QuitInProcessServer
877                 ^this.primitiveFailed
878         }
879         allocSharedControls { arg numControls=1024;
880                 _AllocSharedControls
881                 ^this.primitiveFailed
882         }
883         setSharedControl { arg num, value;
884                 _SetSharedControl
885                 ^this.primitiveFailed
886         }
887         getSharedControl { arg num;
888                 _GetSharedControl
889                 ^this.primitiveFailed
890         }
892         // recording output
893         record { |path|
894                 if(recordBuf.isNil){
895                         this.prepareForRecord(path);
896                         Routine({
897                                 this.sync;
898                                 this.record;
899                         }).play;
900                 }{
901                         if(recordNode.isNil){
902                                 recordNode = Synth.tail(RootNode(this), "server-record",
903                                                 [\bufnum, recordBuf.bufnum]);
904                                 CmdPeriod.doOnce {
905                                         recordNode = nil;
906                                         if (recordBuf.notNil) { recordBuf.close {|buf| buf.freeMsg }; recordBuf = nil; };
907                                 }
908                         } {
909                                 recordNode.run(true)
910                         };
911                         "Recording: %\n".postf(recordBuf.path);
912                 };
913         }
915         pauseRecording {
916                 recordNode.notNil.if({ recordNode.run(false); "Paused".postln }, { "Not Recording".warn });
917         }
919         stopRecording {
920                 if(recordNode.notNil) {
921                         recordNode.free;
922                         recordNode = nil;
923                         recordBuf.close({ |buf| buf.freeMsg });
924                         "Recording Stopped: %\n".postf(recordBuf.path);
925                         recordBuf = nil;
926                 } {
927                         "Not Recording".warn
928                 };
929         }
931         prepareForRecord { arg path;
932                 if (path.isNil) {
933                         if(File.exists(thisProcess.platform.recordingsDir).not) {
934                                 thisProcess.platform.recordingsDir.mkdir
935                         };
937                         // temporary kludge to fix Date's brokenness on windows
938                         if(thisProcess.platform.name == \windows) {
939                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Main.elapsedTime.round(0.01) ++ "." ++ recHeaderFormat;
941                         } {
942                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ recHeaderFormat;
943                         };
944                 };
945                 recordBuf = Buffer.alloc(this, 65536, recChannels,
946                         {arg buf; buf.writeMsg(path, recHeaderFormat, recSampleFormat, 0, 0, true);},
947                         this.options.numBuffers + 1); // prevent buffer conflicts by using reserved bufnum
948                 recordBuf.path = path;
949                 SynthDef("server-record", { arg bufnum;
950                         DiskOut.ar(bufnum, In.ar(0, recChannels))
951                 }).send(this);
952         }
954         // CmdPeriod support for Server-scope and Server-record and Server-volume
955         cmdPeriod {
956                 addr = addr.recover;
957                 this.changed(\cmdPeriod);
958         }
960         doOnServerTree {
961                 if(scopeWindow.notNil) { scopeWindow.run }
962         }
964         defaultGroup { ^Group.basicNew(this, 1) }
966         queryAllNodes { arg queryControls = false;
967                 var resp, done = false;
968                 if(isLocal, {this.sendMsg("/g_dumpTree", 0, queryControls.binaryValue);}, {
969                         resp = OSCFunc({ arg msg;
970                                 var i = 2, tabs = 0, printControls = false, dumpFunc;
971                                 if(msg[1] != 0, {printControls = true});
972                                 ("NODE TREE Group" + msg[2]).postln;
973                                 if(msg[3] > 0, {
974                                         dumpFunc = {|numChildren|
975                                                 var j;
976                                                 tabs = tabs + 1;
977                                                 numChildren.do({
978                                                         if(msg[i + 1] >=0, {i = i + 2}, {
979                                                                 i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
980                                                         });
981                                                         tabs.do({ "   ".post });
982                                                         msg[i].post; // nodeID
983                                                         if(msg[i + 1] >=0, {
984                                                                 " group".postln;
985                                                                 if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
986                                                         }, {
987                                                                 (" " ++ msg[i + 2]).postln; // defname
988                                                                 if(printControls, {
989                                                                         if(msg[i + 3] > 0, {
990                                                                                 " ".post;
991                                                                                 tabs.do({ "   ".post });
992                                                                         });
993                                                                         j = 0;
994                                                                         msg[i + 3].do({
995                                                                                 " ".post;
996                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
997                                                                                         (msg[i + 4 + j] ++ ": ").post;
998                                                                                 });
999                                                                                 msg[i + 5 + j].post;
1000                                                                                 j = j + 2;
1001                                                                         });
1002                                                                         "\n".post;
1003                                                                 });
1004                                                         });
1005                                                 });
1006                                                 tabs = tabs - 1;
1007                                         };
1008                                         dumpFunc.value(msg[3]);
1009                                 });
1010                                 done = true;
1011                         }, '/g_queryTree.reply', addr).oneShot;
1012                         this.sendMsg("/g_queryTree", 0, queryControls.binaryValue);
1013                         SystemClock.sched(3, {
1014                                 done.not.if({
1015                                         resp.free;
1016                                         "Remote server failed to respond to queryAllNodes!".warn;
1017                                 });
1018                         });
1019                 })
1020         }
1021         printOn { |stream|
1022                 stream << name;
1023         }
1024         storeOn { arg stream;
1025                 var codeStr = this.switch (
1026                         Server.default,                         { if (sync_s) { "s" } { "Server.default" } },
1027                         Server.local,                           { "Server.local" },
1028                         Server.internal,                        { "Server.internal" },
1029                         { "Server.fromName(" + name.asCompileString + ")" }
1030                 );
1031                 stream << codeStr;
1032         }
1034         archiveAsCompileString { ^true }
1035         archiveAsObject { ^true }
1037         volume_ {arg newVolume;
1038                 volume.volume_(newVolume);
1039         }
1041         mute {
1042                 volume.mute;
1043         }
1045         unmute {
1046                 volume.unmute;
1047         }
1049         hasShmInterface { ^serverInterface.notNil }
1051         reorder { arg nodeList, target, addAction=\addToHead;
1052                 target = target.asTarget;
1053                 this.sendMsg(62, Node.actionNumberFor(addAction), target.nodeID, *(nodeList.collect(_.nodeID))); //"/n_order"
1054         }
1056         getControlBusValue {|busIndex|
1057                 if (serverInterface.isNil) {
1058                         Error("Server-getControlBusValue only supports local servers").throw;
1059                 } {
1060                         ^serverInterface.getControlBusValue(busIndex)
1061                 }
1062         }
1064         getControlBusValues {|busIndex, busChannels|
1065                 if (serverInterface.isNil) {
1066                         Error("Server-getControlBusValues only supports local servers").throw;
1067                 } {
1068                         ^serverInterface.getControlBusValues(busIndex, busChannels)
1069                 }
1070         }
1072         setControlBusValue {|busIndex, value|
1073                 if (serverInterface.isNil) {
1074                         Error("Server-getControlBusValue only supports local servers").throw;
1075                 } {
1076                         ^serverInterface.setControlBusValue(busIndex, value)
1077                 }
1078         }
1080         setControlBusValues {|busIndex, valueArray|
1081                 if (serverInterface.isNil) {
1082                         Error("Server-getControlBusValues only supports local servers").throw;
1083                 } {
1084                         ^serverInterface.setControlBusValues(busIndex, valueArray)
1085                 }
1086         }