HelpBrowser: path box becomes a more conventional search box
[supercollider.git] / SCClassLibrary / Common / Control / Server.sc
blobf568a1effbd432b413984c6646511a1900ceaced
1 ServerOptions
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 <>blockAllocClass;
31         var <>verbosity = 0;
32         var <>zeroConf = false; // Whether server publishes port to Bonjour, etc.
34         var <>restrictedPath = nil;
36         var <>initialNodeID = 1000;
37         var <>remoteControlVolume = false;
39         var <>memoryLocking = false;
41         device
42         {
43                 ^if(inDevice == outDevice)
44                 {
45                         inDevice
46                 }
47                 {
48                         [inDevice, outDevice]
49                 }
50         }
52         device_
53         {
54                 |dev|
55                 inDevice = outDevice = dev;
56         }
58         // max logins
59         // session-password
61         // prevent buffer conflicts in Server-prepareForRecord and Server-scope by
62         // ensuring reserved buffers
64         numBuffers {
65                 ^numBuffers - 2
66         }
68         numBuffers_ { | argNumBuffers |
69                 numBuffers = argNumBuffers + 2
70         }
72         asOptionsString { | port = 57110 |
73                 var o;
74                 o = if (protocol == \tcp, " -t ", " -u ");
75                 o = o ++ port;
77                 if (numAudioBusChannels != 128, {
78                         o = o ++ " -a " ++ numAudioBusChannels;
79                 });
80                 if (numControlBusChannels != 4096, {
81                         o = o ++ " -c " ++ numControlBusChannels;
82                 });
83                 if (numInputBusChannels != 8, {
84                         o = o ++ " -i " ++ numInputBusChannels;
85                 });
86                 if (numOutputBusChannels != 8, {
87                         o = o ++ " -o " ++ numOutputBusChannels;
88                 });
89                 if (numBuffers != 1024, {
90                         o = o ++ " -b " ++ numBuffers;
91                 });
92                 if (maxNodes != 1024, {
93                         o = o ++ " -n " ++ maxNodes;
94                 });
95                 if (maxSynthDefs != 1024, {
96                         o = o ++ " -d " ++ maxSynthDefs;
97                 });
98                 if (blockSize != 64, {
99                         o = o ++ " -z " ++ blockSize;
100                 });
101                 if (hardwareBufferSize.notNil, {
102                         o = o ++ " -Z " ++ hardwareBufferSize;
103                 });
104                 if (memSize != 8192, {
105                         o = o ++ " -m " ++ memSize;
106                 });
107                 if (numRGens != 64, {
108                         o = o ++ " -r " ++ numRGens;
109                 });
110                 if (numWireBufs != 64, {
111                         o = o ++ " -w " ++ numWireBufs;
112                 });
113                 if (sampleRate.notNil, {
114                         o = o ++ " -S " ++ sampleRate;
115                 });
116                 if (loadDefs.not, {
117                         o = o ++ " -D 0";
118                 });
119                 if (inputStreamsEnabled.notNil, {
120                         o = o ++ " -I " ++ inputStreamsEnabled ;
121                 });
122                 if (outputStreamsEnabled.notNil, {
123                         o = o ++ " -O " ++ outputStreamsEnabled ;
124                 });
125                 if (inDevice == outDevice)
126                 {
127                         if (inDevice.notNil,
128                         {
129                                 o = o ++ " -H %".format(inDevice.quote);
130                         });
131                 }
132                 {
133                         o = o ++ " -H % %".format(inDevice.asString.quote, outDevice.asString.quote);
134                 };
135                 if (verbosity != 0, {
136                         o = o ++ " -v " ++ verbosity;
137                 });
138                 if (zeroConf.not, {
139                         o = o ++ " -R 0";
140                 });
141                 if (restrictedPath.notNil, {
142                         o = o ++ " -P " ++ restrictedPath;
143                 });
144                 if (memoryLocking, {
145                         o = o ++ " -L";
146                 });
147                 ^o
148         }
150         firstPrivateBus { // after the outs and ins
151                 ^numOutputBusChannels + numInputBusChannels
152         }
154         bootInProcess {
155                 _BootInProcessServer
156                 ^this.primitiveFailed
157         }
159         rendezvous_ {|bool|
160                 zeroConf = bool;
161                 this.deprecated(thisMethod, ServerOptions.findMethod(\zeroConf_))
162         }
164         rendezvous {|bool|
165                 this.deprecated(thisMethod, ServerOptions.findMethod(\zeroConf));
166                 ^zeroConf;
167         }
169         *prListDevices
170         {
171                 arg in, out;
172                 _ListAudioDevices
173                 ^this.primitiveFailed
174         }
176         *devices
177         {
178                 ^this.prListDevices(1, 1);
179         }
181         *inDevices
182         {
183                 ^this.prListDevices(1, 0);
184         }
186         *outDevices
187         {
188                 ^this.prListDevices(0, 1);
189         }
192 Server {
193         classvar <>local, <>internal, <default, <>named, <>set, <>program, <>sync_s = true;
195         var <name, <>addr, <clientID=0;
196         var <isLocal, <inProcess, <>sendQuit, <>remoteControlled;
197         var <serverRunning = false, <serverBooting = false, bootNotifyFirst = false;
198         var <>options, <>latency = 0.2, <dumpMode = 0, <notify = true, <notified=false;
199         var <nodeAllocator;
200         var <controlBusAllocator;
201         var <audioBusAllocator;
202         var <bufferAllocator;
203         var <syncThread, <syncTasks;
205         var <numUGens=0, <numSynths=0, <numGroups=0, <numSynthDefs=0;
206         var <avgCPU, <peakCPU;
207         var <sampleRate, <actualSampleRate;
209         var alive = false, booting = false, aliveThread, <>aliveThreadPeriod = 0.7, statusWatcher;
210         var <>tree;
212         var <window, <>scopeWindow;
213         var <emacsbuf;
214         var recordBuf, <recordNode, <>recHeaderFormat="aiff", <>recSampleFormat="float";        var <>recChannels=2;
216         var <volume;
218         var <pid;
220         *default_ { |server|
221                 default = server; // sync with s?
222                 if (sync_s, { thisProcess.interpreter.s = server });
223                 this.all.do(_.changed(\default));
224         }
226         *new { arg name, addr, options, clientID=0;
227                 ^super.new.init(name, addr, options, clientID)
228         }
230         *all { ^set }
231         *all_ { arg dict; set = dict }
233         init { arg argName, argAddr, argOptions, argClientID;
234                 name = argName.asSymbol;
235                 addr = argAddr;
236                 clientID = argClientID;
237                 options = argOptions ? ServerOptions.new;
238                 if (addr.isNil, { addr = NetAddr("127.0.0.1", 57110) });
239                 inProcess = addr.addr == 0;
240                 isLocal = inProcess || { addr.addr == 2130706433 };
241                 remoteControlled = isLocal;
242                 serverRunning = false;
243                 named.put(name, this);
244                 set.add(this);
245                 this.newAllocators;
246                 Server.changed(\serverAdded, this);
247                 volume = Volume.new(server: this, persist: true);
248         }
250         initTree {
251                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
252                 this.sendMsg("/g_new", 1, 0, 0);
253                 tree.value(this);
254                 ServerTree.run(this);
255         }
256         newAllocators {
257                 this.newNodeAllocators;
258                 this.newBusAllocators;
259                 this.newBufferAllocators;
260                 NotificationCenter.notify(this, \newAllocators);
261         }
263         newNodeAllocators {
264                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
265         }
267         newBusAllocators {
268                 controlBusAllocator = ContiguousBlockAllocator.new(options.numControlBusChannels);
269                 audioBusAllocator = ContiguousBlockAllocator.new(options.numAudioBusChannels,
270                         options.firstPrivateBus);
271         }
273         newBufferAllocators {
274                 bufferAllocator = ContiguousBlockAllocator.new(options.numBuffers);
275         }
277         nextNodeID {
278                 ^nodeAllocator.alloc
279         }
280         nextPermNodeID {
281                 ^nodeAllocator.allocPerm
282         }
283         freePermNodeID { |id|
284                 ^nodeAllocator.freePerm(id)
285         }
287         *initClass {
288                 Class.initClassTree(ServerOptions);
289                 Class.initClassTree(NotificationCenter);
290                 named = IdentityDictionary.new;
291                 set = Set.new;
292                 default = local = Server.new(\localhost, NetAddr("127.0.0.1", 57110));
293                 Platform.switch(\windows, {
294                         program = "scsynth.exe";
295                 }, {
296                         internal = Server.new(\internal, NetAddr.new);
297                         program = "cd % && exec ./scsynth".format(String.scDir.quote);
298                 });
299         }
301         *fromName { arg name;
302                 ^Server.named[name] ?? {
303                         Server(name, NetAddr.new("127.0.0.1", 57110), ServerOptions.new, 0)
304                 }
305         }
307         // bundling support added
308         sendMsg { arg ... msg;
309                 addr.sendMsg(*msg);
310         }
311         sendBundle { arg time ... msgs;
312                 addr.sendBundle(time, *msgs)
313         }
315         sendRaw { arg rawArray;
316                 addr.sendRaw(rawArray);
317         }
319         sendMsgSync { arg condition ... args;
320                 var cmdName, resp;
321                 if (condition.isNil) { condition = Condition.new };
322                 cmdName = args[0].asString;
323                 if (cmdName[0] != $/) { cmdName = cmdName.insert(0, $/) };
324                 resp = OSCFunc({|msg|
325                         if (msg[1].asString == cmdName) {
326                                 resp.free;
327                                 condition.test = true;
328                                 condition.signal;
329                         };
330                 }, '/done', addr);
331                 condition.test = false;
332                 addr.sendBundle(nil, args);
333                 condition.wait;
334         }
336         sync { arg condition, bundles, latency; // array of bundles that cause async action
337                 addr.sync(condition, bundles, latency)
338         }
340         schedSync { arg func;
341                 syncTasks = syncTasks.add(func);
342                 if(syncThread.isNil) {
343                         syncThread = Routine.run {
344                                 var c; c = Condition.new;
345                                 while { syncTasks.notEmpty } { syncTasks.removeAt(0).value(c) };
346                                 syncThread = nil;
347                         };
348                 };
350         }
352         // bundling support added
353         listSendMsg { arg msg;
354                 addr.sendMsg(*msg);
355         }
356         listSendBundle { arg time, msgs;
357                 addr.sendBundle(time, *(msgs.asArray));
358         }
360         // load from disk locally, send remote
361         sendSynthDef { arg name, dir;
362                 var file, buffer;
363                 dir = dir ? SynthDef.synthDefDir;
364                 file = File(dir ++ name ++ ".scsyndef","r");
365                 if (file.isNil, { ^nil });
366                 protect {
367                         buffer = Int8Array.newClear(file.length);
368                         file.read(buffer);
369                 }{
370                         file.close;
371                 };
372                 this.sendMsg("/d_recv", buffer);
373         }
374         // tell server to load from disk
375         loadSynthDef { arg name, completionMsg, dir;
376                 dir = dir ? SynthDef.synthDefDir;
377                 this.listSendMsg(
378                         ["/d_load", dir ++ name ++ ".scsyndef", completionMsg ]
379                 )
380         }
381         //loadDir
382         loadDirectory { arg dir, completionMsg;
383                 this.listSendMsg(["/d_loadDir", dir, completionMsg]);
384         }
386         serverRunning_ { arg val;
387                 if(addr.hasBundle) {
388                         { this.changed(\bundling); }.defer;
389                 } {
390                         if (val != serverRunning) {
391                                 if(thisProcess.platform.isSleeping.not) {
392                                         serverRunning = val;
393                                         if (serverRunning.not) {
395                                                 ServerQuit.run(this);
397                                                 AppClock.sched(5.0, {
398                                                         // still down after 5 seconds, assume server is really dead
399                                                         // if you explicitly shut down the server then newAllocators
400                                                         // and the \newAllocators notification will happen immediately
401                                                         if(serverRunning.not) {
402                                                                 NotificationCenter.notify(this,\didQuit);
403                                                         };
404                                                         recordNode = nil;
405                                                         if(this.isLocal.not){
406                                                                 notified = false;
407                                                         }
408                                                 })
410                                         }{
411                                                 ServerBoot.run(this);
412                                         };
413                                         { this.changed(\serverRunning); }.defer;
414                                 }
415                         }
416                 };
417         }
419         wait { arg responseName;
420                 var routine;
421                 routine = thisThread;
422                 OSCFunc({
423                         routine.resume(true);
424                 }, responseName, addr).oneShot;
425         }
427         waitForBoot { arg onComplete, limit=100;
428                 if(serverRunning.not) { this.boot };
429                 this.doWhenBooted(onComplete, limit);
430         }
432         doWhenBooted { arg onComplete, limit=100;
433                 var mBootNotifyFirst = bootNotifyFirst;
434                 bootNotifyFirst = false;
436                 ^Routine({
437                         while({
438                                 ((serverRunning.not
439                                   or: (serverBooting and: mBootNotifyFirst.not))
440                                  and: {(limit = limit - 1) > 0})
441                                 and: { pid.tryPerform(\pidRunning) == true }
442                         },{
443                                 0.2.wait;
444                         });
446                         if(serverRunning.not,{
447                                 "server failed to start".error;
448                                 "For advice: [http://supercollider.sf.net/wiki/index.php/ERROR:_server_failed_to_start]".postln;
449                                 serverBooting = false;
450                         }, onComplete);
451                 }).play(AppClock);
452         }
454         bootSync { arg condition;
455                 condition ?? { condition = Condition.new };
456                 condition.test = false;
457                 this.waitForBoot({
458                         // Setting func to true indicates that our condition has become true and we can go when signaled.
459                         condition.test = true;
460                         condition.signal
461                 });
462                 condition.wait;
463         }
465         ping { arg n=1, wait=0.1, func;
466                 var result=0, pingFunc;
467                 if(serverRunning.not) { "server not running".postln; ^this };
468                 pingFunc = {
469                         Routine.run {
470                                         var t, dt;
471                                         t = Main.elapsedTime;
472                                         this.sync;
473                                         dt = Main.elapsedTime - t;
474                                         ("measured latency:" + dt + "s").postln;
475                                         result = max(result, dt);
476                                         n = n - 1;
477                                         if(n > 0) {
478                                                 SystemClock.sched(wait, {pingFunc.value; nil })
479                                         } {
480                                                 ("maximum determined latency of" + name + ":" + result + "s").postln;
481                                                 func.value(result)
482                                         }
483                                 };
484                 };
485                 pingFunc.value;
486         }
488         addStatusWatcher {
489                 if(statusWatcher.isNil) {
490                         statusWatcher =
491                                 OSCFunc({ arg msg;
492                                         var cmd, one;
493                                         if(notify){
494                                                 if(notified.not){
495                                                         this.sendNotifyRequest;
496                                                         "Receiving notification messages from server %\n".postf(this.name);
497                                                 }
498                                         };
499                                         alive = true;
500                                         #cmd, one, numUGens, numSynths, numGroups, numSynthDefs,
501                                                 avgCPU, peakCPU, sampleRate, actualSampleRate = msg;
502                                         {
503                                                 this.serverRunning_(true);
504                                                 this.changed(\counts);
505                                                 nil // no resched
506                                         }.defer;
507                                 }, '/status.reply', addr).fix;
508                 } {
509                         statusWatcher.enable;
510                 };
511         }
512         // Buffer objects are cached in an Array for easy
513         // auto buffer info updating
514         addBuf { |buffer|
515                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\cache));
516                 buffer.cache;
517         }
519         freeBuf { |i|
520                 var     buf;
521                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\uncache));
522                 if((buf = Buffer.cachedBufferAt(this, i)).notNil) { buf.free };
523         }
525         // /b_info on the way
526         // keeps a reference count of waiting Buffers so that only one responder is needed
527         waitForBufInfo {
528                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\cache));
529         }
531         resetBufferAutoInfo {
532                 this.deprecated(thisMethod, Meta_Buffer.findRespondingMethodFor(\clearServerCaches));
533                 Buffer.clearServerCaches(this);
534         }
536         cachedBuffersDo { |func| Buffer.cachedBuffersDo(this, func) }
537         cachedBufferAt { |bufnum| ^Buffer.cachedBufferAt(this, bufnum) }
539         startAliveThread { arg delay=0.0;
540                 this.addStatusWatcher;
541                 ^aliveThread ?? {
542                         aliveThread = Routine({
543                                 // this thread polls the server to see if it is alive
544                                 delay.wait;
545                                 loop({
546                                         this.status;
547                                         aliveThreadPeriod.wait;
548                                         this.serverRunning = alive;
549                                         alive = false;
550                                 });
551                         });
552                         AppClock.play(aliveThread);
553                         aliveThread
554                 }
555         }
556         stopAliveThread {
557                 if( aliveThread.notNil, {
558                         aliveThread.stop;
559                         aliveThread = nil;
560                 });
561                 if( statusWatcher.notNil, {
562                         statusWatcher.free;
563                         statusWatcher = nil;
564                 });
565         }
566         aliveThreadIsRunning {
567                 ^aliveThread.notNil and: {aliveThread.isPlaying}
568         }
569         *resumeThreads {
570                 set.do({ arg server;
571                         server.stopAliveThread;
572                         server.startAliveThread(server.aliveThreadPeriod);
573                 });
574         }
576         boot { arg startAliveThread=true, recover=false;
577                 var resp;
578                 if (serverRunning, { "server already running".inform; ^this });
579                 if (serverBooting, { "server already booting".inform; ^this });
581                 serverBooting = true;
582                 if(startAliveThread, { this.startAliveThread });
583                 if(recover) { this.newNodeAllocators } { this.newAllocators };
584                 bootNotifyFirst = true;
585                 this.doWhenBooted({
586                         serverBooting = false;
587                         if (sendQuit.isNil) {
588                                 sendQuit = not(this.inProcess) and: {this.isLocal};
589                         };
590                         this.initTree;
591                         (volume.volume != 0.0).if({
592                                 volume.play;
593                                 });
594                 });
595                 if (remoteControlled.not, {
596                         "You will have to manually boot remote server.".inform;
597                 },{
598                         this.bootServerApp;
599                 });
600         }
602         bootServerApp {
603                 if (inProcess, {
604                         "booting internal".inform;
605                         this.bootInProcess;
606                         //alive = true;
607                         //this.serverRunning = true;
608                         pid = thisProcess.pid;
609                 },{
610                         pid = (program ++ options.asOptionsString(addr.port)).unixCmd;
611                         //unixCmd(program ++ options.asOptionsString(addr.port)).postln;
612                         ("booting " ++ addr.port.asString).inform;
613                 });
614         }
616         reboot { arg func; // func is evaluated when server is off
617                 if (isLocal.not) { "can't reboot a remote server".inform; ^this };
618                 if(serverRunning) {
619                         Routine.run {
620                                 this.quit;
621                                 this.wait(\done);
622                                 0.1.wait;
623                                 func.value;
624                                 this.boot;
625                         }
626                 } {
627                         func.value;
628                         this.boot
629                 }
630         }
632         status {
633                 addr.sendStatusMsg
634         }
636         notify_ { |flag = true|
637                 notify = flag;
638                 if(flag){
639                         if(serverRunning){
640                                 this.sendNotifyRequest(true);
641                                 notified = true;
642                                 "Receiving notification messages from server %\n".postf(this.name);
643                         }
644                 }{
645                         this.sendNotifyRequest(false);
646                         notified = false;
647                         "Switched off notification messages from server %\n".postf(this.name);
648                 }
649         }
651         sendNotifyRequest { arg flag=true;
652                 notified = flag;
653                 addr.sendMsg("/notify", flag.binaryValue);
654         }
656         dumpOSC { arg code=1;
657                 /*
658                         0 - turn dumping OFF.
659                         1 - print the parsed contents of the message.
660                         2 - print the contents in hexadecimal.
661                         3 - print both the parsed and hexadecimal representations of the contents.
662                 */
663                 dumpMode = code;
664                 this.sendMsg(\dumpOSC,code);
665         }
667         quit {
668                 var     serverReallyQuitWatcher, serverReallyQuit = false;
669                 statusWatcher !? {
670                         statusWatcher.disable;
671                         if(notified) {
672                                 serverReallyQuitWatcher = OSCFunc({ |msg|
673                                         if(msg[1] == '/quit') {
674                                                 statusWatcher.enable;
675                                                 serverReallyQuit = true;
676                                                 serverReallyQuitWatcher.free;
677                                         };
678                                 }, '/done', addr);
679                                 // don't accumulate quit-watchers if /done doesn't come back
680                                 AppClock.sched(3.0, {
681                                         if(serverReallyQuit.not) {
682                                                 "Server % failed to quit after 3.0 seconds.".format(this.name).warn;
683                                                 serverReallyQuitWatcher.free;
684                                         };
685                                 });
686                         };
687                 };
688                 addr.sendMsg("/quit");
689                 if (inProcess, {
690                         this.quitInProcess;
691                         "quit done\n".inform;
692                 },{
693                         "/quit sent\n".inform;
694                 });
695                 alive = false;
696                 notified = false;
697                 dumpMode = 0;
698                 pid = nil;
699                 serverBooting = false;
700                 sendQuit = nil;
701                 this.serverRunning = false;
702                 if(scopeWindow.notNil) { scopeWindow.quit };
703                 RootNode(this).freeAll;
704                 volume.isPlaying.if({
705                         volume.free
706                         });
707                 this.newAllocators;
708         }
710         *quitAll {
711                 set.do({ arg server;
712                         if (server.sendQuit === true) {
713                                 server.quit
714                         };
715                 })
716         }
717         *killAll {
718                 // if you see Exception in World_OpenUDP: unable to bind udp socket
719                 // its because you have multiple servers running, left
720                 // over from crashes, unexpected quits etc.
721                 // you can't cause them to quit via OSC (the boot button)
723                 // this brutally kills them all off
724                 "killall -9 scsynth".unixCmd;
725                 this.quitAll;
726         }
727         freeAll {
728                 this.sendMsg("/g_freeAll", 0);
729                 this.sendMsg("/clearSched");
730                 this.initTree;
731         }
733         *freeAll { arg evenRemote = false;
734                 if (evenRemote) {
735                         set.do { arg server;
736                                 if ( server.serverRunning ) { server.freeAll }
737                         }
738                 } {
739                         set.do { arg server;
740                                 if (server.isLocal and:{ server.serverRunning }) { server.freeAll }
741                         }
742                 }
743         }
745         *hardFreeAll { arg evenRemote = false;
746                 if (evenRemote) {
747                         set.do { arg server;
748                                 server.freeAll
749                         }
750                 } {
751                         set.do { arg server;
752                                 if (server.isLocal) { server.freeAll }
753                         }
754                 }
755         }
757         *allRunningServers {
758                 ^this.all.select(_.serverRunning)
759         }
761         // bundling support
764         openBundle { arg bundle;        // pass in a bundle that you want to
765                                                         // continue adding to, or nil for a new bundle.
766                 if(addr.hasBundle) {
767                         bundle = addr.bundle.addAll(bundle);
768                         addr.bundle = []; // debatable
769                 };
770                 addr = BundleNetAddr.copyFrom(addr, bundle);
771         }
772         closeBundle { arg time; // set time to false if you don't want to send.
773                 var bundle;
774                 if(addr.hasBundle) {
775                         bundle = addr.closeBundle(time);
776                         addr = addr.saveAddr;
777                 } {
778                         "there is no open bundle.".warn
779                 };
780                 ^bundle;
781         }
782         makeBundle { arg time, func, bundle;
783                 this.openBundle(bundle);
784                 try {
785                         func.value(this);
786                         bundle = this.closeBundle(time);
787                 }{|error|
788                         addr = addr.saveAddr; // on error restore the normal NetAddr
789                         error.throw
790                 }
791                 ^bundle
792         }
793         bind { arg func;
794                 ^this.makeBundle(this.latency, func)
795         }
797         // internal server commands
798         bootInProcess {
799                 ^options.bootInProcess;
800         }
801         quitInProcess {
802                 _QuitInProcessServer
803                 ^this.primitiveFailed
804         }
805         allocSharedControls { arg numControls=1024;
806                 _AllocSharedControls
807                 ^this.primitiveFailed
808         }
809         setSharedControl { arg num, value;
810                 _SetSharedControl
811                 ^this.primitiveFailed
812         }
813         getSharedControl { arg num;
814                 _GetSharedControl
815                 ^this.primitiveFailed
816         }
818         // recording output
819         record { |path|
820                 if(recordBuf.isNil){
821                         this.prepareForRecord(path);
822                         Routine({
823                                 this.sync;
824                                 this.record;
825                         }).play;
826                 }{
827                         if(recordNode.isNil){
828                                 recordNode = Synth.tail(RootNode(this), "server-record",
829                                                 [\bufnum, recordBuf.bufnum]);
830                         }{
831                                 recordNode.run(true)
832                         };
833                         "Recording: %\n".postf(recordBuf.path);
834                 };
835         }
837         pauseRecording {
838                 recordNode.notNil.if({ recordNode.run(false); "Paused".postln }, { "Not Recording".warn });
839         }
841         stopRecording {
842                 if(recordNode.notNil) {
843                         recordNode.free;
844                         recordNode = nil;
845                         recordBuf.close({ arg buf; buf.free; });
846                         "Recording Stopped: %\n".postf(recordBuf.path);
847                         recordBuf = nil;
848                 } {
849                         "Not Recording".warn
850                 };
851         }
853         prepareForRecord { arg path;
854                 if (path.isNil) {
855                         if(File.exists(thisProcess.platform.recordingsDir).not) {
856                                 systemCmd("mkdir" + thisProcess.platform.recordingsDir.quote);
857                         };
859                         // temporary kludge to fix Date's brokenness on windows
860                         if(thisProcess.platform.name == \windows) {
861                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Main.elapsedTime.round(0.01) ++ "." ++ recHeaderFormat;
863                         } {
864                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ recHeaderFormat;
865                         };
866                 };
867                 recordBuf = Buffer.alloc(this, 65536, recChannels,
868                         {arg buf; buf.writeMsg(path, recHeaderFormat, recSampleFormat, 0, 0, true);},
869                         this.options.numBuffers + 1); // prevent buffer conflicts by using reserved bufnum
870                 recordBuf.path = path;
871                 SynthDef("server-record", { arg bufnum;
872                         DiskOut.ar(bufnum, In.ar(0, recChannels))
873                 }).send(this);
874                 // cmdPeriod support
875                 CmdPeriod.add(this);
876         }
878         // CmdPeriod support for Server-scope and Server-record and Server-volume
879         cmdPeriod {
880                 if(recordNode.notNil) { recordNode = nil; };
881                 if(recordBuf.notNil) { recordBuf.close {|buf| buf.free; }; recordBuf = nil; };
882                 addr = addr.recover;
883                 this.changed(\cmdPeriod);
884                 if(scopeWindow.notNil) {
885                         fork { 0.5.wait; scopeWindow.run } // wait until synth is freed
886                 }{
887                         CmdPeriod.remove(this)
888                 };
889         }
891         defaultGroup { ^Group.basicNew(this, 1) }
893         queryAllNodes { arg queryControls = false;
894                 var resp, done = false;
895                 if(isLocal, {this.sendMsg("/g_dumpTree", 0, queryControls.binaryValue);}, {
896                         resp = OSCFunc({ arg msg;
897                                 var i = 2, tabs = 0, printControls = false, dumpFunc;
898                                 if(msg[1] != 0, {printControls = true});
899                                 ("NODE TREE Group" + msg[2]).postln;
900                                 if(msg[3] > 0, {
901                                         dumpFunc = {|numChildren|
902                                                 var j;
903                                                 tabs = tabs + 1;
904                                                 numChildren.do({
905                                                         if(msg[i + 1] >=0, {i = i + 2}, {
906                                                                 i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
907                                                         });
908                                                         tabs.do({ "   ".post });
909                                                         msg[i].post; // nodeID
910                                                         if(msg[i + 1] >=0, {
911                                                                 " group".postln;
912                                                                 if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
913                                                         }, {
914                                                                 (" " ++ msg[i + 2]).postln; // defname
915                                                                 if(printControls, {
916                                                                         if(msg[i + 3] > 0, {
917                                                                                 " ".post;
918                                                                                 tabs.do({ "   ".post });
919                                                                         });
920                                                                         j = 0;
921                                                                         msg[i + 3].do({
922                                                                                 " ".post;
923                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
924                                                                                         (msg[i + 4 + j] ++ ": ").post;
925                                                                                 });
926                                                                                 msg[i + 5 + j].post;
927                                                                                 j = j + 2;
928                                                                         });
929                                                                         "\n".post;
930                                                                 });
931                                                         });
932                                                 });
933                                                 tabs = tabs - 1;
934                                         };
935                                         dumpFunc.value(msg[3]);
936                                 });
937                                 done = true;
938                         }, '/g_queryTree.reply', addr).oneShot;
939                         this.sendMsg("/g_queryTree", 0, queryControls.binaryValue);
940                         SystemClock.sched(3, {
941                                 done.not.if({
942                                         resp.free;
943                                         "Remote server failed to respond to queryAllNodes!".warn;
944                                 });
945                         });
946                 })
947         }
948         printOn { |stream|
949                 stream << name;
950         }
951         storeOn { arg stream;
952                 var codeStr = this.switch (
953                         Server.default,                         { if (sync_s) { "s" } { "Server.default" } },
954                         Server.local,                           { "Server.local" },
955                         Server.internal,                        { "Server.internal" },
956                         { "Server.fromName(" + name.asCompileString + ")" }
957                 );
958                 stream << codeStr;
959         }
961         archiveAsCompileString { ^true }
962         archiveAsObject { ^true }
964         volume_ {arg newVolume;
965                 volume.volume_(newVolume);
966                 }
968         mute {
969                 volume.mute;
970                 }
972         unmute {
973                 volume.unmute;
974         }
976         reorder { arg nodeList, target, addAction=\addToHead;
977                 target = target.asTarget;
978                 this.sendMsg(62, Node.actionNumberFor(addAction), target.nodeID, *(nodeList.collect(_.nodeID))); //"/n_order"
979         }