Server

[ ]:
import sc3nb as scn

The SCServer class is the central interface for

  • controlling the SuperCollider audio server process

  • managing SuperCollider Objects

  • using OSC for outgoing and incoming packets

To achieve all this the SCServer is

  • registered as a client to the SuperCollider audio server process (scsynth) and exchanging SuperCollider Commands with the server process

  • and also running an OSC server in python which can communicate via OSC with scsynth and sclang.

For information about how to communicate using OSC see the OSC communication notebook. This notebook focuses on using the SCServer class for interacting with scsynth

Starting the Server

The most convienent way is to use the default sc3nb SCServer instance

[ ]:
sc = scn.startup()
[ ]:
sc.server

However you can also use the SCServer class directly

The server connection can be created

  • locally using boot, which will start a scsynth process and connect to it or

  • remote using remote for connecting to an already running scsynth process

[ ]:
serv = scn.SCServer()
serv
[ ]:
serv.boot()

Notice how the SCServer always tries to boot using the default SuperCollider audio server port 57110. But this port is already used by sc.server and thus the SCServer tries to connect to the already running instance using SCserver.remote. This enables a user to share the same scsynth instance with other users and/or use it from other notebooks. If the port to be used is explicitly specified the SCServer instance will fail instead of connecting.

The SCServer will register to the scsynth process using SCServer.notify()

Let’s look how many clients are allowed and what the client_ids and the corresponding default_groups of the SCServer instances are.

[ ]:
print(f"The scsynth process of this SCServer instance allows {sc.server.max_logins} clients to login.")
[ ]:
print(f"sc.server has client id {sc.server.client_id} and the default Group {sc.server.default_group}")
[ ]:
print(f"serv has client id {serv.client_id} and the default Group {serv.default_group}")

However also note that the instances use different ports meaning they are able to independendly send and receive OSC packets

[ ]:
sc.server.connection_info()

and also note that serv is not connected to sclang but has the same connection info for scsynth.

[ ]:
serv.connection_info()

A Synth running on the SuperCollider audio server will be visible to all connected clients

[ ]:
serv_synth = scn.Synth("s2", {"amp": 0.05, "pan": -1, "freq": 100}, server=serv)
[ ]:
default_synth = scn.Synth("s2", {"amp": 0.05, "pan": 1, "freq": 440})  # no need to specify sc.server as server argument

This also includes sclang, which is another client of the scsynth process

[ ]:
%sc ~sclang_synth = Synth("s2", [\amp, 0.1])
[ ]:
sc.server.dump_tree()

This also means freeing all Synths at once can be done with each client

[ ]:
sc.server.free_all()
# serv.free_all()
# %sc s.freeAll
[ ]:
sc.server.dump_tree()

and quitting one server also quits the others.

[ ]:
serv.quit()
[ ]:
sc.server

Let’s reboot the default server

[ ]:
sc.server.reboot()

More information about multi client setups can be found in the SuperCollider documentation.

Configuring Server options

Startup options of the SCServer instance can be set via ServerOptions, which can be passed as argument when starting the SCServer

The default ServerOptions in sc3nb are:

[ ]:
scn.ServerOptions()

Getting Information

The SCServer instance provides various kinds of information

  • What nodes are currently running

[ ]:
sc.server.dump_tree()
[ ]:
sc.server.query_tree()
  • The current status of the server, acquired via the /status OSC command

[ ]:
sc.server.status()

which can also be accessed directly via properties

[ ]:
sc.server.nominal_sr
[ ]:
sc.server.num_synthdefs
  • the version of the SC3 server process, acquired via the /version OSC command

[ ]:
sc.server.version()
  • the address of the SuperCollider audio server

[ ]:
sc.server.addr
  • The connection info

[ ]:
sc.server.connection_info()
  • other runtime properties of the Server

[ ]:
sc.server.has_booted
[ ]:
sc.server.is_running
[ ]:
sc.server.client_id
[ ]:
sc.server.max_logins
[ ]:
sc.server.default_group
[ ]:
sc.server.output_bus
[ ]:
sc.server.input_bus

Controlling Volume

[ ]:
syn = scn.Synth("s2")
[ ]:
sc.server.volume
[ ]:
scn.dbamp(sc.server.volume)
[ ]:
sc.server.muted
[ ]:
sc.server.muted = True
[ ]:
sc.server.volume = -10.0
[ ]:
scn.dbamp(sc.server.volume)
[ ]:
sc.server.muted = False
[ ]:
sc.server.volume = 0.0
[ ]:
scn.dbamp(sc.server.volume)
[ ]:
syn.free()
syn.wait(timeout=1)

Server dumps

The Server process can dump information about

  • incoming OSC packages. See console for output

[ ]:
sc.server.dump_osc() # specify level=0 to deactivate
  • currently running Nodes

[ ]:
sc.server.dump_tree()  # Notice how the OSC packet is now included in the output
[ ]:
sc.server.blip() # see dumped bundle for test sound on console

Make a test sound

The following methods produces the SCServer startup sound. The test sound should ease any anxiety whether the server is properly started/running

[ ]:
sc.server.blip()

Managing Nodes

  • freeing all running nodes and reinitialize the server

[ ]:
sc.server.free_all()
[ ]:
sc.server.free_all(root=False)  # only frees the default group of this client
  • send the /clearSched OSC command. This is automatically done when using free_all

[ ]:
sc.server.clear_schedule()
  • Execute init hooks. This is also automatically done when using free_all, init or connect_sclang

[ ]:
sc.server.execute_init_hooks()
  • Adding init hooks.

[ ]:
sc.server.send_default_groups
  • Syncing the SuperCollider audio server by sending a /sync OSC command and waiting for the reply.

[ ]:
sc.server.sync()

Allocating IDs

The SCServer instance manages the IDs for Nodes, Buffers and Buses for the SuperCollider Objects via the following methods. These can also be used for getting suitable IDs when manually creating OSC packages.

  • Get the IDs via the allocator.

[ ]:
ids = sc.server.buffer_ids.allocate(num=2)
ids
  • Free the IDs after usage via the allocator.

[ ]:
sc.server.buffer_ids.free(ids)

There are allocators for

  • Nodes - sc.server.node_ids

  • Buffer - sc.server.buffer_ids

  • Buses (Audio and Control) - sc.server.audio_bus_ids, sc.server.control_bus_ids

[ ]:
sc.server.reboot()
[ ]:
# example to see how consecutive buffer alloc works:
ids = sc.server.buffer_ids.allocate(num=5)
print("5 buffers:", ids)
sc.server.buffer_ids.free(ids[0:2])
print("freed buffers ", ids[0:2])
ids4 = sc.server.buffer_ids.allocate(num=4)
print("allocated 4 buffers:", ids4, "-> new numbers to be consecutive")
sc.server.sync()
ids2 = sc.server.buffer_ids.allocate(num=2)
print("allocated 2 buffers:", ids2, "-> using the two freed before")

Handling SynthDefs

The server offers the following methods for handling SynthDefs. These are shortcuts for the respective SynthDef methods.

sc.server.send_synthdef
sc.server.load_synthdef
sc.server.load_synthdefs

Refer to the SynthDef guide for more information about SynthDefs.

[ ]:
sc.exit()