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 forcmd(.., verbose=False)
)
[4]:
sc.lang.cmds('"Hello User".postln') # check jupyter console for output
cmdg()
send command and get the output (alias forcmd(.., 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}")
-> fioositnanic
returned an <class 'str'> of value fioositnanic
[23]:
%scs ~retval = "sonification".scramble
%scg ~retval ++ "!"
-> oiiatnoisncf!
[23]:
'oiiatnoisncf!'
You can combine your code in a single row.
[24]:
scramble = %scg ~retval = "sonification".scramble; ~retval ++ "!";
scramble
-> initifcsoano!
[24]:
'initifcsoano!'
[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.69 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.