SWDataNetworkClient

This implements an OSC client to a DataNetwork, so that an external SuperCollider client can also be part of the network.

This class is a subclass of SWDataNetwork, so the methods used in that class can be used in this class as well. In some cases methods have an additional argument, which only need to be set, when called internally, so the user interface is the same as when using SWDataNetwork. Access to nodes, slots and their data is just the same as for a local network.

There are some additional methods to send out OSC messages, or for internal use.

Some nomenclature:

A DataNode, or a node, is a collection of data streams which somehow belong together.
For example, this can be representing:

An expected Node is a DataNode that is prepared to be added to the network, but may not be present yet.

A DataSlot, or a slot, is one datastream. So corresponding to the examples of nodes above a slot would represent:

A client: the programming environment of one collaborator hooked up to the network. E.g. Joe, using PureData, or Marije, using SuperCollider, or Vincent, using Processing, or Brett, using Max/MSP.

Subscription: a client can subscribe to a node to receive its data, or subscribe to a single slot of a node. These are called the client's subscriptions.

Setter: a client can provide data to the network by creating a DataNode. The client then becomes the "setter" of the node. Other clients can not set data to the same node.

- Example -


// create a network client:
y = SWDataNetworkClient.new( "192.168.0.104", "myname" );

/* 
The first argument is the IP address of the datanetwork host,
this address is shown in the basic GUI on the host in the lower right corner.

The second argument "myname" is the name by which you (as a client)
will be identified in the network, and should be unique among all
clients (all collaborators in the project). It is used to remember
your subscriptions and "setters" when you reconnect to the host,
e.g. when you restart supercollider (recompile the library) and
re-execute the code.

The client will find the port of the host automatically, and register.
If the host goes down, the client will search for the host, and react
to an announce message from the host to reconnect.

The client will automatically unregister, if you recompile the language,
or quit SuperCollider.
*/


//------------ Querying:

/*
You can make queries about which nodes and which slots are present in
the network.

After you are registered, you will automatically receive info messages
on any new nodes and slots
*/

// query which nodes are present; this sends a message to the host:
y.queryNodes; // the result will print out a lot of informational messages.

// to see which nodes are present (local check,
// not sending a message to the host):
y.nodes;

// query which nodes are expected, this sends a message to the host:
y.queryExpected;

// to see which nodes are expected (local check, not sending
// a message to the host:
y.expectedNodes;

// query which slots are present, this sends a message to the host:
y.querySlots;


// query which other clients are present (sending message to host):
y.queryClients;

// query to which nodes you are subscribed (sending message to host):
y.querySubscriptions;

// query of which nodes you are the setter (sending message to host):
y.querySetters;

// perform all of the queries (sending message to host):
y.queryAll;


//------------ Subscribing to a node:

// subscribe to node 101, by number:
y.subscribeNode( 101 );

// subscribe to node 102 by passing in an instance of SWDataNode as an argument:
y.subscribeNode( y.nodes[102] );


// a lot of the interaction with the DataNetwork can also be
//  done through a graphical user interface:
y.makeGui;

// -------- working with the data node:

// get a reference to a DataNode in a variable:
a = y.nodes[102];

// if the node has a label, you acces it by its label:
a = y[\minibee102];

// access the values of all slots of the node:
a.value;

(
SynthDef(\swexample,{ |out=0,amp=0.1,freq=400|
	Out.ar( out, SinOsc.ar( freq, 0, amp ) );
}).send(s)
)

// use it at synth instantiation:
b = Synth.new( \swexample, [\amp,a.value ] );

// set it again:
b.set( \amp, a.value );

// free the synth
b.free;

// instead of setting it manually each time, you can assign
// an action to the data node to do this automatically:

b = Synth.new( \swexample, [\amp,a.value ] );

a.action= { |data| b.set( \amp, data )};

// another method is to put the data automatically on a bus,
// and grab the data from there:

// reset the action:
a.action = {};

// create a bus:
a.createBus(s);

// map the amplitude of b to the bus:
b.map( \amp, a.bus );

b.free;

// alternatively, you can use In.kr in your synthdef to read
// from the bus:
(
SynthDef(\swexampleBus,{ |out=0,ampbus=0,freq=400|
	Out.ar( out, SinOsc.ar( freq, 0, In.kr( ampbus, 1 ) ) );
}).send(s)
)

b = Synth.new( \swexampleBus, [\ampbus,a.bus ] );

b.free;


// -------- working with a single slot of a data node:

// DataNodes can have multiple channels of data, so the above
// approach is mostly useful for multichannel control of data.
// You can also work with a single slot of a node, and control a synth:

// get a reference to a DataSlot in a variable:
a = y.nodes[102].slots[0];

// if the slot has a label, you acces it by its label:
a = y[\minibee102_slot0];

// access the value of the slot:
a.value;

(
SynthDef(\swexample,{ |out=0,amp=0.1,freq=400|
	Out.ar( out, SinOsc.ar( freq, 0, amp ) );
}).send(s)
)

// use it at synth instantiation:
b = Synth.new( \swexample, [\amp,a.value ] );

// set it again:
b.set( \amp, a.value );

// free the synth
b.free;

// instead of setting it manually each time, you can assign
// an action to the data node to do this automatically:

b = Synth.new( \swexample, [\amp,a.value ] );

