SharedServer represents a (remote) server that several clients can play on.



Several clients can play on the same remote SharedServer; 

they keep can their node IDs separate by using DIFFERENT clientIDs. 

[Bus allocators could do the same - not done yet!]


Each SharedServer has its own group on the remote server, so it can 

freeAll its own synths with cmd-period. 



example: 


// make two server objects, same options:

(

o = SharedServerOptions.new

.numAudioBusChannels_(1024)

.numControlBusChannels_(4096)

.numInputBusChannels_(42)

.numOutputBusChannels_(42)

.memSize_(8192 * 32)

.numClients_(8) // change if needed

// remoteControlVolume_( true)

;

)

// make one server

t = SharedServer(\t1_shared, NetAddr("localhost", 57111), o, clientID: 1) 


// only one server should do this, 

// all others do x.serverRunning_(true);

t.boot;

t.makeWindow;



// typically on a different computer, another server object

// pointing to the same remote scsynth:

(

u = SharedServer(\shared_U2, NetAddr("localhost", 57111), o, clientID: 2);

// scsynth is the same for t and u, so 

// u is already booted, but does not know it yet.


u.serverRunning_(true); //  tell it it is running

u.initTree; // init its myGroup

u.makeWindow; // and make a window for it

)



// Node/Group tests: 


t.queryAllNodes

// posts: 

NODE TREE Group 0

  1 group

101 group // 100 + clientID - this is t's myGroup

102 group // and this is u's myGroup


Synth(\default, nil, t); // play on t

t.queryAllNodes


t.freeAll // t can remove its synths with freeAll


Synth(\default, nil, u); // play on u

t.queryAllNodes


t.freeAll //  t can't kill it

u.freeAll // u can!


Synth(\default, nil, t);


u.freeAll(true); // in EMERGENCIES u can hard-freeAll with a flag true. 


// BUT - now t's myGroup is gone: 

t.queryAllNodes


// so t has to run initTree to recover .. ugly! 

t.initTree;

t.queryAllNodes;

// maybe initTree should notify the server objects on the other clients? 

// how best? 



// test several synths 

50.do { Synth(\default, [\freq, rrand(48.0, 60).midicps, \amp, 0.05], t) };

50.do { Synth(\default, [\freq, rrand(72.0, 84).midicps, \amp, 0.05], u) };


t.freeAll;

u.freeAll;




// bus tests : - busses should never overlap between clients! 


Bus.audio(t, 4);

Bus.audio(t, 4);

Bus.control(t, 16);

Bus.control(t, 4);


Bus.audio(u, 4);

Bus.audio(u, 4);

Bus.control(u, 16);

Bus.control(u, 4);



// two proxyspaces can be run on separate server objects (typically on different clients), 

// but play on the same server, and never have bus number conflicts:


p = ProxySpace(t);

p.server


q = ProxySpace(u);

q.server


p[\a] = { CombL.ar(Dust.ar(10).lag(0.001), 0.1, 0.01, 1.4) * LFClipNoise.ar };

p[\a].server

p[\a].play;


q[\b] = { CombL.ar(Dust.ar(10).lag(0.001), 0.1, 0.015, 1.5) * LFClipNoise.ar };

q[\b].server

q[\b].play(1);


t.queryAllNodes


t.freeAll

u.freeAll


t.queryAllNodes



// WFS: which channels are reserved for me? 


t.myOuts;

u.myOuts;


// buffer tests : - buffers should never overlap between clients! 


// allocates the same numbers

b = Buffer.alloc(t, t.sampleRate * 4, 2);

c = Buffer.alloc(u, t.sampleRate * 4, 2);


b.free; c.free;



t.quit; u.quit;

t.boot; 


// non-overlapping now:

b = Buffer.alloc(t, t.sampleRate * 4, 2);

c = Buffer.alloc(u, t.sampleRate * 4, 2);





// you can get all the buffers from the server:


t.getBuffers({ |bufs| "\n YO! SharedServer - found these buffers: ".postln; bufs.printAll });


t.buffers;


u.getBuffers






// Get all buffer infos from the SharedServer


(

b = Array.new(32); 

o.remove; o = OSCresponder(nil, 'bufscan', { |time, resp, msg| 

var bufnum, frames, chans, srate; 

#bufnum, frames, chans, srate = msg.keep(-4).postln; 

if (chans > 0) { b = b.add(Buffer(t, frames, chans, srate, bufnum: bufnum)) };

}).add;


{ var bufnum = Line.kr(0, 1024, 8).round(1);

var trig = HPZ1.kr(bufnum);

SendReply.kr(trig, 'bufscan', [bufnum, BufFrames.kr(bufnum), BufChannels.kr(bufnum), BufSampleRate.kr(bufnum)]);

}.play(t);

)