- 1. Basic keyboard shortcuts
- 2. Function
- 3. SynthDef
- 4. GUI and sequencers
- 5. Sequencing using patterns
- 6. Demand rate UGens
- 7. How to make a recording
- Evaluate region:
Ctrl + RET
(Control and Return)- Evaluates a code chunk within brackets
( )
- Evaluates a code chunk within brackets
- Evaluate line:
Shift + RET
- Stop sound forcefully:
Ctrl + .
(Control and period) - Boot Server:
Ctrl + B
- Recompile Library:
Ctrl + Shift + L
- Open help file: Highlight keyword and press
Ctrl + D
- Open source code: Highlight keyword and press
Ctrl + I
On MacOS instead of Ctrl
use Cmd
.
A function is enclosed within curly brackets { }
and is evaluated when using the method value
.
See the example below from Function SC3 help file.
- Link to SC3 script below
- ./src/simple-func.scd
(
var a, b, c;
a = { [100, 200, 300].choose }; // a function is enclosed within curly brackets
b = { 10.rand + 1 }; // another Function
c = a + b; // c is a Function.
c.value.postln; // evaluate c and print the result
)
A function with arguments:
- Link to SC3 script below
- ./src/func-with-args.scd
f = { |a, b| (a * b).postln }; // |a, b| are the arguments
f.value(3, 10);
A SynthDef is a language side representation of a synth definition on the server. This is the proper way to go in order to define sounds and call them in the language. They are more efficient than UGens.
A synth with no arguments (no external control of it).
- Link to SC3 script below
- ./src/def-no-args.scd
SynthDef(\def_noArgs, { Out.ar(0, LPF.ar(PinkNoise.ar(1), 550))}).add;
a = Synth(\def_noArgs); // play the sound
a.free; // stop the sound and release the Synth
// (check the incremental values on the Post Window)
- Link to SC3 script below
- ./src/def-with-args.scd
(
SynthDef(\def_args, { | freq = 440 |
Out.ar(0,
Pan2.ar(
SinOsc.ar(freq, 0, 0.2) * Line.kr(1, 0, 1, doneAction: 2) // Line releases the Synth
)
)
}).add;
)
a = Synth(\def_args); // you don't have to free the Synth doneAction: 2 does that for you
a = Synth(\def_args, [\freq, 330]); // change the frequency
An Introduction to GUI and a List of GUI Classes are available from on sccode.org.
- Link to SC3 script below
- ./src/button-ugen.scd
(
s.waitForBoot({
w = Window.new;
b = Button(w, Rect(20, 20, 80, 26))
.states_([["play", Color.black, Color.rand]])
.mouseDownAction_({
a = {EnvGen.kr(Env.adsr, doneAction:2) * SinOsc.ar(440, 0, 0.4)}.play;
})
.action_({ // an action sets the value to the View when something happens (ie. a button pressed)
a.release(0.3); // set the action to release the sound
});
w.front;
})
)
The code below defines a synth and you can listen the sound using a button.
- Link to SC3 script below
- ./src/button-synthdef.scd
SynthDef(\noise, { | amp = 0.2 | Out.ar(0, PinkNoise.ar(amp)); }).add;
a = Synth(\noise);
a.set(\amp, 0.3); // set the argument while the Synth is playing
a.free;
(
var w, butt, sound; // w is not an interpreter's var -- it's a local var
// define a window
w = Window.new("first piece", Rect(0, 0, 200, 200));
w.front; // make the window
// make the button
butt = Button(w, Rect(0, 0, 100, 100));
//define the states of the button
butt.states = [["start"], ["stop"]];
butt.action = { | v |
if (v.value == 1) {
sound = Synth('noise2');
w.onClose({
{ butt.value = 0 }.defer;
});
}{
sound.free;
}
};
)
A sequencer that uses buffers to load samples.
Load the buffers and define the synth.
- Link to SC3 script below
- ./src/grid-sequencer-defs.scd
// you can use the following sound files from freesound.org
/*
http://www.freesound.org/people/datasoundsample/sounds/41947/
http://www.freesound.org/people/datasoundsample/sounds/41948/
http://www.freesound.org/people/datasoundsample/sounds/41942/
http://www.freesound.org/people/datasoundsample/sounds/41944/
*/
(
// make sure to boot the server before loading the samples
s.waitForBoot {
// load samples from relative path (make a subfolder in the working directory with name 'samples')
p = (thisProcess.nowExecutingPath.dirname++"/samples/*").pathMatch;
// collect samples to buffers
c = p.collect{|item| Buffer.read(s, item) };
}
)
// define the SynthDef (synth definition)
(
// buffdrums
SynthDef(\buffdrums, { |bufnum=0, pan=0.0, amp=0.5|
Out.ar(0, Pan2.ar(PlayBuf.ar(1, bufnum, doneAction: 2), pan, amp))
}).add;
)
// play drum hits
Synth(\buffdrums, [\bufnum, c[0]]);
Synth(\buffdrums, [\bufnum, c[1]]);
Synth(\buffdrums, [\bufnum, c[2]]);
Synth(\buffdrums, [\bufnum, c[3]]);
Make the GUI and play the routine.
- Link to SC3 script below
- ./src/grid-sequencer.scd
(
w = Window.new("Grid");
a = [
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value })
];
b = [
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value })
];
w.layout_( GridLayout.rows(
[ a[0], a[1], a[2], a[3] ],
[ b[0], b[1], b[2], b[3] ],
[ b[4], b[5], b[6], b[7] ],
[ b[8], b[9], b[10], b[11] ],
[ b[12], b[13], b[14], b[15] ]
)).front;
)
//a[0].valueAction = 1;
//b[0].value;
(
t = 0.3;
r = Routine {
loop {
4 do: { |i|
a[i].valueAction = 1; // switch on the current beat
a[(i+1)%4].valueAction = 0; // switch off all other beats
a[(i+2)%4].valueAction = 0;
a[(i+3)%4].valueAction = 0;
// play the sounds
4 do: { |j|
if( (b[j].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[3]]);
};
if( (b[j+4].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[2]]);
};
if( (b[j+8].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[1]]);
};
if( (b[j+12].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[0]]);
};
};
t.wait; // t values below ~0.04 will not produce accurate sequencing
};
};
};
// start the routine
r.play(AppClock); // AppClock is used with GUI
)
// stop the routine
r.stop;
(
w = Window.new("Grid"); // a, b, w are interpreter's vars
a = [
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.green], ["", Color.black, Color.yellow]])
.action_({ | butt | butt.value })
];
b = [
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value }),
Button(w)
.states_([["", Color.black, Color.white], ["", Color.black, Color.red]])
.action_({ | butt | butt.value })
];
w.layout_( GridLayout.rows(
[ a[0], a[1], a[2], a[3] ],
[ b[0], b[1], b[2], b[3], [ p = Slider(w), rows:4] ], // control amp with p
[ b[4], b[5], b[6], b[7] ],
[ b[8], b[9], b[10], b[11] ],
[ b[12], b[13], b[14], b[15] ],
[
[
k = Slider(w) // bufnum = 3
.orientation_(\horizontal)
.value_(0.5),
columns:4
]
],
[
[
l = Slider(w) // bufnum = 2
.orientation_(\horizontal)
.value_(0.5),
columns:4
]
],
[
[
m = Slider(w) // bufnum = 1
.orientation_(\horizontal)
.value_(0.5),
columns:4
]
],
[
[
n = Slider(w) // bufnum = 0
.orientation_(\horizontal)
.value_(0.5),
columns:4
]
]
)).front;
)
//a[0].valueAction = 1;
//b[0].value;
(
t = 0.3;
r = Routine {
loop {
4 do: { |i|
a[i].valueAction = 1; // switch on the current beat
a[(i+1)%4].valueAction = 0; // switch off all other beats
a[(i+2)%4].valueAction = 0;
a[(i+3)%4].valueAction = 0;
// play the sounds
4 do: { |j|
// control panning
if( (b[j].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[3], \pan, (k.value * 2 - 1)]);
};
if( (b[j+4].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[2], \pan, (l.value * 2 - 1)]);
};
if( (b[j+8].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[1], \pan, (m.value * 2 - 1)]);
};
if( (b[j+12].value==1) && (i%4==j) ){
Synth(\buffdrums, [\bufnum, c[0], \pan, (n.value * 2 - 1)]);
};
};
t.wait; // t values below ~0.04 will not produce accurate sequencing
};
};
};
// start the routine
r.play(AppClock); // AppClock is used with GUI
)
// stop the routine
r.stop;
See Pattern help file. Some popular pattern classes are Pbind
, Pseq
, Pwhite
, Prand
, Ppar
, Prout
, Pn
.
- Link to SC3 script below
- ./src/pattern-sequencing.scd
(
s.waitForBoot{
// all sounds (buffers) are loaded from the same synthdef
SynthDef(\buff_pat, { |bufnum=0| Out.ar(0, PlayBuf.ar(1, bufnum, doneAction: 2)) }).add;
// path to samples
p = (thisProcess.nowExecutingPath.dirname++"/samples/*").pathMatch;
// collect samples to buffers
c = p.collect{|item| Buffer.read(s, item) };
s.sync; // to wait for asynchronous things to complete
p = Ppar([
Pbind(\instrument, \buff_pat, \dur, 0.25, \bufnum, Prand(c.collect(_.bufnum)))
], inf // repeat for ever
).play;
};
)
A handy way to play with patterns is performed using method asStream
. The example below is from Pn help file.
- Link to SC3 script below
- ./src/pattern-as-stream.scd
// sound example
(
SynthDef(\help_sinegrain,
{ arg out=0, freq=440, sustain=0.05;
var env;
env = EnvGen.kr(Env.perc(0.01, sustain, 0.2), doneAction:2);
Out.ar(out, SinOsc.ar(freq, 0, env))
}).add;
)
(
var a;
a = Pn(Pshuf([1, 2, 2, 3, 3, 3], 4)).asStream; // repeat pattern four times
{
loop {
Synth(\help_sinegrain, [\freq, a.next * 600 + 300]); // sets the next value of the pattern
0.2.wait;
}
}.fork; // fork is alternative syntax for Routine
)
- Link to SC3 script below
- ./src/demand-sequencing.scd
(
{
var freq;
freq = Duty.kr(
Drand([0.01, 0.2, 0.4], inf), // demand ugen as durations
0,
Dseq([204, 400, 201, 502, 300, 200], inf)
);
SinOsc.ar(freq * [1, 1.01]) * 0.1
}.play;
)
Defining a class to record the output of SC3.
- Link to SC3 script below
- ./src/Record.sc
// By Ioannis Zannos
Record {
*start {
{
Server.default.prepareForRecord;
0.1.wait;
Server.default.record;
}.fork;
}
*stop { Server.default.stopRecording; }
}
Save the class as a file Record.sc
in your Platform.userExtensionDir
folder, and recompile the class library.
Platform.userExtensionDir; // Extensions available only to your user account
Platform.systemExtensionDir; // Extensions available to all users on the machine
When you have recompile the library execute Record.start
to record and Record.stop
to stop the recording.
You can open the server’s GUI with the command s.makeGui
, press the record button and record.