cmake build system: visiblity support for clang
[supercollider.git] / SCClassLibrary / Common / Control / Server.sc
blob9d597fb4c515e7dfb1983739146b031b5e299de1
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 (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_{|num|
155                 this.numPrivateAudioBusChannels = num - (numInputBusChannels + numOutputBusChannels);
156                 this.deprecated(thisMethod);
157         }
159         numAudioBusChannels{
160                 ^numPrivateAudioBusChannels + numInputBusChannels + numOutputBusChannels
161         }
163         bootInProcess {
164                 _BootInProcessServer
165                 ^this.primitiveFailed
166         }
168         rendezvous_ {|bool|
169                 zeroConf = bool;
170                 this.deprecated(thisMethod, ServerOptions.findMethod(\zeroConf_))
171         }
173         rendezvous {|bool|
174                 this.deprecated(thisMethod, ServerOptions.findMethod(\zeroConf));
175                 ^zeroConf;
176         }
178         *prListDevices {
179                 arg in, out;
180                 _ListAudioDevices
181                 ^this.primitiveFailed
182         }
184         *devices {
185                 ^this.prListDevices(1, 1);
186         }
188         *inDevices {
189                 ^this.prListDevices(1, 0);
190         }
192         *outDevices {
193                 ^this.prListDevices(0, 1);
194         }
197 ServerShmInterface {
198         // order matters!
199         var ptr, finalizer;
201         *new {|port|
202                 ^super.new.connect(port)
203         }
205         connect {
206                 _ServerShmInterface_connectSharedMem
207                 ^this.primitiveFailed
208         }
210         disconnect {
211                 _ServerShmInterface_disconnectSharedMem
212                 ^this.primitiveFailed
213         }
215         getControlBusValue {
216                 _ServerShmInterface_getControlBusValue
217                 ^this.primitiveFailed
218         }
220         getControlBusValues {
221                 _ServerShmInterface_getControlBusValues
222                 ^this.primitiveFailed
223         }
225         setControlBusValue {
226                 _ServerShmInterface_getControlBusValue
227                 ^this.primitiveFailed
228         }
230         setControlBusValues {
231                 _ServerShmInterface_getControlBusValues
232                 ^this.primitiveFailed
233         }
236 Server {
237         classvar <>local, <>internal, <default, <>named, <>set, <>program, <>sync_s = true;
239         var <name, <>addr, <clientID=0;
240         var <isLocal, <inProcess, <>sendQuit, <>remoteControlled;
241         var <serverRunning = false, <serverBooting = false, bootNotifyFirst = false;
242         var <>options, <>latency = 0.2, <dumpMode = 0, <notify = true, <notified=false;
243         var <nodeAllocator;
244         var <controlBusAllocator;
245         var <audioBusAllocator;
246         var <bufferAllocator;
247         var <scopeBufferAllocator;
248         var <syncThread, <syncTasks;
250         var <numUGens=0, <numSynths=0, <numGroups=0, <numSynthDefs=0;
251         var <avgCPU, <peakCPU;
252         var <sampleRate, <actualSampleRate;
254         var alive = false, booting = false, aliveThread, <>aliveThreadPeriod = 0.7, statusWatcher;
255         var <>tree;
257         var <window, <>scopeWindow;
258         var <emacsbuf;
259         var recordBuf, <recordNode, <>recHeaderFormat="aiff", <>recSampleFormat="float";
260         var <>recChannels=2;
262         var <volume;
264         var <pid;
265         var serverInterface;
267         *default_ { |server|
268                 default = server; // sync with s?
269                 if (sync_s, { thisProcess.interpreter.s = server });
270                 this.all.do(_.changed(\default));
271         }
273         *new { arg name, addr, options, clientID=0;
274                 ^super.new.init(name, addr, options, clientID)
275         }
277         *all { ^set }
278         *all_ { arg dict; set = dict }
280         init { arg argName, argAddr, argOptions, argClientID;
281                 name = argName.asSymbol;
282                 addr = argAddr;
283                 clientID = argClientID;
284                 options = argOptions ? ServerOptions.new;
285                 if (addr.isNil, { addr = NetAddr("127.0.0.1", 57110) });
286                 inProcess = addr.addr == 0;
287                 isLocal = inProcess || { addr.addr == 2130706433 };
288                 remoteControlled = isLocal;
289                 serverRunning = false;
290                 named.put(name, this);
291                 set.add(this);
292                 this.newAllocators;
293                 Server.changed(\serverAdded, this);
294                 volume = Volume.new(server: this, persist: true);
295         }
297         initTree {
298                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
299                 this.sendMsg("/g_new", 1, 0, 0);
300                 tree.value(this);
301                 ServerTree.run(this);
302         }
303         newAllocators {
304                 this.newNodeAllocators;
305                 this.newBusAllocators;
306                 this.newBufferAllocators;
307                 this.newScopeBufferAllocators;
308                 NotificationCenter.notify(this, \newAllocators);
309         }
311         newNodeAllocators {
312                 nodeAllocator = NodeIDAllocator(clientID, options.initialNodeID);
313         }
315         newBusAllocators {
316                 controlBusAllocator = ContiguousBlockAllocator.new(options.numControlBusChannels);
317                 audioBusAllocator = ContiguousBlockAllocator.new(options.numAudioBusChannels,
318                         options.firstPrivateBus);
319         }
321         newBufferAllocators {
322                 bufferAllocator = ContiguousBlockAllocator.new(options.numBuffers);
323         }
325         newScopeBufferAllocators {
326                 if (isLocal) {
327                         scopeBufferAllocator = StackNumberAllocator.new(0, 127);
328                 }
329         }
331         nextNodeID {
332                 ^nodeAllocator.alloc
333         }
334         nextPermNodeID {
335                 ^nodeAllocator.allocPerm
336         }
337         freePermNodeID { |id|
338                 ^nodeAllocator.freePerm(id)
339         }
341         *initClass {
342                 Class.initClassTree(ServerOptions);
343                 Class.initClassTree(NotificationCenter);
344                 named = IdentityDictionary.new;
345                 set = Set.new;
346                 default = local = Server.new(\localhost, NetAddr("127.0.0.1", 57110));
347                 Platform.switch(\windows, {
348                         program = "scsynth.exe";
349                 }, {
350                         internal = Server.new(\internal, NetAddr.new);
351                         program = "cd % && exec ./scsynth".format(String.scDir.quote);
352                 });
353         }
355         *fromName { arg name;
356                 ^Server.named[name] ?? {
357                         Server(name, NetAddr.new("127.0.0.1", 57110), ServerOptions.new, 0)
358                 }
359         }
361         // bundling support added
362         sendMsg { arg ... msg;
363                 addr.sendMsg(*msg);
364         }
365         sendBundle { arg time ... msgs;
366                 addr.sendBundle(time, *msgs)
367         }
369         sendRaw { arg rawArray;
370                 addr.sendRaw(rawArray);
371         }
373         sendMsgSync { arg condition ... args;
374                 var cmdName, resp;
375                 if (condition.isNil) { condition = Condition.new };
376                 cmdName = args[0].asString;
377                 if (cmdName[0] != $/) { cmdName = cmdName.insert(0, $/) };
378                 resp = OSCFunc({|msg|
379                         if (msg[1].asString == cmdName) {
380                                 resp.free;
381                                 condition.test = true;
382                                 condition.signal;
383                         };
384                 }, '/done', addr);
385                 condition.test = false;
386                 addr.sendBundle(nil, args);
387                 condition.wait;
388         }
390         sync { arg condition, bundles, latency; // array of bundles that cause async action
391                 addr.sync(condition, bundles, latency)
392         }
394         schedSync { arg func;
395                 syncTasks = syncTasks.add(func);
396                 if(syncThread.isNil) {
397                         syncThread = Routine.run {
398                                 var c; c = Condition.new;
399                                 while { syncTasks.notEmpty } { syncTasks.removeAt(0).value(c) };
400                                 syncThread = nil;
401                         };
402                 };
404         }
406         // bundling support added
407         listSendMsg { arg msg;
408                 addr.sendMsg(*msg);
409         }
410         listSendBundle { arg time, msgs;
411                 addr.sendBundle(time, *(msgs.asArray));
412         }
414         // load from disk locally, send remote
415         sendSynthDef { arg name, dir;
416                 var file, buffer;
417                 dir = dir ? SynthDef.synthDefDir;
418                 file = File(dir ++ name ++ ".scsyndef","r");
419                 if (file.isNil, { ^nil });
420                 protect {
421                         buffer = Int8Array.newClear(file.length);
422                         file.read(buffer);
423                 }{
424                         file.close;
425                 };
426                 this.sendMsg("/d_recv", buffer);
427         }
428         // tell server to load from disk
429         loadSynthDef { arg name, completionMsg, dir;
430                 dir = dir ? SynthDef.synthDefDir;
431                 this.listSendMsg(
432                         ["/d_load", dir ++ name ++ ".scsyndef", completionMsg ]
433                 )
434         }
435         //loadDir
436         loadDirectory { arg dir, completionMsg;
437                 this.listSendMsg(["/d_loadDir", dir, completionMsg]);
438         }
440         serverRunning_ { arg val;
441                 if(addr.hasBundle) {
442                         { this.changed(\bundling); }.defer;
443                 } {
444                         if (val != serverRunning) {
445                                 if(thisProcess.platform.isSleeping.not) {
446                                         serverRunning = val;
447                                         if (serverRunning.not) {
449                                                 ServerQuit.run(this);
451                                                 AppClock.sched(5.0, {
452                                                         // still down after 5 seconds, assume server is really dead
453                                                         // if you explicitly shut down the server then newAllocators
454                                                         // and the \newAllocators notification will happen immediately
455                                                         if(serverRunning.not) {
456                                                                 NotificationCenter.notify(this,\didQuit);
457                                                         };
458                                                         recordNode = nil;
459                                                         if(this.isLocal.not){
460                                                                 notified = false;
461                                                         }
462                                                 })
464                                         }{
465                                                 ServerBoot.run(this);
466                                         };
467                                         { this.changed(\serverRunning); }.defer;
468                                 }
469                         }
470                 };
471         }
473         wait { arg responseName;
474                 var routine;
475                 routine = thisThread;
476                 OSCFunc({
477                         routine.resume(true);
478                 }, responseName, addr).oneShot;
479         }
481         waitForBoot { arg onComplete, limit=100;
482                 if(serverRunning.not) { this.boot };
483                 this.doWhenBooted(onComplete, limit);
484         }
486         doWhenBooted { arg onComplete, limit=100;
487                 var mBootNotifyFirst = bootNotifyFirst;
488                 bootNotifyFirst = false;
490                 ^Routine({
491                         while({
492                                 ((serverRunning.not
493                                   or: (serverBooting and: mBootNotifyFirst.not))
494                                  and: {(limit = limit - 1) > 0})
495                                 and: { pid.tryPerform(\pidRunning) == true }
496                         },{
497                                 0.2.wait;
498                         });
500                         if(serverRunning.not,{
501                                 "server failed to start".error;
502                                 "For advice: [http://supercollider.sf.net/wiki/index.php/ERROR:_server_failed_to_start]".postln;
503                                 serverBooting = false;
504                         }, onComplete);
505                 }).play(AppClock);
506         }
508         bootSync { arg condition;
509                 condition ?? { condition = Condition.new };
510                 condition.test = false;
511                 this.waitForBoot({
512                         // Setting func to true indicates that our condition has become true and we can go when signaled.
513                         condition.test = true;
514                         condition.signal
515                 });
516                 condition.wait;
517         }
519         ping { arg n=1, wait=0.1, func;
520                 var result=0, pingFunc;
521                 if(serverRunning.not) { "server not running".postln; ^this };
522                 pingFunc = {
523                         Routine.run {
524                                         var t, dt;
525                                         t = Main.elapsedTime;
526                                         this.sync;
527                                         dt = Main.elapsedTime - t;
528                                         ("measured latency:" + dt + "s").postln;
529                                         result = max(result, dt);
530                                         n = n - 1;
531                                         if(n > 0) {
532                                                 SystemClock.sched(wait, {pingFunc.value; nil })
533                                         } {
534                                                 ("maximum determined latency of" + name + ":" + result + "s").postln;
535                                                 func.value(result)
536                                         }
537                                 };
538                 };
539                 pingFunc.value;
540         }
542         addStatusWatcher {
543                 if(statusWatcher.isNil) {
544                         statusWatcher =
545                                 OSCFunc({ arg msg;
546                                         var cmd, one;
547                                         if(notify){
548                                                 if(notified.not){
549                                                         this.sendNotifyRequest;
550                                                         "Receiving notification messages from server %\n".postf(this.name);
551                                                 }
552                                         };
553                                         alive = true;
554                                         #cmd, one, numUGens, numSynths, numGroups, numSynthDefs,
555                                                 avgCPU, peakCPU, sampleRate, actualSampleRate = msg;
556                                         {
557                                                 this.serverRunning_(true);
558                                                 this.changed(\counts);
559                                                 nil // no resched
560                                         }.defer;
561                                 }, '/status.reply', addr).fix;
562                 } {
563                         statusWatcher.enable;
564                 };
565         }
566         // Buffer objects are cached in an Array for easy
567         // auto buffer info updating
568         addBuf { |buffer|
569                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\cache));
570                 buffer.cache;
571         }
573         freeBuf { |i|
574                 var     buf;
575                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\uncache));
576                 if((buf = Buffer.cachedBufferAt(this, i)).notNil) { buf.free };
577         }
579         // /b_info on the way
580         // keeps a reference count of waiting Buffers so that only one responder is needed
581         waitForBufInfo {
582                 this.deprecated(thisMethod, Buffer.findRespondingMethodFor(\cache));
583         }
585         resetBufferAutoInfo {
586                 this.deprecated(thisMethod, Meta_Buffer.findRespondingMethodFor(\clearServerCaches));
587                 Buffer.clearServerCaches(this);
588         }
590         cachedBuffersDo { |func| Buffer.cachedBuffersDo(this, func) }
591         cachedBufferAt { |bufnum| ^Buffer.cachedBufferAt(this, bufnum) }
593         inputBus {
594                 ^Bus(\audio, this.options.numOutputBusChannels, this.options.numInputBusChannels, this);
595         }
597         outputBus {
598                 ^Bus(\audio, 0, this.options.numOutputBusChannels, this);
599         }
601         startAliveThread { arg delay=0.0;
602                 this.addStatusWatcher;
603                 ^aliveThread ?? {
604                         aliveThread = Routine({
605                                 // this thread polls the server to see if it is alive
606                                 delay.wait;
607                                 loop({
608                                         this.status;
609                                         aliveThreadPeriod.wait;
610                                         this.serverRunning = alive;
611                                         alive = false;
612                                 });
613                         });
614                         AppClock.play(aliveThread);
615                         aliveThread
616                 }
617         }
618         stopAliveThread {
619                 if( aliveThread.notNil, {
620                         aliveThread.stop;
621                         aliveThread = nil;
622                 });
623                 if( statusWatcher.notNil, {
624                         statusWatcher.free;
625                         statusWatcher = nil;
626                 });
627         }
628         aliveThreadIsRunning {
629                 ^aliveThread.notNil and: {aliveThread.isPlaying}
630         }
631         *resumeThreads {
632                 set.do({ arg server;
633                         server.stopAliveThread;
634                         server.startAliveThread(server.aliveThreadPeriod);
635                 });
636         }
638         boot { arg startAliveThread=true, recover=false;
639                 var resp;
640                 if (serverRunning, { "server already running".inform; ^this });
641                 if (serverBooting, { "server already booting".inform; ^this });
643                 serverBooting = true;
644                 if(startAliveThread, { this.startAliveThread });
645                 if(recover) { this.newNodeAllocators } { this.newAllocators };
646                 bootNotifyFirst = true;
647                 this.doWhenBooted({
648                         serverBooting = false;
649                         if (sendQuit.isNil) {
650                                 sendQuit = not(this.inProcess) and: {this.isLocal};
651                         };
653                         if (this.inProcess) {
654                                 serverInterface = ServerShmInterface(thisProcess.pid);
655                         } {
656                                 if (isLocal) {
657                                         serverInterface = ServerShmInterface(addr.port);
658                                 }
659                         };
661                         this.initTree;
662                         (volume.volume != 0.0).if({
663                                 volume.play;
664                                 });
665                 });
666                 if (remoteControlled.not, {
667                         "You will have to manually boot remote server.".inform;
668                 },{
669                         this.bootServerApp;
670                 });
671         }
673         bootServerApp {
674                 if (inProcess, {
675                         "booting internal".inform;
676                         this.bootInProcess;
677                         //alive = true;
678                         //this.serverRunning = true;
679                         pid = thisProcess.pid;
680                 },{
681                         if (serverInterface.notNil) {
682                                 serverInterface.disconnect;
683                                 serverInterface = nil;
684                         };
686                         pid = (program ++ options.asOptionsString(addr.port)).unixCmd;
687                         //unixCmd(program ++ options.asOptionsString(addr.port)).postln;
688                         ("booting " ++ addr.port.asString).inform;
689                 });
690         }
692         reboot { arg func; // func is evaluated when server is off
693                 if (isLocal.not) { "can't reboot a remote server".inform; ^this };
694                 if(serverRunning) {
695                         Routine.run {
696                                 this.quit;
697                                 this.wait(\done);
698                                 0.1.wait;
699                                 func.value;
700                                 this.boot;
701                         }
702                 } {
703                         func.value;
704                         this.boot
705                 }
706         }
708         status {
709                 addr.sendStatusMsg
710         }
712         notify_ { |flag = true|
713                 notify = flag;
714                 if(flag){
715                         if(serverRunning){
716                                 this.sendNotifyRequest(true);
717                                 notified = true;
718                                 "Receiving notification messages from server %\n".postf(this.name);
719                         }
720                 }{
721                         this.sendNotifyRequest(false);
722                         notified = false;
723                         "Switched off notification messages from server %\n".postf(this.name);
724                 }
725         }
727         sendNotifyRequest { arg flag=true;
728                 notified = flag;
729                 addr.sendMsg("/notify", flag.binaryValue);
730         }
732         dumpOSC { arg code=1;
733                 /*
734                         0 - turn dumping OFF.
735                         1 - print the parsed contents of the message.
736                         2 - print the contents in hexadecimal.
737                         3 - print both the parsed and hexadecimal representations of the contents.
738                 */
739                 dumpMode = code;
740                 this.sendMsg(\dumpOSC,code);
741         }
743         quit {
744                 var     serverReallyQuitWatcher, serverReallyQuit = false;
745                 statusWatcher !? {
746                         statusWatcher.disable;
747                         if(notified) {
748                                 serverReallyQuitWatcher = OSCFunc({ |msg|
749                                         if(msg[1] == '/quit') {
750                                                 statusWatcher.enable;
751                                                 serverReallyQuit = true;
752                                                 serverReallyQuitWatcher.free;
753                                         };
754                                 }, '/done', addr);
755                                 // don't accumulate quit-watchers if /done doesn't come back
756                                 AppClock.sched(3.0, {
757                                         if(serverReallyQuit.not) {
758                                                 "Server % failed to quit after 3.0 seconds.".format(this.name).warn;
759                                                 serverReallyQuitWatcher.free;
760                                         };
761                                 });
762                         };
763                 };
764                 addr.sendMsg("/quit");
765                 if (inProcess, {
766                         this.quitInProcess;
767                         "quit done\n".inform;
768                 },{
769                         "/quit sent\n".inform;
770                 });
771                 alive = false;
772                 notified = false;
773                 dumpMode = 0;
774                 pid = nil;
775                 serverBooting = false;
776                 sendQuit = nil;
777                 this.serverRunning = false;
778                 if(scopeWindow.notNil) { scopeWindow.quit };
779                 RootNode(this).freeAll;
780                 volume.isPlaying.if({
781                         volume.free
782                         });
783                 this.newAllocators;
784         }
786         *quitAll {
787                 set.do({ arg server;
788                         if (server.sendQuit === true) {
789                                 server.quit
790                         };
791                 })
792         }
793         *killAll {
794                 // if you see Exception in World_OpenUDP: unable to bind udp socket
795                 // its because you have multiple servers running, left
796                 // over from crashes, unexpected quits etc.
797                 // you can't cause them to quit via OSC (the boot button)
799                 // this brutally kills them all off
800                 thisProcess.platform.killAll(this.program.basename);
801                 this.quitAll;
802         }
803         freeAll {
804                 this.sendMsg("/g_freeAll", 0);
805                 this.sendMsg("/clearSched");
806                 this.initTree;
807         }
809         *freeAll { arg evenRemote = false;
810                 if (evenRemote) {
811                         set.do { arg server;
812                                 if ( server.serverRunning ) { server.freeAll }
813                         }
814                 } {
815                         set.do { arg server;
816                                 if (server.isLocal and:{ server.serverRunning }) { server.freeAll }
817                         }
818                 }
819         }
821         *hardFreeAll { arg evenRemote = false;
822                 if (evenRemote) {
823                         set.do { arg server;
824                                 server.freeAll
825                         }
826                 } {
827                         set.do { arg server;
828                                 if (server.isLocal) { server.freeAll }
829                         }
830                 }
831         }
833         *allRunningServers {
834                 ^this.all.select(_.serverRunning)
835         }
837         // bundling support
840         openBundle { arg bundle;        // pass in a bundle that you want to
841                                                         // continue adding to, or nil for a new bundle.
842                 if(addr.hasBundle) {
843                         bundle = addr.bundle.addAll(bundle);
844                         addr.bundle = []; // debatable
845                 };
846                 addr = BundleNetAddr.copyFrom(addr, bundle);
847         }
848         closeBundle { arg time; // set time to false if you don't want to send.
849                 var bundle;
850                 if(addr.hasBundle) {
851                         bundle = addr.closeBundle(time);
852                         addr = addr.saveAddr;
853                 } {
854                         "there is no open bundle.".warn
855                 };
856                 ^bundle;
857         }
858         makeBundle { arg time, func, bundle;
859                 this.openBundle(bundle);
860                 try {
861                         func.value(this);
862                         bundle = this.closeBundle(time);
863                 }{|error|
864                         addr = addr.saveAddr; // on error restore the normal NetAddr
865                         error.throw
866                 }
867                 ^bundle
868         }
869         bind { arg func;
870                 ^this.makeBundle(this.latency, func)
871         }
873         // internal server commands
874         bootInProcess {
875                 ^options.bootInProcess;
876         }
877         quitInProcess {
878                 _QuitInProcessServer
879                 ^this.primitiveFailed
880         }
881         allocSharedControls { arg numControls=1024;
882                 _AllocSharedControls
883                 ^this.primitiveFailed
884         }
885         setSharedControl { arg num, value;
886                 _SetSharedControl
887                 ^this.primitiveFailed
888         }
889         getSharedControl { arg num;
890                 _GetSharedControl
891                 ^this.primitiveFailed
892         }
894         // recording output
895         record { |path|
896                 if(recordBuf.isNil){
897                         this.prepareForRecord(path);
898                         Routine({
899                                 this.sync;
900                                 this.record;
901                         }).play;
902                 }{
903                         if(recordNode.isNil){
904                                 recordNode = Synth.tail(RootNode(this), "server-record",
905                                                 [\bufnum, recordBuf.bufnum]);
906                                 CmdPeriod.doOnce {
907                                         recordNode = nil;
908                                         if (recordBuf.notNil) { recordBuf.close {|buf| buf.free; }; recordBuf = nil; };
909                                 }
910                         } {
911                                 recordNode.run(true)
912                         };
913                         "Recording: %\n".postf(recordBuf.path);
914                 };
915         }
917         pauseRecording {
918                 recordNode.notNil.if({ recordNode.run(false); "Paused".postln }, { "Not Recording".warn });
919         }
921         stopRecording {
922                 if(recordNode.notNil) {
923                         recordNode.free;
924                         recordNode = nil;
925                         recordBuf.close({ arg buf; buf.free; });
926                         "Recording Stopped: %\n".postf(recordBuf.path);
927                         recordBuf = nil;
928                 } {
929                         "Not Recording".warn
930                 };
931         }
933         prepareForRecord { arg path;
934                 if (path.isNil) {
935                         if(File.exists(thisProcess.platform.recordingsDir).not) {
936                                 thisProcess.platform.recordingsDir.mkdir
937                         };
939                         // temporary kludge to fix Date's brokenness on windows
940                         if(thisProcess.platform.name == \windows) {
941                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Main.elapsedTime.round(0.01) ++ "." ++ recHeaderFormat;
943                         } {
944                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ recHeaderFormat;
945                         };
946                 };
947                 recordBuf = Buffer.alloc(this, 65536, recChannels,
948                         {arg buf; buf.writeMsg(path, recHeaderFormat, recSampleFormat, 0, 0, true);},
949                         this.options.numBuffers + 1); // prevent buffer conflicts by using reserved bufnum
950                 recordBuf.path = path;
951                 SynthDef("server-record", { arg bufnum;
952                         DiskOut.ar(bufnum, In.ar(0, recChannels))
953                 }).send(this);
954         }
956         // CmdPeriod support for Server-scope and Server-record and Server-volume
957         cmdPeriod {
958                 addr = addr.recover;
959                 this.changed(\cmdPeriod);
960         }
962         doOnServerTree {
963                 if(scopeWindow.notNil) { scopeWindow.run }
964         }
966         defaultGroup { ^Group.basicNew(this, 1) }
968         queryAllNodes { arg queryControls = false;
969                 var resp, done = false;
970                 if(isLocal, {this.sendMsg("/g_dumpTree", 0, queryControls.binaryValue);}, {
971                         resp = OSCFunc({ arg msg;
972                                 var i = 2, tabs = 0, printControls = false, dumpFunc;
973                                 if(msg[1] != 0, {printControls = true});
974                                 ("NODE TREE Group" + msg[2]).postln;
975                                 if(msg[3] > 0, {
976                                         dumpFunc = {|numChildren|
977                                                 var j;
978                                                 tabs = tabs + 1;
979                                                 numChildren.do({
980                                                         if(msg[i + 1] >=0, {i = i + 2}, {
981                                                                 i = i + 3 + if(printControls, {msg[i + 3] * 2 + 1}, {0});
982                                                         });
983                                                         tabs.do({ "   ".post });
984                                                         msg[i].post; // nodeID
985                                                         if(msg[i + 1] >=0, {
986                                                                 " group".postln;
987                                                                 if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
988                                                         }, {
989                                                                 (" " ++ msg[i + 2]).postln; // defname
990                                                                 if(printControls, {
991                                                                         if(msg[i + 3] > 0, {
992                                                                                 " ".post;
993                                                                                 tabs.do({ "   ".post });
994                                                                         });
995                                                                         j = 0;
996                                                                         msg[i + 3].do({
997                                                                                 " ".post;
998                                                                                 if(msg[i + 4 + j].isMemberOf(Symbol), {
999                                                                                         (msg[i + 4 + j] ++ ": ").post;
1000                                                                                 });
1001                                                                                 msg[i + 5 + j].post;
1002                                                                                 j = j + 2;
1003                                                                         });
1004                                                                         "\n".post;
1005                                                                 });
1006                                                         });
1007                                                 });
1008                                                 tabs = tabs - 1;
1009                                         };
1010                                         dumpFunc.value(msg[3]);
1011                                 });
1012                                 done = true;
1013                         }, '/g_queryTree.reply', addr).oneShot;
1014                         this.sendMsg("/g_queryTree", 0, queryControls.binaryValue);
1015                         SystemClock.sched(3, {
1016                                 done.not.if({
1017                                         resp.free;
1018                                         "Remote server failed to respond to queryAllNodes!".warn;
1019                                 });
1020                         });
1021                 })
1022         }
1023         printOn { |stream|
1024                 stream << name;
1025         }
1026         storeOn { arg stream;
1027                 var codeStr = this.switch (
1028                         Server.default,                         { if (sync_s) { "s" } { "Server.default" } },
1029                         Server.local,                           { "Server.local" },
1030                         Server.internal,                        { "Server.internal" },
1031                         { "Server.fromName(" + name.asCompileString + ")" }
1032                 );
1033                 stream << codeStr;
1034         }
1036         archiveAsCompileString { ^true }
1037         archiveAsObject { ^true }
1039         volume_ {arg newVolume;
1040                 volume.volume_(newVolume);
1041                 }
1043         mute {
1044                 volume.mute;
1045                 }
1047         unmute {
1048                 volume.unmute;
1049         }
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")
1059                 } {
1060                         ^serverInterface.getControlBusValue(busIndex)
1061                 }
1062         }
1064         getControlBusValues {|busIndex, busChannels|
1065                 if (serverInterface.isNil) {
1066                         error("Server-getControlBusValues only supports local servers")
1067                 } {
1068                         ^serverInterface.getControlBusValues(busIndex, busChannels)
1069                 }
1070         }
1072         setControlBusValue {|busIndex, value|
1073                 if (serverInterface.isNil) {
1074                         error("Server-getControlBusValue only supports local servers")
1075                 } {
1076                         ^serverInterface.setControlBusValue(busIndex, value)
1077                 }
1078         }
1080         setControlBusValues {|busIndex, valueArray|
1081                 if (serverInterface.isNil) {
1082                         error("Server-getControlBusValues only supports local servers")
1083                 } {
1084                         ^serverInterface.setControlBusValues(busIndex, valueArray)
1085                 }
1086         }