a.action= { |data| b.set( \amp, data )};

// another method is to put the data automatically on a bus,
// and grab the data from there:

// reset the action:
a.action = {};

// create a bus:
a.createBus(s);

// map the amplitude of b to the bus:
b.map( \amp, a.bus );

b.free;

// alternatively, you can use In.kr in your synthdef:
(
SynthDef(\swexampleBus,{ |out=0,ampbus=0,freq=400|
	Out.ar( out, SinOsc.ar( freq, 0, In.kr( ampbus, 1 ) ) );
}).send(s)
)

b = Synth.new( \swexampleBus, [\ampbus,a.bus ] );

b.free;

- Methods -

*new( hostip, name, reg )
Create a new client, connecting to hostip. hostip should be a hostname or string (the NetAddr of it is created in the class). name is the name that the client will have in the network. reg is a flag, whether or not to register rightaway to the host.
makeGui
Create a GUI for the client.
setHost ( ip, port )
Set the ip and port of the host. This is the method called upon receiving the datanetwork announce message. This then calls resetHost.
resetHost ( addr )
Set the ip and port of the host. This will remove and then re-add the OSC responders with the new host NetAddr.
findHost ( ip )
Try to find the host, based on the given IP (if none is given, the method will use the IP of the previous host). The client will then get the port number from the file in which it is written by the host (access with curl through http).
lostHost ( )
Posts a message when the DataNetwork host has quit.
tryReconnect
Try to find the host (with the method findHost), and register again to that host.
autoregister ( )
autoregister_ ( )
If true, when the host NetAddr is reset with setHost, the client will automatically register to it.
host ( )
Retrieve the current host.
myaddr ( )
Retrieve own address.
lasttime ( )
Last time the client received a ping message.
worryAboutTime ( )
Method called to check whether the last time a pong was received is not too late. If it has been too long, the client tries to register anew.
registered ( )
If set, then the client has been registered succesfully
addExpected ( id, label, size, fromnw )
Add an expected node to the network. This is the same as in SWDataNetwork. However, if called from an OSC message, then fromnw is set to true and the changes are not sent back to the host.
setData ( id, data, fromnw )
Set data in the network. This is the same as in SWDataNetwork. However, if called from an OSC message, then fromnw is set to true and the changes are not sent back to the host.
add ( key, slot, fromnw )
Add a label in the spec of the network. This is the same as in SWDataNetwork. However, if called from an OSC message, then fromnw is set to true and the changes are not sent back to the host.
removeNode ( id, fromnw )
Remove datanode in the network. This is the same as in SWDataNetwork. However, if called from an OSC message, then fromnw is set to true and the changes are not sent back to the host.

- Methods to interface with the remote host

register ( )
Register to the network.
unregister ( )
Unregister from the network. This method is called automatically when sclang stops (or library is recompiled).

- Sending query messages -

queryAll ( )
Execute all of the queries below.
queryExpected ( )
Query which nodes are expected in the network.
queryNodes ( )
Query which nodes are present in the network.
querySlots ( )
Query which slots are present in the network.
querySetters ( )
Query of which nodes the client is a setter.
querySubscriptions ( )
Query to which nodes and slots the client is subscribed.
queryClients ( )
Query which clients are in the network.

- Subscribing and unsubscribing to nodes -

subscribeAll ( )
Subscribe to all nodes.
unsubscribeAll ( )
Unsubscribe from all nodes currently subscribed to.
removeAll ( )
Remove all nodes this client is a setter of.
subscribeNode ( node )
Subscribe to a node. node can be an instance of SWDataNode or a node ID.
unsubscribeNode ( node )
Unsubscribe from a node. node can be an instance of SWDataNode or a node ID.
subscribeSlot ( slot )
Subscribe to a slot. slot can be an instance of SWDataNode or a slot ID (Array of size 2).
unsubscribeSlot ( slot )
Unsubscribe from a slot. slot can be an instance of SWDataNode or a slot ID (Array of size 2).
getNode ( node )
Get data from a node just one single time. node can be an instance of SWDataNode or a node ID.
getSlot ( slot )
Get data from a slot just one single time. slot can be an instance of SWDataNode or a slot ID (Array of size 2).

- Private methods called from OSC messages; these message automatically update the data in the network -

nodeInfo ( msg )
slotInfo ( msg )
nodeData ( msg )
slotData ( msg )

- Private methods called from OSC messages; these message automatically post an informative message in the post window -

unsubscribeNodeInfo ( msg )
unsubscribeSlotInfo ( msg )
subscribeNodeInfo ( msg )
subscribeSlotInfo ( msg )
clientInfo ( msg )
setterInfo ( msg )

- Private methods -

labelNode ( node )
private - This method is called when this client labels a node in the network, using the add method. This method sends out an OSC message to the host.
labelSlot ( slot )
private - This method is called when this client labels a slot in the network, using the add method. This method sends out an OSC message to the host.
sendData ( id, data )
private - This method is called when this client sets data to the network. This method sends out an OSC message to the host with the data.
sendPong ( )
private - sends a pong to the host. This method is called in response to receiving a ping message.
registered_ ( )
private - set the registered flag
addResponders ( )
private - Add all OSC responders
removeResponders ( )
private - Remove all OSC responders




Marije Baalman 2009-03-16