[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 AddAction

  • group - 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.
[ ]: