You are on page 1of 6

Nodes

Server.default=s=Server.internal;

When creating Synths, we have been accepting certain defaults 'behind the scenes'.
In particular, we have not worried about the rendering order for the synthesis
(known as 'execution order'). This can be controlled by specifying Nodes on the
Server, as I shall explain.

Nodes

The Server has a graph of all the running Synths, which may be organised into
Groups for convenience. You can see Synths and Groups being created just by looking
at the Server graphics.

A Node means a Synth or a Group. Whenever you press command+period you reset the
graph, cleaning out all the Synths and Groups you added, that is, clearing all
Nodes.

s.queryAllNodes //run me to see the Nodes on the Server

(There is also a keyboard shortcut of pressing 'N' when the Server window is in
focus)
When the Server determines the audio output, it must run through all the Synths in
the graph, rendering them all. The order it does so is referred to as execution
order.

This can cause trouble if you're not careful with order! Because the inputs of some
Synths depend on calculating some other Synth first: imagine a reverb unit, which
has to follow some spawned sine wave grains. If the reverb is calculated first, it
has no current input to work on.

Demo:

(
s=Server.default;

SynthDef("reverb",{Out.ar(0,CombN.ar(In.ar(0,2),0.1,0.1,4))}).add;
SynthDef("impulses",{Out.ar(0,Impulse.ar([0.9,1.19],0,0.3))}).add;
)

//where is the reverb?


(
Synth("impulses");
Synth("reverb");
)

When you create Synths, you may rely on defaults for the execution order. This is
dangerous (as you'll discover when you find that you aren't getting the sound you
thought you should when you combine say an effects unit and some other instrument),
and it is good practise to be explicit about the graph you create. This is a
headache in some ways because it is extra effort for you; the only consolation you
can take is that SC3 is more efficient because it doesn't have to worry about
taking responsibility over execution order.

What are the defaults?

The initial state of the Node graph on the Server looks like this (do
command+period first to destroy any existing nodes so you have the starting state):

s.queryAllNodes //run me to see the Nodes on the Server

The two default Nodes are convenient Groups for putting your Synths into.

Group(0) is the absolute root of the tree. All new Synths get placed within this
Group somewhere (they might be in subGroups but they will be within the RootNode
Group at the top of the hierarchy).

r=RootNode.new; //this gets a reference to Group(0)

Group(1) was added as an additional default to receive all created Synths, to avoid
cluttering the base of the tree.

Group.basicNew(s, 1); //this gets a reference to Group(1)


If we get a new Synth running:

{SinOsc.ar(440,0,0.1)}.play

If you now Query all Nodes you'll see the Synth was added to Group(1).

Now run this as well:

{SinOsc.ar(880,0,0.1)}.play

Query all nodes again. See how an execution order for rendering is building up.

You might want to reset, then try the earlier impulse and reverb example at this
point, to see what happens with their execution order.

Now note we could have fixed the early example like this:

//with the reverb?


(
Synth("reverb");
Synth("impulses");
)

That works because the defaults are on our side now. Another fix would use the
InFeedback UGen. InFeedback takes the old value from the bus before this
calculation cycle, allowing you to set up feedback cycles, and also circumvent
execution order issues like this.

But in more complex cases, it is highly advised that you take responsibility for
the node graph on the Server, rather than accept defaults. In fact, always taking
this responsibility will mean you never get into trouble, and is a good habit to
fall into. You do this by specifying whenever you create a Synth where to put it in
the Node tree. You can put new Nodes after or before other Nodes, and at the head
or tail of Groups (lists of Synths)

//controlled execution
(
g=Group.basicNew(s,1);
Synth.tail(g, "reverb");
Synth.head(g,"impulses");
)

Note it doesn't matter what order the code runs in now since the placement is
carefully controlled:

//controlled execution
(
g=Group.basicNew(s,1);
Synth.head(g,"impulses");
Synth.tail(g,"reverb");
)

Other ways we might do this:

(
a=Synth("impulses");
Synth.after(a,"reverb");
)

(
a=Synth("reverb");
Synth.before(a,"impulses");
)

You should have noted by now that all Synths get a number (starting from 1000) and
all Groups get a number (starting from 0). The maximum number of Nodes you can have
in your graph is set in ServerOptions and defaults to 1024.

Combining the use of Nodes with the use of Buses; you use Nodes to control
execution order, and buses (via In and Out) to pass audio data between Synths - so
effects units can operate on a bus that another synth has already written to, and
execution order will guarantee that this will all be calculated in the right order.
Have a look at the reverb and impulses SynthDefs above again to see how this works
in practice.

A further perspective:

[Order-of-execution]

You might also like