Skip to content

Latest commit

 

History

History
575 lines (495 loc) · 17.1 KB

meetup-wk2.org

File metadata and controls

575 lines (495 loc) · 17.1 KB

Sounds and GUI

Basic keyboard shortcuts

  • Evaluate region: Ctrl + RET (Control and Return)
    • 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.

Function

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);

SynthDef

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

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)

A synth with arguments

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

GUI and sequencers

An Introduction to GUI and a List of GUI Classes are available from on sccode.org.

GUI and UGen

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;
})
)

GUI and SynthDef

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;
    }
};
)

GUI, Buffers and Routine

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;

Grid sequencer and sliders

(
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;

Sequencing using patterns

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
)

Demand rate UGens

See Demand, Duty help files.

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;
)

How to make a recording

The hard way

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.

An easy way

You can open the server’s GUI with the command s.makeGui, press the record button and record.