This page was generated from examples/sclang-examples.ipynb.

Usage of sclang in sc3nb

You can send commands and receive data directly from the SuperCollider Language

[1]:
import time
import numpy as np

import sc3nb as scn
[2]:
sc = scn.startup()
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.

sclang command execution

To send sc3 commands (i.e. program strings) to the language, either use the following functions

  • cmd() normal command sending.

[3]:
# sc.cmd(cmdstr, pyvars)
sc.lang.cmd('"Hello World".postln')  # output will be printed
Hello World
-> Hello World
  • cmds() silent version without (alias for cmd(.., verbose=False))

[4]:
sc.lang.cmds('"Hello User".postln')  # check jupyter console for output
  • cmdg() send command and get the output (alias for cmd(.., get_return=True)). More details below

[5]:
string = sc.lang.cmdg('"sc3nb".postln')
print(f'We received the string = "{string}"')
sc3nb
-> sc3nb
We received the string = "sc3nb"

or use the corresponding Magics in Jupyter

  • Jupyter line magics %sc, %scv, %scs, %scg, %scgv, %scgs

  • Jupyter cell magics %%sc, %%scv, %%scs, %%scg, %%scgv, %%scgs

which wrap the above functions: cmd{v,g,s} = %sc{v,g,s} and v=verbose, g=get, s=silent verbose is default, so %sc=%scv

Line magics can be placed within code just as the function calls as shown here:

[6]:
for p in range(1, 10):  # a bouncing ball
    %scs Synth.new(\s1, [\freq, 200]) // this is SC code so use // instead of #
    time.sleep(1/p)

Use raw python strings for multi-line sc3-programs:

[7]:
sc.lang.cmd(r"""
Routine({
    x = 5.collect{ |i|
        0.2.wait;
        Synth.new(\default, [\freq, 50+(50*i)]);
    };
    1.wait;
    x.do{|e|
        e.release;
        0.1.wait;};
}).play;
""")
-> a Routine

alternatively, you can use the cell magics

[8]:
%%sc
Routine({
    x = 5.collect{ |i|
        0.2.wait;
        Synth.new(\default, [\freq, 50+(50*i)]);
    };
    1.wait;
    x.do{|e|
        e.release;
        0.1.wait;};
}).play;
-> a Routine

Note that the code is executed in sclang and Python is returning directly after sending the command.

sclang command execution with python variable injection

Python variables can be injected into sc3 commands by using the ^ special:

The following examples demonstrates it by setting frequencies by using python variables

[9]:
for p in range(1, 50):  # a tone ladder
    freq = 50 + p*3
    dur = np.log(p)
    position = np.sign(p-25)
    %scs Synth.new(\s1, [\freq, ^freq, \dur, ^dur, \pan, ^position])
    time.sleep(0.05)

This is injection is done with

[10]:
help(scn.util.convert_to_sc)
Help on function convert_to_sc in module sc3nb.util:

convert_to_sc(obj: Any) -> str
    Converts python objects to SuperCollider code literals.

    This supports currently:

    * numpy.ndarray -> SC Array representation
    * complex type -> SC Complex
    * strings -> if starting with sc3: it will be used as SC code
                 if it starts with a \ (single escaped backward slash) it will be used as symbol
                 else it will be inserted as string

    For unsupported types the __repr__ will be used.

    Parameters
    ----------
    obj : Any
        object that should be converted to a SuperCollider code literal.

    Returns
    -------
    str
        SuperCollider Code literal

Here are some conversion examples

[11]:
python_list = [1,2,3,4]
%sc ^python_list.class
-> Array
[12]:
complex_py = 1+1j
%sc ^complex_py.class
-> Complex
[13]:
symbol = r"\\python"
%sc ^symbol.class
-> Symbol

When using the cmd, cmdg and cmds functions you can also provide a dictionary with variable names as keys and content as values (which can use other python vars or statements)

[14]:
sc.lang.cmdv("^name1 / ^name2", pyvars={'name1': 9,'name2': 9*2})
-> 0.5

Without providing pyvars, variables are searched in the users namespace.

[15]:
freq = 5
rate = 6
sc.lang.cmdv("(^freq + 1) * (^rate + 1)")
-> 42

alternatively via the magic this is done as:

[16]:
%scv (^freq + 1) * (^rate + 1)
-> 42

Getting sclang output in python

  • To get the output of an sclang snippet into a python variable, use the cmdg function.

  • The following example shows how to transfer a synth’s nodeID

[17]:
# start a Synth
sc.lang.cmd(r"""x = Synth.new(\default)""")
-> Synth('default' : 1067)
[18]:
# get the nodeId to python
nodeID = sc.lang.cmdg("x.nodeID")
print(nodeID)
-> 1067
1067
[19]:
# use the nodeID to free the Synth via a message to scsynth audio server directly
sc.server.msg("/n_free", nodeID)

sc.cmdg(), resp. %scg return integers, floats, strings and lists * %scg can be assigned to a python variable within code

[20]:
a = %scg 1234 + 23452
print(f"returned an {type(a)} of value {a}")
-> 24686
returned an <class 'int'> of value 24686
[21]:
a = %scg 1234.5.squared
print(f"returned an {type(a)} of value {a}")
-> 1523990.25
returned an <class 'float'> of value 1523990.25
[22]:
a = %scg "sonification".scramble
print(f"returned an {type(a)} of value {a}")
-> notsconiiifa
returned an <class 'str'> of value notsconiiifa
[23]:
%scs ~retval = "sonification".scramble
%scg ~retval ++ "!"
-> cnoaionstiif!
[23]:
'cnoaionstiif!'

You can combine your code in a single row.

[24]:
scramble = %scg ~retval = "sonification".scramble; ~retval ++ "!";
scramble
-> ticofnisnioa!
[24]:
'ticofnisnioa!'
[25]:
a = %scg (1,1.1..2)
-> [ 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9 ]

Note that floating-point numbers do only have limited precision

[26]:
print(f"list with length: {len(a)}")
a
list with length: 10
[26]:
[1.0,
 1.100000023841858,
 1.2000000476837158,
 1.2999999523162842,
 1.399999976158142,
 1.5,
 1.600000023841858,
 1.7000000476837158,
 1.7999999523162842,
 1.899999976158142]

However they should be close

[27]:
[round(num, 6) for num in a]
[27]:
[1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
[28]:
np.allclose(a, np.arange(1, 2, 0.1))
[28]:
True

Some more usage examples

You can use the SuperCollider GUI features.

[29]:
sc.lang.cmd(r"MouseX.help", timeout=10)
SCDoc: Indexing help-files...
SCDoc: Indexed 1367 documents in 0.67 seconds
-> MouseX
[30]:
%sc {SinOsc.ar(MouseX.kr(200,400))}.play  // move mouse horizontally, CMD-. to stop
-> Synth('temp__0' : 1068)
[31]:
%sc s.scope()
-> a Stethoscope
[32]:
sc.server.free_all()  # leaves the scope running
[33]:
%%sc
{
    x = Synth.new(\s2, [\freq, 100, \num, 1]);
    250.do{|i|
        x.set(\freq, sin(0.2*i.pow(1.5))*100 + 200);
        0.02.wait;
    };
    x.free;
}.fork
-> a Routine
[34]:
sc.exit()
Quitting SCServer... [scsynth | reached EOF ]
Done.
Exiting sclang... [sclang | reached EOF ]
Done.