Compact coding tools for SuperCollider, with Emacs Org-mode intergration
(IZ Tue, 25 Feb 2014 16:20:26ff)
The purpose of this library is to provide simple and short solutions for some relatively complex or arduous recurring tasks in SuperCollider. It is hoped that it will make working with SuperCollider easier, at any level. The following sections introduce features of the library through examples.
Examples are added here after being tested. More details about what tiny-sc sets out to do are found in files Roadmap.org
and BasicIdeas.org
.
Associating symbols with synths, starting, stopping with fadeout, replacing the stored synth in the same symbol:
Try the following, one line at a time:
// Start a synth and store it in a name { WhiteNoise.ar } => \sound; // Stop The sound with the default fade-out duration \sound.fadeOut; // Restart the stored sound - remake the synth. \sound.start; // Replace the synth at \sound with another one { SinOsc.ar(440) } => \sound; // Replace again, fading-out the previous sound in 5 seconds { PinkNoise.ar } =>.5 \sound; // Floating point durations must be given through a message { GrayNoise.ar(Decay.kr(Dust.kr(5))) } => \sound.fadeOut(0.5); // Output channel can be set 1 => ~out; // So can the amplitude 0.01 => ~amp; // Alternative syntax: \sound.set(\amp, 0.01) // Fadeout with 8000 custom duration \sound.fadeOut(5);
The environment variable ~fadeTime stores the global default duration for fade-in and fade-out of synths. It is stored in the parent environment of the current environment. The =!> operator is used to set environment variables of the parent environment. Examples:
// Set global fade time to 3 seconds 3 =!> \fadeTime; // Fade in a synth { WhiteNoise.ar } => \sound; // Cross-fade another synth in its place { SinOsc.ar(LFNoise0.kr(5).range(400, 4000)) } => \sound; // And a third synth { LPF.ar(GrayNoise.ar, LFNoise0.kr(5).range(400, 4000)) } => \sound; // Set global fade time to 0.01 seconds 0.01 =!> \fadeTime; // Notice difference in cross-fading duration { LFDNoise3.ar(LFNoise0.kr(15).range(400, 4000)) } => \sound; // One more time { SinOsc.ar(LFNoise0.kr(15).range(400, 4000)) } => \sound; // Fadeout also uses the global default \sound.fadeOut;
When a synth template gets chucked to a synthtree with =>, the parameters of that synth get pushed to the current environment. They can then be accessed and set directly as environment variables.
{ LPF.ar(LFSaw.ar(\freq.kr(440)), \filterFreq.kr(1000)) } => \sound; // Parameters of last chucked SynthTree are in the environment. // Change the filter frequency: 400 => ~filterFreq; // Change the generator frequency: 600 => ~freq; // Play a routine into frequency, then fade out { 50 do: { 50.rrand(80).midicps => ~freq; 0.05.wait }; \sound.fadeOut(3); }.fork;
{ SinOsc.ar(\freq.kr(400)) } => \sound; // Play a pattern into ~freq: { 50.rrand(80).midicps } -> 0.1 => ~freq; // Alternative formulation: { 80.rrand(90).midicps } pp: 0.2 => ~freq; //: Play another pattern into ~freq: { 250 rrand: 350 } -> 0.05 => ~freq; //: And another: { [60, 62, 66, 67].choose.midicps } -> Prand([0.1, 0.2, 0.4], inf) => ~freq; // Pattern keeps playing when new synth is chucked into tree: { LPF.ar(LFPulse.ar(\freq.kr(440)), 1000) } => \sound; // Open knobs interface to watch how freq changes ~st.knobs; // Use nil to play with global duration stored in ~dur: { [65, 69, 70, 73].choose.midicps } -> nil => ~freq; // Change global duration: 0.1 =!> \dur; // Use a pattern for global duration: Pbrown(0.01, 0.26, 0.05, inf).asStream =!> \dur;
Add some more synths to the tree:
{ SinOsc.ar(440) } => \la; { SinOsc.ar(550) } => \doDiese; { SinOsc.ar(660) } => \mi;
Stop all synths by typing Command-. (on SC IDE), or Control-c Control-s (on Emacs), or by evaluating this:
thisProcess.stop;
Then run this to restart the synths:
SynthTree.initTree;
To stop all synths of the SynthTree from being restarted, evaluate this:
SynthTree.stopAll;
After this, no synths will be restarted with SynthTree.initTree
.
Example 1: Simple patching of one source to one effect:
// Start an effects synth with a low-pass filter { LPF.ar(Inp.ar, \freq.kr(2000)) } => \lpf; // Start a WhiteNoise synth { WhiteNoise.ar } => \source; // Send the noise synth to the filter \lpf =< \source; // Change the frequency of the low pass filter \lpf.set(\freq, 5000);
Example 2: Several synths sending to one effect.
{ LPF.ar(Inp.ar, LFNoise0.kr(40 ! 2).range(500, 4000)) } => \lpf; \lpf =< ({ PinkNoise.ar } ==> \source); \lpf =< ({ LFPulse.ar(LFNoise0.kr(30).range(3000, 4000)) } ==> \source2);
Example 3: Changing the synths of the source and of the effect:
{ Inp.ar * Decay.kr({ Dust.kr(\trigRate.kr(1)) } ! 2) } =>.5 \lpf; \lpf =< ({ LFTri.ar(LFNoise2.kr(12).range(400, 4000)) } ==> \source2);
Confirm that the tree can be restarted after Command-. also when it contains linked synths:
thisProcess.stop; // run this to stop all synths first // Then run this to restart all stopped synths: SynthTree.initTree;
There are 5 main view types:
- Fader View
- Vertical strip on the left, showing the currently registered SynthTree instances and their run status, with a slider for controlling the level of each instance.
- Knobs View
- Horizontal strip at the bottom, one for each SynthTree instances, with knob controls for setting all registered parameters of the SynthTree. This is opened from the Fader view by typing “k” on a selected SynthTree strip’s label.
- Synth Template View
- A window with 2 list views: The left list shows the tags (categories) of SynthTree templates (SynthDefs or Functions) and the right list shows the templates belonging to the selected category. At the bottom is a drag view showing the name of the selected template. Drag the selected template onto any label on the Fader view to play that template on the SynthTree belonging to that fader strip.
- Pattern Template View
- (Tentative / Under development!) Holds templates of patterns to play in SynthTrees. May be integrated in the same list as the Synth Template View.
- Process Registry View
- Experimental / Proof of concept: Shows a list of currently running synth and routine processes, in a manner similar to the process view of Mini Audicle in ChucK. Works together with Emacs/Org-Mode (see keyboard shortcuts). Can also work with SuperCollider IDE, but requires using different methods for playing Functions, Synths or Routines. This feature is superseded by the Fader View, but kept here as proof-of-concept.
SynthTemplate.gui;
Key | Action | |
---|---|---|
return | send template to currently selected SynthTree instance* | |
shift-return | send template to a new SynthTree instance | |
control-return | add template as input to currently selected SynthTree instance | |
control-. | thisProcess.quit (like in SuperCollider IDE) | |
control-/ | SynthTree.initTree (restart all SynthTrees stopped by control-. |
(*) Note : The currently SynthTree is selected in the Faders panel by clicking on the label displaying the SynthTree description (template name + synthtree name), or by chucking into a SynthTree in code (=>).
SynthTree.faders;
Key | Action | |
---|---|---|
On the whole window | ||
b | Open Buffer List for creating buffer-playback synth | |
, | Stop synths and routines (thisProcess.stop ) | |
. | Stop synths and remove from SynthTree.initTree | |
i | SynthTree.initTree. Restart non-removed synths | |
/ | SynthTree.initTree. Restart non-removed synths | |
0-9 | Set global fade time to 0.02, 1, 2, 3 … 9 seconds | |
On slots that contain a SynthTree: | ||
k | Open knobs window for controlling all parameters of synth | |
g | start synth | |
s | stop synth | |
space | Toggle play status of selected SynthTree. | |
, | Stop synths and routines (thisProcess.stop ) | |
. | Stop synths and remove from SynthTree.initTree |
{ SinOsc.ar(\freq.kr(440)) } => \viewtest; \viewtest.view(\freq).view(\amp);
// Start an "effect" synth with an input { LPF.ar(In.ar(\in.kr(0)), \freq.kr(4000)) } => \lpf; // Set fadeTime of effect: \lpf.fadeTime = 10; // Send a synth to the input of the effect synth \lpf =< ({ WhiteNoise.ar } ==> \source); // Set fadeTime of source; \source.fadeTime = 5; // change effect, with fadeTime stored previously { Inp.ar * Decay2.kr(Dust.kr(3)) } => \lpf; // change source, with fadeTime stored previously { SinOsc.ar(2000 rrand: 3000) } ==> \source; // change source again, With fadeTime stored previously { LFTri.ar(400 rrand: 800) } ==> \source;
Play a sample loaded from disk with PlayBuf (If no name is specified, the name of the receiver of .buf
is used to find a buffer of the same name. If no such buffer exists, then a Dialog window is opened for choosing a file to load into a buffer):
{ \buf.playBuf } => \chimes.buf.set(\amp, 1);
Play the same sample in a different synth, with different rate
{ \buf.playBuf(rate: 1.2) } => \different.buf(\chimes).set(\amp, 1);
Setting classvar autoload of BufferList
to true
will make SuperCollider load all .aiff
and .wav
files that are found under folder sounds
in the SuperCollider user support directory (Platform.userAppSupportDir
) whenever the default server boots.
Following opens a Buffer List view with all buffers loaded through selecting from a SynthTree as shown above, or put in the default “sounds” folder in User App Support Dir/SuperCollider
BufferList.showList;
Keyboard commands on the Buffer List list view:
Key | Action |
---|---|
return | play/stop selected buffer in a SynthTree named as the buffer |
shift-return | like return, but set loop to 1 (loop buffer) |
space, shift-space | like return, but always create new SynthTree to play in |
l | load a new buffer from file |
s | save list of loaded buffers to file |
o | load list of buffers from file |
Following keyboard bindings only apply to Emacs.
- C-c C-x C-/
- sclang-init-synth-tree
Following keyboard shortcuts allow one to choose a synthtree from the list of synthtrees currently loaded in SuperCollider, or operate on the last chosen synthtree in emacs:
- H-c c
- org-sc-select-synthtree-then-chuck
- H-c H-c
- org-sc-chuck-into-last-synthtree
- H-c k
- org-sc-select-synthtree-then-knobs
- H-c space
- org-sc-toggle-synthtree
- H-c H-space
- org-sc-toggle-last-synthtree
- H-c g
- org-sc-start-synthtree
- H-c s
- org-sc-stop-synthtree
- H-c H-s
- org-sc-stop-last-synthtree
The chuck commands (H-c c
, H-c H-c
) enclose the snippet or section into a function before chucking. Try for example H-c c
placing the cursor in the following line of code in sclang-mode:
//: SinOsc.ar(\freq.kr(800) * LFNoise0.kr(12).range(0.8, 1.2)); //:
Stop the example above by typing H-c H-space
.
Following keyboard shortcuts select a buffer from the list of buffers currently loaded in SuperCollider, or operate on the buffer list:
- H-b g
- org-sc-play-buffer
- H-b l
- org-sc-load-buffer
- H-b f
- org-sc-free-buffer
- H-b L
- org-sc-show-buffer-list
- H-b o
- org-sc-open-buffer-list
- H-b s
- org-sc-save-buffer-list
- C-c C-s
- sclang-main-stop
- H-C-o
- org-sc-toggle-mode
Note: The process registry window and the org-sc-eval-in-routine technique is now superseded by SynthTree and its guis. SynthTree Fader gui is a more convenient way to control running synths. The process registry is nevertheless kept here as mere “proof of concept”, imitating the MiniAudicle process list window of ChucK.
- H-C-r
- sclang-process-registry-gui: Open registry gui.
- C-M-x
- org-sc-eval
- H-C-x
- org-sc-eval-in-routine. Wraps code in routine and registers it in ProcessRegistry.
- C-M-z
- org-sc-stop-section-processes. Stop all processes started from the current section. Uses automatically generated section ID to identify the current section.
- H-C-z
- org-sc-stop-section-processes
- C-c C-M-.
- org-sc-stop-section-processes
- H-C-n
- org-sc-next-section
- C-M-n
- org-sc-eval-next. Go to next section and evaluate as in org-sc-eval.
- H-C-p
- org-sc-previous-section
- C-M-p
- org-sc-eval-previous
- C-c C-,
- sclang-eval-line
- C-c C-9
- sclang-eval-dwim
- C-c C-x l
- org-sc-toggle-autoload
- C-c C-x C-l
- org-sc-load-marked
Before evaluating the following sections, type H-C-r
to open the Process Registry window. This displays the currently running processes. Selecting a process and typing delete will stop or free that process.
a = { SinOsc.ar(\freq.kr(440), 0, 0.1) }.pla; 0.1.wait; a.set(\freq, 550); 0.1.wait; a.set(\freq, 660); a release: 3;
// Type C-M-x with the cursor in the current sectiona = { SinOsc.ar(\freq.kr(440), 0, 0.1) }.pla; 7 do: { 0.1.wait; a.set(\freq, 550); 0.1.wait; a.set(\freq, 660); }; a release: 3;
// Type C-M-x with the cursor in the current sectiona = { SinOsc.ar(\freq.kr(440), 0, 0.1) }.pla; 50 do: { 0.1.wait; a.set(\freq, (440 * (4..12).choose / 4).postln); }; a release: 3; 3.wait; “DONE!”.postln;
// Watch the registry window tracking 1 to 30 rapidly changing synths // Kill the routine by selecting it in the registry window and // hitting the backspace key. // Then kill any remaining synths one by one with the backspace keyvar synths, fwalk, swalk, synth; synths = List(); fwalk = (Pbrown(30, 90, 0.75, inf) + Pfunc({ 0.01.exprand(1.5)})).asStream; swalk = Pbrown(0, 30, 1, inf).asStream; loop { if (swalk.next > synths.size) { synths add: Syn(“adsrsine”, [\freq, fwalk.next.midicps]); }{ synth = synths.choose; synth.release(1.0.exprand(5.0)); synths remove: synth; }; 0.05.wait; };
var synths, fwalk, swalk, synth; synths = List(); fwalk = (Pbrown(30, 90, 0.75, inf) + Pfunc({ 0.01.exprand(1.5)})).asStream; swalk = Pbrown(0, 30, 1, inf).asStream; loop { if (swalk.next > synths.size) { synths add: Syn(“adsrringz”, [\freq, fwalk.next.midicps, \decayTime, 3, &, 0.02]); }{ synth = synths.choose; synth.release(1.0.exprand(5.0)); synths remove: synth; }; 0.05.wait; };
- H-C-o
- org-sc-toggle-mode
- C-c .
- sclang-execute-current-snippet
- C-c C-,
- sclang-eval-line
- C-c C-.
- sclang-select-snippet
- C-M-x
- sclang-execute-current-snippet
- C-M-f
- sclang-goto-next-snippet
- C-M-b
- sclang-goto-previous-snippet
- C-M-n
- sclang-execute-next-snippet
- C-M-p
- sclang-execute-previous-snippet
- C-H-f
- sclang-goto-next-snippet
- C-H-b
- sclang-goto-previous-snippet
- C-H-n
- sclang-execute-next-snippet
- C-H-p
- sclang-execute-previous-snippet
- C-H-r
- sclang-process-registry-gui
- C-c l
- sclang-recompile
- M-C
- sclang-clear-post-buffer
Use =|> to set the source of a SynthTree without starting it. This is necessary in cases like the following, where the starting of the synth is done explicitly by trig in a routine:
//: { var synth; synth = { [SinOsc, LFPulse, LFTri, LFSaw].choose.ar(\freq.kr(400)) } =|> \test; 50 do: { synth.trig(\freq, 400 rrand: 1200); 0.1.wait; } }.fork //:
{ Inp.ar * Decay2.kr({ Dust.kr(1) } ! 2, 0.5, 2) } => \smooth; \smooth =< ({ GrayNoise.ar(3) } ==> \gray);
…