314 lines
11 KiB
Text
314 lines
11 KiB
Text
|
#lang scribble/manual
|
||
|
@(require (for-label racket))
|
||
|
|
||
|
@title{Group}
|
||
|
Client-side representation of a group node on the server@section{categories}
|
||
|
Server>Nodes, Server>Abstractions
|
||
|
|
||
|
@section{description}
|
||
|
|
||
|
A Group is the client-side representation of a group node on the server, which is a collection of other nodes organized as a linked list.
|
||
|
The Nodes within a Group may be controlled together, and may be both link::Classes/Synth::s and other Groups.
|
||
|
Groups are thus useful for controlling a number of nodes at once, and when used as targets can be very helpful in controlling order of execution. (See link::Guides/Order-of-execution:: for more details on this topic).
|
||
|
|
||
|
For more on the crucial distinction between client objects and server nodes, see link::Guides/ClientVsServer::. For information on creating nodes without using objects, see link::Guides/NodeMessaging::.
|
||
|
|
||
|
N.B. Group is a subclass of Node, and thus many of its most useful and important methods are documented in the link::Classes/Node:: help file. Please refer to it for more information.
|
||
|
|
||
|
@section{subsection}
|
||
|
RootNode and the default group
|
||
|
|
||
|
When a Server is booted there is a top level group with an ID of zero that defines the root of the tree. This is represented by a subclass of Group: link::Classes/RootNode::.
|
||
|
If the Server was booted from within SCLang (as opposed to from the command line) then a default_group with an ID of 1 will be automatically created. This group is the default enclosing group for all Nodes, i.e. it's what you get if you don't specify.
|
||
|
In general you should create new Nodes within the default group of a Server rather than in its RootNode.
|
||
|
See link::Classes/Server::, link::Reference/default_group:: and link::Classes/RootNode:: for more detail.
|
||
|
|
||
|
@section{subsection}
|
||
|
Bundling
|
||
|
|
||
|
Some of the methods below have two versions: a regular one which sends its corresponding message to the server immediately, and one which returns the message in an link::Classes/Array:: so that it can be added to a bundle.
|
||
|
It is also possible to capture the messages generated by the regular methods using Server's automated bundling capabilities. See link::Classes/Server:: and link::Guides/Bundled-Messages:: for more detail.
|
||
|
|
||
|
@section{classmethods}
|
||
|
|
||
|
@section{private}
|
||
|
creationCmd
|
||
|
|
||
|
@section{subsection}
|
||
|
Creation with Immediate Instantiation on the Server
|
||
|
|
||
|
@section{method}
|
||
|
new
|
||
|
Create and return a Group.
|
||
|
|
||
|
@section{argument}
|
||
|
target
|
||
|
A target for this Group. If target is not a Group or Synth, it will be converted as follows: If it is a Server, it will be converted to the default_group of that server. If it is nil, to the default_group of the default Server.
|
||
|
@section{argument}
|
||
|
addAction
|
||
|
one of the following Symbols:
|
||
|
@section{table}
|
||
|
|
||
|
## \addToHead || (the default) add at the head of the group specified by target
|
||
|
## \addToTail || add at the tail of the group specified by target
|
||
|
## \addAfter || add immediately after target in its server's node order
|
||
|
## \addBefore || add immediately before target in its server's node order
|
||
|
## \addReplace || replace target and take its place in its server's node order
|
||
|
::
|
||
|
Note: A Synth is not a valid target for \addToHead and \addToTail.
|
||
|
@section{discussion}
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
s.boot;
|
||
|
g = Group.new; // add a Group at the head of the default Server's default group
|
||
|
h = Group.new(g, \addAfter);
|
||
|
s.queryAllNodes; // note the Group within the default group (ID 1)
|
||
|
g.free; h.free;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
@section{subsection}
|
||
|
Convenience methods for add actions
|
||
|
The following convenience methods correspond to the add actions above:
|
||
|
|
||
|
@section{method}
|
||
|
after
|
||
|
Create and return a Group and add it immediately after aNode.
|
||
|
|
||
|
@section{method}
|
||
|
before
|
||
|
Create and return a Group and add it immediately before aNode.
|
||
|
|
||
|
@section{method}
|
||
|
head
|
||
|
Create and return a Group. If aGroup is a Group add it at the head of that group. If it is a Server, add it at the head of the default_group of that server. If it is nil, add it at the head of the default_group of the default Server.
|
||
|
|
||
|
@section{method}
|
||
|
tail
|
||
|
Create and return a Group. If aGroup is a Group add it at the tail of that group. If it is a Server, add it at the tail of the default_group of that server. If it is nil, add it at the tail of the default_group of the default Server.
|
||
|
|
||
|
@section{method}
|
||
|
replace
|
||
|
Create and return a Group and use it to replace nodeToReplace, taking its place in its server's node order.
|
||
|
|
||
|
@section{subsection}
|
||
|
Creation without Instantiation on the Server
|
||
|
|
||
|
For use in message bundles it is also possible to create a Group object in the client app without immediately creating a group node on the server. Once done one can call methods which create messages to add to a bundle, which when sent to the server will instantiate the group or perform other operations. (See strong::Control::, below.)
|
||
|
|
||
|
@section{method}
|
||
|
basicNew
|
||
|
Create and return a Group object without creating a group node on the server. (This method is inherited from Node and is documented here only for convenience.)
|
||
|
@section{argument}
|
||
|
server
|
||
|
An optional instance of Server. If nil this will default to the default Server.
|
||
|
@section{argument}
|
||
|
nodeID
|
||
|
An optional node ID number. If not supplied one will be generated by the Server's NodeIDAllocator. Normally you should not need to supply an ID.
|
||
|
@section{discussion}
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
s.boot;
|
||
|
g = Group.basicNew(s); // Create without sending
|
||
|
s.sendBundle(nil, g.newMsg;); // Now send a message; create at the head of s' default group
|
||
|
s.queryAllNodes;
|
||
|
g.free;
|
||
|
::
|
||
|
|
||
|
After creation, use instance methods ]
|
||
|
|
||
|
@racketblock[newMsg, addToHeadMsg, addToTailMsg, addBeforeMsg, addAfterMsg, addReplaceMsg:: to instantiate this synth on the server. See link::#instancemethods#Instance Methods:: below.
|
||
|
|
||
|
|
||
|
]
|
||
|
@section{instancemethods}
|
||
|
|
||
|
|
||
|
@section{subsection}
|
||
|
Creation without Instantiation on the Server
|
||
|
Use class method
|
||
|
@racketblock[basicNew:: to create a Synth without instantiating it on the server. Then use the following instance methods:
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
newMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group. If target is nil, it will default to the default_group of the Server specified in *basicNew when this Group was created. The default addAction is \addToHead. (See *new above for details of addActions.
|
||
|
|
||
|
@section{method}
|
||
|
addToHeadMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group. If aGroup is a Group it will be added at the head of that group. If it is nil, it will be added at the head of the default_group of this Group's server (as specified when *basicNew was called).
|
||
|
|
||
|
@section{method}
|
||
|
addToTailMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group. If aGroup is a Group it will be added at the tail of that group. If it is nil, it will be added at the tail of the default_group of this Group's server (as specified when *basicNew was called).
|
||
|
|
||
|
@section{method}
|
||
|
addBeforeMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group, immediately before aNode.
|
||
|
|
||
|
@section{method}
|
||
|
addAfterMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group, immediately after aNode.
|
||
|
|
||
|
@section{method}
|
||
|
addReplaceMsg
|
||
|
Returns a message of the type g_new which can be bundled. When sent to the server this message will instantiate this group, replacing nodeToReplace in the server's node order.
|
||
|
|
||
|
@section{subsection}
|
||
|
Control and Introspection
|
||
|
|
||
|
For further methods of controlling Groups (set, map, etc.), see the link::Classes/Node:: help file.
|
||
|
|
||
|
@section{method}
|
||
|
moveNodeToHead, moveNodeToHeadMsg
|
||
|
Move aNode to the head of this group
|
||
|
|
||
|
@section{method}
|
||
|
moveNodeToTail, moveNodeToTailMsg
|
||
|
Move aNode to the tail of this group
|
||
|
|
||
|
@section{method}
|
||
|
freeAll, freeAllMsg
|
||
|
Free all the nodes in this group, but do not free this group itself.
|
||
|
|
||
|
@section{method}
|
||
|
deepFree, deepFreeMsg
|
||
|
Free all Synths in the group, and all Synths in any enclosed groups, but do not free this group or any of its enclosed groups.
|
||
|
|
||
|
@section{method}
|
||
|
dumpTree
|
||
|
Post a representation of this group's node subtree. (Sends a message of the type g_dumpTree.) If
|
||
|
@racketblock[postControls:: is true, then the current Control (arg) values for any synths contained in this group will be posted as well. The default is false.
|
||
|
|
||
|
]
|
||
|
@section{method}
|
||
|
queryTree
|
||
|
Queries the server for a message describing this group's node subtree. (Sends a message of the type g_queryTree.) This reply is passed to the action function as an argument. See g_queryTree in Server-Command-Reference or the example below for information on how the reply is structured.
|
||
|
|
||
|
|
||
|
@section{Examples}
|
||
|
|
||
|
|
||
|
@racketblock[
|
||
|
s.boot;
|
||
|
|
||
|
(
|
||
|
SynthDef("help-Group-moto-rev", { arg out=0,freq=100,ffreq=120;
|
||
|
var x;
|
||
|
x = RLPF.ar(LFPulse.ar(SinOsc.kr(0.2, 0, 10, freq), [0,0.1], 0.1),
|
||
|
ffreq, 0.1).clip2(0.4);
|
||
|
Out.ar(out, x);
|
||
|
}).add;
|
||
|
|
||
|
|
||
|
SynthDef("help-Group-wah", { arg out, rate = 1.5, cfreq = 1400, mfreq = 1200, rq=0.1;
|
||
|
var zin, zout, q;
|
||
|
|
||
|
zin = In.ar(out, 2);
|
||
|
cfreq = Lag3.kr(cfreq, 0.1);
|
||
|
mfreq = Lag3.kr(mfreq, 0.1);
|
||
|
q = Ramp.kr(rq, 0.1);
|
||
|
zout = RLPF.ar(zin, LFNoise1.kr(rate, mfreq, cfreq), q, 10).distort
|
||
|
* 0.15;
|
||
|
|
||
|
// replace the incoming bus with the effected version
|
||
|
ReplaceOut.ar( out , zout );
|
||
|
|
||
|
}).add;
|
||
|
)
|
||
|
|
||
|
g = Group.new;
|
||
|
|
||
|
(
|
||
|
l = Array.fill(3,{
|
||
|
// random freq for each synth, added to g at the head
|
||
|
Synth("help-Group-moto-rev",["out",0,"freq",rrand(10,120)],g,\addToHead);
|
||
|
});
|
||
|
)
|
||
|
|
||
|
// set all controls that match "ffreq" in all nodes in g to 90
|
||
|
g.set("ffreq",300);
|
||
|
|
||
|
g.set("freq",80);
|
||
|
|
||
|
// since we stored the Synths in an Array, we can also control them individually
|
||
|
(
|
||
|
r = Routine({
|
||
|
inf.do({
|
||
|
l.do({ arg node;
|
||
|
node.set("freq",rrand(10,120));
|
||
|
1.0.wait;
|
||
|
});
|
||
|
})
|
||
|
});
|
||
|
|
||
|
r.play;
|
||
|
)
|
||
|
|
||
|
// g is in a group too. Since we didn't specify it's the default group (ID 1) of the default Server
|
||
|
g.group.inspect;
|
||
|
|
||
|
// asking a wah to go order-of-execution after g, in the same group as g.
|
||
|
x = Synth.after(g,"help-Group-wah",["out",0]);
|
||
|
|
||
|
// now dump my tree to confirm
|
||
|
g.dumpTree;
|
||
|
|
||
|
x.free;
|
||
|
|
||
|
// free all nodes in g, but not g itself
|
||
|
g.freeAll;
|
||
|
|
||
|
// don't forget the Routine is still running...
|
||
|
r.stop;
|
||
|
|
||
|
// oh, and set l to nil so the Synths and Array can be garbage collected
|
||
|
l = nil;
|
||
|
|
||
|
// and i'm still on the server, its just my children that were freed
|
||
|
g.query;
|
||
|
|
||
|
// don't need the individual synth objects this time
|
||
|
(
|
||
|
3.do({
|
||
|
// random freq for each synth, added to g at the head
|
||
|
Synth("help-Group-moto-rev",["out",0,"freq",rrand(10,1200)],g,\addToHead);
|
||
|
});
|
||
|
)
|
||
|
|
||
|
// now query my tree and post a graph of it (duplicates dumpTree)
|
||
|
// msg format is ['/g_querytree.reply', node1-ID, numChildren, defName, child1-ID, numChildren, ...]
|
||
|
(
|
||
|
g.queryTree({|msg|
|
||
|
var i = 1, tabs = 0, dumpFunc;
|
||
|
("NODE TREE Group" + msg[1]).postln;
|
||
|
if(msg[2] > 0, {
|
||
|
dumpFunc = {|numChildren|
|
||
|
tabs = tabs + 1;
|
||
|
numChildren.do({
|
||
|
i = i + 3;
|
||
|
tabs.do({ " ".post });
|
||
|
msg[i].post;
|
||
|
(" " ++ msg[i + 2]).postln;
|
||
|
if(msg[i + 1] > 0, { dumpFunc.value(msg[i + 1]) });
|
||
|
});
|
||
|
tabs = tabs - 1;
|
||
|
};
|
||
|
dumpFunc.value(msg[2]);
|
||
|
});
|
||
|
});
|
||
|
)
|
||
|
|
||
|
// kill me and my children
|
||
|
g.free;
|
||
|
|
||
|
// see, I'm gone
|
||
|
g.query;
|
||
|
::
|
||
|
|
||
|
]
|
||
|
|
||
|
|