This page was generated from examples/supercollider-objects/node-examples.ipynb.
[1]:
import time
import sc3nb as scn
from sc3nb import Synth
[2]:
sc = scn.startup(with_blip=False)
Starting sclang process... [sclang | start reading ]
Done.
Registering OSC /return callback in sclang... Done.
Loading default sc3nb SynthDefs... Done.
Booting SuperCollider Server... [scsynth | start reading ]
Done.
Nodes (Synth and Group)
One of the most important objects in SuperCollider are Nodes.
To see all Nodes on the Server use
[3]:
sc.server.dump_tree()
NODE TREE Group 0
469762049 group
402653185 group
335544321 group
268435457 group
201326593 group
134217729 group
67108865 group
1 group
You can also get a Python object represenation of the current server state via.
[4]:
root_node = sc.server.query_tree()
root_node
[4]:
Group(0) {} children=[
Group(469762049) s {} children=[],
Group(402653185) s {} children=[],
Group(335544321) s {} children=[],
Group(268435457) s {} children=[],
Group(201326593) s {} children=[],
Group(134217729) s {} children=[],
Group(67108865) s {} children=[],
Group(1) s {} children=[]]
Note that this will send a /g_queryTree
command and parse the /g_queryTree.reply
response of the server. The resulting Group representation will currently not be updated unless you use query_tree
again.
[5]:
root_node.children[-1]
[5]:
Group(1) s {} children=[]
A node is either a Synth or a Group of nodes
Synth
Create and control a Synth
Create a new Synth
[6]:
synth = Synth(name="s2", controls={"freq": 100})
synth
[6]:
<Synth(20001) 's2' s {'freq': 100}>
You can now hear the Synth playing and see it in the NodeTree
[7]:
sc.server.default_group
[7]:
Group(67108865) s {} children=[]
[8]:
sc.server.query_tree()
[8]:
Group(0) {} children=[
Group(469762049) s {} children=[],
Group(402653185) s {} children=[],
Group(335544321) s {} children=[],
Group(268435457) s {} children=[],
Group(201326593) s {} children=[],
Group(134217729) s {} children=[],
Group(67108865) s {} children=[
<Synth(20001) 's2' ~ {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>],
Group(1) s {} children=[]]
Free a synth
[9]:
synth.free()
[9]:
<Synth(20001) 's2' f {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
Start the synth again
[10]:
synth.new()
[10]:
<Synth(20001) 's2' s {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
[11]:
synth.nodeid
[11]:
20001
Calling new
on an already running synth will cause a SuperCollider Server error
[12]:
synth.new()
time.sleep(0.5) # wait some time to ensure the audio server has send the /fail OSC message
Error at SCServer('127.0.0.1', 57110) pid=13566 from scsynth: ('/fail', '/s_new', 'duplicate node ID')
Pause a synth
[13]:
synth.run(False)
[13]:
<Synth(20001) 's2' s {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
[14]:
synth
[14]:
<Synth(20001) 's2' s {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
Run a paused synth
[15]:
synth.run() # default flag for run is True
[15]:
<Synth(20001) 's2' {'freq': 100.0, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
You can also wait for a Synth
[16]:
synth_with_duration = scn.Synth("s1", dict(dur=2))
# wait for the Synth to finish playing
synth_with_duration.wait()
Set / get Synth parameters
Set synth parameters, using any for the following set
calls
set(key, value, ...)
set(list_of_keys_and_values])
set(dict)
[17]:
synth.set("freq", 200)
[17]:
<Synth(20001) 's2' {'freq': 200, 'amp': 0.30000001192092896, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
[18]:
synth.set(["freq", 30, "amp", 0.3])
[18]:
<Synth(20001) 's2' {'freq': 30, 'amp': 0.3, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
[19]:
synth.set({"freq": 130, "amp": 0.1})
[19]:
<Synth(20001) 's2' {'freq': 130, 'amp': 0.1, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
The Synth does save its current control arguments in current_controls
[20]:
synth.current_controls
[20]:
{'freq': 130,
'amp': 0.1,
'num': 4.0,
'pan': 0.0,
'lg': 0.10000000149011612,
'gate': 1.0}
However these are only cached values on the python object. Updating them will only affect the new
call. See below why this can be useful.
[21]:
synth.current_controls['freq'] = 440
synth
[21]:
<Synth(20001) 's2' {'freq': 440, 'amp': 0.1, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
[22]:
synth.free()
synth.new() # The new Synth will be created with freq = 440
[22]:
<Synth(20001) 's2' s {'freq': 440, 'amp': 0.1, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
To see what arguments can be set you can look at the synth_desc
[23]:
synth.synth_desc
[23]:
{'freq': SynthArgument(name='freq', rate='control', default=400.0),
'amp': SynthArgument(name='amp', rate='control', default=0.30000001192092896),
'num': SynthArgument(name='num', rate='control', default=4.0),
'pan': SynthArgument(name='pan', rate='control', default=0.0),
'lg': SynthArgument(name='lg', rate='control', default=0.10000000149011612),
'gate': SynthArgument(name='gate', rate='control', default=1.0)}
You can use get
to see the current value. This will request the current value from the SuperCollider audio server
[24]:
synth.get("freq")
[24]:
440.0
[25]:
synth.get("pan")
[25]:
0.0
This will also update the cached values in current_args
[26]:
synth
[26]:
<Synth(20001) 's2' ~ {'freq': 440.0, 'amp': 0.1, 'num': 4.0, 'pan': 0.0, 'lg': 0.10000000149011612, 'gate': 1.0}>
This is also possible directly with
[27]:
synth.pan
[27]:
0.0
Which can also be used for setting the argument
[28]:
synth.pan = -1
You can also query information about the Synth. Look at Group for more information about these values
[29]:
synth.query()
[29]:
SynthInfo(nodeid=20001, group=67108865, prev_nodeid=-1, next_nodeid=-1)
[30]:
synth.free()
[30]:
<Synth(20001) 's2' f {'freq': 440.0, 'amp': 0.1, 'num': 4.0, 'pan': -1, 'lg': 0.10000000149011612, 'gate': 1.0}>
Keep in mind that getting a synth value is always querying the server.
This means we need to receive a message which cant be done using the Bundler. If you want to use relative values in a Bundler you should use the cached values from current_args
[31]:
with sc.server.bundler() as bundle:
synth.new({"freq": 600})
for _ in range(100):
synth.set(['freq', synth.current_controls['freq'] * 0.99])
bundle.wait(0.05)
synth.free()
[32]:
synth.wait() # wait for synth
Some methods also allow getting the OSC Message instead of sending
[33]:
synth.new(return_msg=True)
[33]:
<OSCMessage("/s_new", ['s2', 20001, 0, 67108865, 'freq', 219.61940002441406, 'amp', 0.10000000149011612, 'num', 4.0, 'pan', -1, 'lg', 0.10000000149011612, 'gate', 1.0])>
Refer to the OSC communication example notebook if you want to learn more about messages and bundles.
Group
Nodes can be grouped and controlled together. This gives you mutliple advantages like
controlling multiple Synth together
specifying the execution order of the nodes. For more details look at Order of nodes
The SuperCollider audio server scsynth does have one root Group with the Node ID 0.
In this root group each user got an default group were all the Nodes of the user (Synths and Groups) should be located
[34]:
sc.server.query_tree()
[34]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[],
Group(1) ~ {} children=[]]
We have the following default Group
[35]:
sc.server.default_group
[35]:
Group(67108865) ~ {} children=[]
Creating Groups
[36]:
g0 = scn.Group()
g0
[36]:
Group(20003) s {} children=[]
[37]:
sc.server.dump_tree()
NODE TREE Group 0
469762049 group
402653185 group
335544321 group
268435457 group
201326593 group
134217729 group
67108865 group
20003 group
1 group
[38]:
g0.free()
[38]:
Group(20003) f {} children=[]
[39]:
sc.server.dump_tree()
NODE TREE Group 0
469762049 group
402653185 group
335544321 group
268435457 group
201326593 group
134217729 group
67108865 group
1 group
[40]:
g0.new()
[40]:
Group(20003) s {} children=[]
[41]:
sc.server.query_tree()
[41]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[ Group(20003) ~ {} children=[]],
Group(1) ~ {} children=[]]
[42]:
sc.server.default_group
[42]:
Group(67108865) ~ {} children=[ Group(20003) ~ {} children=[]]
Create a Group in our new Group
[43]:
g1 = scn.Group(target=g0)
g1
[43]:
Group(20004) s {} children=[]
[44]:
sc.server.query_tree()
[44]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[
Group(20003) ~ {} children=[ Group(20004) ~ {} children=[]]],
Group(1) ~ {} children=[]]
You can get information about you Group via
[45]:
g0.query()
[45]:
GroupInfo(nodeid=20003, group=67108865, prev_nodeid=-1, next_nodeid=-1, head=20004, tail=20004)
The query contains the same information as a Synth query and additionally the head and tail Node of this group.
Notice the special value -1
meaning None
.
[46]:
g1.query()
[46]:
GroupInfo(nodeid=20004, group=20003, prev_nodeid=-1, next_nodeid=-1, head=-1, tail=-1)
Free the node
[47]:
g0.free()
[47]:
Group(20003) f {} children=[ Group(20004) ~ {} children=[]]
[48]:
sc.server.query_tree()
[48]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[],
Group(1) ~ {} children=[]]
Order of Nodes
The execution of the Nodes does plays an important role. For more details look at the SuperCollider Documentation
The Node placement of a Nodes can be controlled by the instantiation arguments
add_action
- An AddAction that specifies where to put Node relative to the target
[49]:
list(scn.AddAction)
[49]:
[<AddAction.TO_HEAD: 0>,
<AddAction.TO_TAIL: 1>,
<AddAction.BEFORE: 2>,
<AddAction.AFTER: 3>,
<AddAction.REPLACE: 4>]
target
- The target of the AddActiongroup
- The group were the Node will be placed
Example of Ordering Nodes
[50]:
g0 = scn.Group()
g0
[50]:
Group(20005) s {} children=[]
[51]:
g1 = scn.Group(target=g0)
g1
[51]:
Group(20006) s {} children=[]
[52]:
s0 = scn.Synth(target=g0, controls={"freq": 200})
[53]:
s1 = scn.Synth(add_action=scn.AddAction.BEFORE, target=s0, controls={"freq": 600})
[54]:
s2 = scn.Synth(add_action=scn.AddAction.TO_TAIL, target=g1, controls={"freq": 1200})
[55]:
sc.server.query_tree()
[55]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[
Group(20005) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20007) 'default' ~ {'freq': 200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]],
Group(1) ~ {} children=[]]
[56]:
s1.move(scn.AddAction.BEFORE, s2)
[56]:
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>
[57]:
sc.server.query_tree()
[57]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[
Group(20005) ~ {} children=[
<Synth(20007) 'default' ~ {'freq': 200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]],
Group(1) ~ {} children=[]]
[58]:
g0
[58]:
Group(20005) ~ {} children=[
<Synth(20007) 'default' ~ {'freq': 200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[59]:
g0.run(False)
[59]:
Group(20005) ~ {} children=[
<Synth(20007) 'default' ~ {'freq': 200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[60]:
g0.run(True)
[60]:
Group(20005) - {} children=[
<Synth(20007) 'default' ~ {'freq': 200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[61]:
g1.run(False)
g1.query_tree()
[61]:
Group(20006) - {} children=[
<Synth(20008) 'default' ~ {'freq': 600.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 1200.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]
[62]:
s0.freq, s1.freq, s2.freq
[62]:
(200.0, 600.0, 1200.0)
[63]:
g0.set("freq", 100)
s0.freq, s1.freq, s2.freq
[63]:
(100.0, 100.0, 100.0)
[64]:
sc.server.dump_tree()
NODE TREE Group 0
469762049 group
402653185 group
335544321 group
268435457 group
201326593 group
134217729 group
67108865 group
20005 group
20007 default
out: 0 freq: 100 amp: 0.10000000149012 pan: 0 gate: 1
20006 group
1 group
[65]:
g1.run(True)
[65]:
Group(20006) - {} children=[
<Synth(20008) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]
[66]:
sc.server.query_tree()
[66]:
Group(0) {} children=[
Group(469762049) ~ {} children=[],
Group(402653185) ~ {} children=[],
Group(335544321) ~ {} children=[],
Group(268435457) ~ {} children=[],
Group(201326593) ~ {} children=[],
Group(134217729) ~ {} children=[],
Group(67108865) ~ {} children=[
Group(20005) ~ {'freq': 100} children=[
<Synth(20007) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {} children=[
<Synth(20008) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]],
Group(1) ~ {} children=[]]
[67]:
g1.set("freq", 440)
s0.freq, s1.freq, s2.freq
[67]:
(100.0, 440.0, 440.0)
[68]:
g0.query_tree()
[68]:
Group(20005) ~ {'freq': 100} children=[
<Synth(20007) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {'freq': 440} children=[
<Synth(20008) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[69]:
g0.move_node_to_tail(s1)
[69]:
Group(20005) ~ {'freq': 100} children=[
<Synth(20007) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {'freq': 440} children=[
<Synth(20008) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[70]:
g0.dump_tree()
[70]:
Group(20005) ~ {'freq': 100} children=[
<Synth(20007) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {'freq': 440} children=[
<Synth(20008) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
<Synth(20009) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[71]:
g1.set("freq", 800)
s0.freq, s1.freq, s2.freq
[71]:
(100.0, 440.0, 800.0)
[72]:
sc.server.default_group.query_tree()
[72]:
Group(67108865) ~ {} children=[
Group(20005) ~ {'freq': 100} children=[
<Synth(20007) 'default' ~ {'freq': 100.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>,
Group(20006) ~ {'freq': 800} children=[
<Synth(20009) 'default' ~ {'freq': 800.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>],
<Synth(20008) 'default' ~ {'freq': 440.0, 'out': 0.0, 'amp': 0.10000000149011612, 'pan': 0.0, 'gate': 1.0}>]]
[73]:
sc.server.free_all()
sc.server.dump_tree()
NODE TREE Group 0
469762049 group
402653185 group
335544321 group
268435457 group
201326593 group
134217729 group
67108865 group
1 group
[74]:
sc.exit()
Quitting SCServer... [scsynth | reached EOF ]
Done.
Exiting sclang... [sclang | reached EOF ]
Done.
[ ]: