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);
)