- [Topic]
- SuperCollider
Common Music supports the writing and rendering of non-realtime SuperCollider files. Support for running in real-time is currently in progress.
SuperCollider synths
SuperCollider synth objects are defined similarly to clm or csound instruments.
To begin, let's look at a fairly simple synthdef in SuperCollider.
( SynthDef("simple", {arg dur=1.0,freq=440.0,amp=0.2,pan=0.0; var osc; osc = EnvGen.kr(Env.triangle(dur,amp), doneAction: 2) * SinOsc.ar(freq); Out.ar(0,Pan2.ar(osc,pan)); }).writeDefFile.load(s); )
Defining a CM scsynth object to generate data for
this instrument is straightforward:
(defobject simple (scsynth) ((freq :initform 440) (dur :initform 1) (amp :initform .2) (pan :initform 0)) (:parameters freq dur amp pan))
The object is a subclass of the abstract class scsynth which contains additional slots that will be introduced below.
All arguments to the synthdef are used in the object definition of simple. The name of the slots are always treated as lowercase strings when written to an .osc file. This is important because if you have defined arguments to your synthdef which are mixed case or uppercase (e.g. "Dur" "DUR") things will not work properly.
In addition to user defined slots, all scsynth objects inherit the following slot initargs:
:nodenumber- The node created on the sc server. This defaults to -1, which allows the server to generate its own id for the instrument. This does mean in most cases that you will not be able to send commands to this specific instance of the synth since you will not know what the node id is. In cases where this is needed this value should be set.
:add-actionnumber- How to add the new node according to following from SuperCollider Server
Command Reference (Default is 1):
- 0 - add the new node to the the head of the group specified by the add target ID.
- 1 - add the new node to the the tail of the group specified by the add target ID
- 2 - add the new node just before the node specified by the add target ID.
- 3 - add the new node just after the node specified by the add target ID.
- 4 - the new node replaces the node specified by the add target ID.
:targetnumber- The group or node referenced by the :add-action slot.
:timenumber- The time to begin the synth.
Envelopes in SuperCollider
To send an envelope to a scsynth, one must typically use node-setn. Instead it is also possible to
use an sc-env when creating a new instance of s scsynth. Here is a simple example in SC:
( SynthDef("simple-env", {arg dur=1.0,freq=440.0,amp=0.2,pan=0.0; var osc,ampenv; ampenv = Env.newClear(8); ampf = Control.names([\ampenv]).kr( ampenv.asArray ); osc = EnvGen.kr(ampf, levelScale: amp, doneAction: 2) * SinOsc.ar(freq); Out.ar(0,Pan2.ar(osc,pan)); }).writeDefFile.load(s); )
This is the corresponding defobject in cm.
(defobject simple-env (scsynth)
((freq :initform 440)
(dur :initform 1)
(amp :initform .2)
(ampenv :initform #f)
(pan :initform 0))
(:parameters freq dur amp ampenv pan))
Now a sc-env can be used in the creation of a new instance of simple-env.
(new simple-env :time (now) :freq (between 300 700) :dur (between 10 20) :node (+ i 1000) :amp .1 :ampenv (new sc-env :envelope '(0 0.0 10 1.0 80 .5 100 0.0) :duration 10) :pan 1.0)
:envelopelist- Envelope used. Required.
:durationnumber- Envelope duration. Required.
:curve{keyword | list | number}- Curve segments. Defaults to :linear
- Valid curve descriptors are
:linear :lin :exp :exponential :step :sine :welch. - Curve descriptors may also be numbers which indicate a curvature value.
:scalenumber- Scale values.
:offsetnumber- Offset values.
:release-nodenumber- Release segment of envelope.
:loop-nodenumber- Loop segment of envelope.
(new sc-env :envelope'(0 0.0 10 1.0 80 .5 100 0.0) :duration 10) (new sc-env :envelope'(0 0.0 10 1.0 80 .5 100 0.0):duration 10 :curve :exp) (new sc-env :envelope'(0 0.0 10 1.0 80 .5 100 0.0):duration 10 :curve '(:exp -1.4 :welch))
Buffers in SuperCollider
Instead of using different commands for creating buffers, sc-buffer attempts to encapsulate some of the most commonly
used buffer creation/filling methods into one cm event.
:bufnumnumber- Buffer number.
:with-filestring- Path to a file to load into the buffer
:with-values{function | list | number}- Fills buffer with
:with-values. If a number, fills buffer with that value. If a list, will set buffer to those values. If a function will call this function for as many frames as are in the buffer minus:starting-at.
:with-genlist- Uses a buffer-gen to fill the buffer.
:starting-atnumber- Offset into buffer. Defaults to 0
:framesnumber- Number of frames in buffer. If used with
:with-filedefaults to reading in the entire file
:channelsnumber- Number of channels in buffer.
:timenumber- When to create buffer.
(new sc-buffer :with-gen'(:sine1 (1.0 .2 .1 .01)) :frames 512 :time (now)) (new sc-buffer :with-file "/Applications/SuperCollider3/sounds/a11wlk01.wav":time (now)) (new sc-buffer :with-values .2 :time (now)) (new sc-buffer :with-values (lambda () (ran :from -1.0 :below 1.0)) :time (now))
SuperCollider Command Events
load-synthdefload-synthdef-dirnode-freenode-runnode-setnode-setnnode-fillnode-mapnode-mapnnode-beforenode-afternode-querysynth-getsynth-getngroup-newgroup-headgroup-tailgroup-free-allgroup-deep-freeugen-commandbuffer-allocbuffer-alloc-readbuffer-readbuffer-writebuffer-freebuffer-zerobuffer-setbuffer-setnbuffer-fillbuffer-closebuffer-getbuffer-getnbuffer-gencontrol-setcontrol-setncontrol-fillcontrol-getcontrol-getn
All SuperCollider command events contain the :time slot inherited
from event.
For a description of the following commands, refer to Help/Server-Command-Reference.rtf in the SuperCollider distribution
Most of these events work with in both realtime and non-realtime mode unless otherwise noted.
:pathstring- Synthdef file to load.
:pathstring- Synthdef directory to load.
:node{number | list}- Node or list of nodes to free.
:node{number | list}- Node(s) to set.
:flag{boolean | list}- True or false referring to node(s).
(new node-run :node 1000 :flag #t :time (now)) (new node-run :node '(1000 1001 1003) :flag '(#t #f #t) :time (now))
:nodenumber- Node to set.
:controls-valueslist- List of keyword/number pairs indicating what control to set and what value to set it to.
:nodenumber- Node to set.
:controls-valueslist- List of keyword/list pairs indicating what control to start with and values indicating the setting of contiguous controls.
:nodenumber- Node to set.
:control{keyword | list}- Control or controls to set.
:num-controlsnumber- Number of values to fill.
:valuenumber- Value to set control(s) to.
(new node-fill :node 1000 :control :freq :num-controls 2 :value 100.1 :time (now)) (new node-fill :node 1000 :control '(:freq01 :freq20) :num-controls '(2 4) :value '(100.1 20.22) :time (now))
:nodenumber- Node to set.
:controls-buseslist- List of keyword/number pairs indicating what control to map to what bus..
:nodenumber- Node to set.
:control{keyword | list}- Keyword or list of keywords indicating the control names.
:bus{number | list}- Control buses index or indices.
:bus{number | list}- Number of controls to map.
(new node-mapn :node 1000 :control :freq :bus 100 :num-controls 2 :time (now)) (new node-mapn :node 1000 :control '(:freq1 :freq100) :bus '(100 200) :num-controls '(12 24) :time (now))
:nodenumber- Node to set.
:beforenumber- the node to place :node before.
(new node-before :node 1001 :before 1000 :time (now)) (new node-before :node '(1001 200) :before '(1000 500) :time (now))
:nodenumber- Node to set.
:afternumber- The node to place
:nodeafter.
(new node-after :node 1000 :after 1001 :time (now)) (new node-after :node '(1001 200) :before '(1000 500) :time (now))
- real-time only
:node{number | list}- Node to set.
- real-time only
:nodenumber- Synth to get.
:controls{number | string | list}- Control index or name or list of controls
(new synth-get :node 1000 :controls "freq":time (now)) (new synth-get :node '(1000 1001) :controls '(("freq" "amp") ("freq" "duration")) :time (now))
- real-time only
:nodenumber- Synth to set.
:controls{number | string | list}- Control index or name or list of controls
:num-controlsnumber- number of controls to get
:idnumber or list- Id of new group.
:add-action{number | list}- How to add the new group according to following from SuperCollider Server Command Reference:
- 0 - add the new group to the the head of the group specified by the add target ID.
- 1 - add the new group to the the tail of the group specified by the add target ID.
- 2 - add the new group just before the node specified by the add target ID.
- 3 - add the new group just after the node specified by the add target ID.
- 4 - the new node replaces the node specified by the add target ID. The target node is freed.
:target{number | list}- The group or node referenced by the
:add-actionslot.
; adds a new group with id 2 just before node 1000 (new group-new :id 2 :add-action 2 :target 1000) (new group-new :id '(2 3) :add-action '(2 1) :target '(1000 900))
:group{number | list}- The group to add
:nodeto the head of. :node{number | list}- The node to add to the head of :group.
(new group-head :group 2 :node 2000 :time (now)) (new group-head :group '(2 10) :node '(2000 3000) :time (now))
:group{number | list}- The group to add
:nodeto the tail of. :node{number | list}- The node to add to the tail of
:group.
(new group-tail :group 2 :node 2000 :time (now)) (new group-tail :group '(2 10) :node '(2000 3000) :time (now))
:group{number | list}- Frees all nodes in the specified group.
:group{number | list}- Frees all nodes in the specified group(s) and groups below it.
:nodenumber- Node ugen is in.
:ugen-indexnumber- Index of unit generator.
:command-namestring- Command for unit generator.
:argslist- Any additional arguments.
:bufnumnumber- Buffer number.
:framesnumber- Number of frames to allocate.
:channelsnumber- Number of channels. Default is 1.
:bufnumnumber- Buffer number.
:filestring- Path of sound file.
:start-framenumber- The starting frame to read from.
:framesnumber- Number of frames to read from file. If this is 0 (the default) the entire file will be read.
(new buffer-alloc-read :bufnum 10 :file "/Applications/SuperCollider3/sounds/a11wlk01.wav" :time (now))
:bufnumnumber- Buffer number.
:filestring- Path of sound file.
:start-framenumber- The starting frame to write from. Defaults to 0.
:framesnumber- Number of frames to read from file. If this is -1 (the default) the entire file will be read.
:buffer-start-framenumber- Frame in buffer to start reading file into. Defaults to 0.
:leave-open?{true | false}- Leave file open. Only should be true when using DiskIn.
:bufnumnumber- Buffer number.
:filestring- Path of sound file.
:header{:aiff|:next|:wav|:ircam|:raw}- Specifies header format of the file to write. Default is :aiff.
:format{:int8|:int16|:int24|:int32|:float|:double|:mulaw|:alaw}- Specifies the sample format of the file to write. Default is :int16.
:framesnumber- Number of frames to write. Default is -1 which writes entire buffer to file.
:start-framenumber- The starting frame to write from.
:leave-open?boolean- Leave file open. Only should be true when using DiskOut.
(new buffer-write :bufnum 10 :file "/path/to/file.aiff" :header :aiff :format :float)
:bufnumnumber- Buffer number.
:bufnumnumber- Buffer number.
:bufnumnumber- Buffer number.
:samples-valueslist- Pairs of sample indices and values to set.
:bufnumnumber- Buffer number.
:samples-valueslist- Pairs of starting sample index and list of values.
:bufnumnumber- Buffer number.
:start-sample{number | list}- Starting sample position to fill
:num-samplesnumber or list- Number of samples to fill.
:value{number | list}- Value to use as fill.
(new buffer-fill :bufnum 10 :start-sample 0 :num-samples 10 :value .8 :time (now)) (new buffer-fill :bufnum 10 :start-sample '(0 256) :num-samples '(10 20) :value '(.8 .2) :time (now))
:bufnumnumber- Buffer number.
:bufnumnumber- Buffer number.
:command{:sine1|:sine2|:sine3|:cheby}- Command to fill the buffer.
:flagskeyword or list. possible values :normalize :wavetable :clear- Flags for command.
:argslist- Additional arguments to command.
- real-time only
:bufnumnumber- Buffer to get.
:samples{number | list}- Sample index or indices
(new buffer-get :buffer 10 :samples 1 :time (now)) (new buffer-get :buffer 10 :samples '(1 3 5 7) :time (now))
- real-time only
:bufnumnumber- Buffer to get.
:samples{number | list}- Sample index or indices
:num-samplesnumber- Number of sequential samples to get
(new buffer-getn :bufnum 10 :samples 0 :num-samples 36 :time (now)) (new buffer-getn :bufnum 10 :samples '(0 128) :num-samples '(36 48) :time (now))
:bus{number | list}- Bus number.
:value{number | list}- Control value.
(new control-set :bus 2 :value 2.0 :time (now)) (new control-set :bus '(2 3 4) :value '(.4 .9 1.2) :time (now))
:bus{number | list}- Bus number.
:value{number | list}- Control value.
(new control-setn :bus 2 :value '(2.0 3.0 .2) :time (now)) (new control-setn :bus '(2 9) :value '((.1 .2 .3) (1.7 1.9 1.01)) :time (now))
:busnumber- The control bus to start at.
:num-busesnumber- Number of control buses to fill.
:valuenumber- Value to set control buses to.
(new control-fill :num-buses 2)
- real-time only
:bus{number | list}- Control bus or buses to get.
- real-time only
:bus{number | list}- Control bus or buses to get.
:num-buses{number | list}- Number of sequential buses to get
(new control-getn :bus 10 :num-buses 36 :time (now)) (new control-getn :bus '(1 20) :num-buses '(3 8) :time (now))
Examples:
Example 1. Using node-set to produce continuous changes in panning.
;;; define a scsynth object (defobject simple (scsynth) ((freq :initform 440) (dur :initform 1) (amp :initform .2) (pan :initform 0)) (:parameters freq dur amp pan time)) ;;; define a process to generate simples. (define (sc-simple-3 num) (process repeat num for i from 0 output (new simple :time (now) :freq (between 300 700) :dur (between 10 20) :node (+ i 1000) :amp .1 :pan 1.0) sprout (process repeat 100 for j from 0 with node = (+ i 1000) with pan-env = '(0 1.0 50 .4 100 -1.0) output (new node-set :node node :controls-values (list :pan (interpl j pan-env)) :time (now)) wait .1) wait (between 2 3))) (events (sc-simple-3 10) "sc-simple-3.osc" :pad 20) ⇒ "sc-simple-3.osc" (define *sc* (sc-open)) (rts (sc-simple-3 10) *sc* )
Auxiliary Functions
CM provides some a few functions and variables to facilitate working with SuperCollider.- [Function]
(sc-open[args])
Opens a sc-stream called "sc.udp" according to the keyword initialization args passed to the stream.
- [Function]
(sc-open?)
Tests to see if "sc.udp" has already been opened.
- [Function]
(sc-close[args])
Closes the sc-stream called "sc.udp" if it is open, otherwise has no effect.
- [Function]
(sc-quit[sc-stream])
Sends a quit message to scsynth listening on "sc.udp" if opened or optional sc-stream.
- [Function]
(sc-dumposcboolean [sc-stream])
Sends a dumpOSC message to scsynth listening on "sc.udp" if opened or optional sc-stream. If boolean is true turns on osc dumping. If false turns it off.
- [Function]
(sc-clearsched[sc-stream])
Sends a clearSched message to scsynth listening on "sc.udp" if opened or optional sc-stream.
- [Function]
(sc-notifyboolean [sc-stream])
Sends a notify message to scsynth listening on "sc.udp" if opened or
optional sc-stream. If boolean is true turns on status. If false turns it off.
- [Function]
(sc-flush[sc-stream])
Sends a clearSched and a group-free-all message to scsynth listening on "sc.udp" if opened or optional sc-stream.
The file sc.cm contains numerous examples of Supercollider score generation. The file sc-rt.cm contain examples of generating Supercollider events in realtime.
SuperCollider Reply Objects
done-replyfail-replystatus-replysynced-replysynth-get-replysynth-getn-replybuffer-get-replybuffer-getn-replybuffer-query-replycontrol-get-replycontrol-getn-replynode-go-replynode-end-replynode-off-replynode-on-replynode-move-replynode-info-replytrigger-reply
- slots:
- cmd-name
- accessors:
- done-cmd-name
- slots:
- cmd-name
- error
- accessors:
- fail-cmd-name
- fail-error
- slots:
- num-ugens
- num-synths
- num-groups
- num-loaded-synths
- avg-cpu
- peak-cpu
- sample-rate
- actual-sample-rate
- accessors:
- status-num-ugens
- status-num-synths
- status-num-groups
- status-num-loaded-synths
- status-avg-cpu
- status-peak-cpu
- status-sample-rate
- status-actual-sample-rate
- slots:
- id
- accessors:
- synced-id
- slots:
- node
- controls-values
- accessors:
- synth-get-node
- synth-get-controls-values
- slots:
- node
- controls-values
- accessors:
- synth-getn-node
- synth-getn-controls-values
- slots:
- bufnum
- samples-values
- accessors:
- buffer-get-bufnum
- buffer-get-samples-values
- slots:
- bufnum
- samples-values
- accessors:
- buffer-getn-bufnum
- buffer-getn-samples-values
- slots:
- bufnum
- num-frames
- num-chans
- sample-rate
- accessors:
- buffer-query-bufnum
- buffer-query-num-frames
- buffer-query-num-chans
- buffer-query-sample-rate
- slots:
- bus
- value
- accessors:
- control-get-bus
- control-get-value
- slots:
- bus
- value
- accessors:
- control-getn-bus
- control-getn-value
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-go-node
- node-go-parent-group
- node-go-previous-node
- node-go-next-node
- node-go-type
- node-go-head-node
- node-go-tail-node
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-end-node
- node-end-parent-group
- node-end-previous-node
- node-end-next-node
- node-end-type
- node-end-head-node
- node-end-tail-node
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-off-node
- node-off-parent-group
- node-off-previous-node
- node-off-next-node
- node-off-type
- node-off-head-node
- node-off-tail-node
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-on-node
- node-on-parent-group
- node-on-previous-node
- node-on-next-node
- node-on-type
- node-on-head-node
- node-on-tail-node
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-move-node
- node-move-parent-group
- node-move-previous-node
- node-move-next-node
- node-move-type
- node-move-head-node
- node-move-tail-node
- slots:
- node
- parent-group
- previous-node
- next-node
- type
- head-node
- tail-node
- accessors:
- node-info-node
- node-info-parent-group
- node-info-previous-node
- node-info-next-node
- node-info-type
- node-info-head-node
- node-info-tail-node
- slots:
- node
- id
- value
- accessors:
- trigger-node
- trigger-id
- trigger-value