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