View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide

Cottle Chapter 9 - Subtractive Synthesis, Noise, Synth.write, Synth.record

Home   How To   Code Pool   Public Library   Theory   Events
[Cottle examples]

9. Subtractive Synthesis, Noise, Synth.write, Synth.record


//9.1 noise

// this ex. must be simplified -- but, I'll implement it in a way that you
// can switch between the different noise generators without stopping
// the synth :)

s = Server.internal;
s.boot;

(
var signalName, choice;

a = { arg choice = 0;
	var signal;
	
	signal = [WhiteNoise.ar(mul: 0.4),  PinkNoise.ar(mul: 0.4),
	BrownNoise.ar(mul: 0.4), GrayNoise.ar(mul: 0.4),
	Dust.ar(3, mul: 0.4)];
	
	Select.ar(choice, signal);
}.scope;
)

a.set(\choice, 1);	// these lines change the value of
a.set(\choice, 2);	// choice inside the playing synth
a.set(\choice, 3);	// on synth a, set the value of "choice" to 3
a.set(\choice, 4);
a.set(\choice, 0);

a.free;

//9.2 Filtered Noise

(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: [0.1, 0.1]);
cutoff = MouseX.kr(40, 10000, 'exponential');
resonance = MouseY.kr(2.0, 0.01);

RLPF.ar(signal, cutoff, resonance) }.scope;
)

(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: 0.1);
cutoff = MouseX.kr(40, 10000, 'exponential');
resonance = MouseY.kr(2.0, 0.01);

RHPF.ar(signal, cutoff, resonance) }.scope;
)

(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: 0.7);
cutoff = MouseX.kr(40, 10000, 'exponential');
resonance = MouseY.kr(2.0, 0.01);

BPF.ar(signal, cutoff, resonance) }.scope;
)


//9.3 Saw with Filter

(
{
var signal, filter, cutoff, resonance;

signal = LFSaw.ar(100, mul: 0.1);
cutoff = MouseX.kr(40, 10000, 'exponential');
resonance = MouseY.kr(2.0, 0.01);

RHPF.ar(signal, cutoff, resonance) }.scope;
)


//9.4 Resonant array

// Klank does not have a mul argument anymore. Therefore you must
// state the multiplication explicitly.

(
{
Klank.ar(
	`[
	[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] //freq array
	],
	BrownNoise.ar(0.03)) * 0.1
}.scope;
)

(
{

var out;
out = Klank.ar(
	`[
	Array.fill(15, {exprand(60, 10000)}) //freq array
	],
	BrownNoise.ar(0.03)) * 0.05
}.scope;
)

//Chimes
//
//9.5 chime burst 

// Guess how the Spawn has to be rewritten? Can you start from
// Cottle's original example and do it yourself (before looking at the answer)?

(
SynthDef("chime", {
	var burstEnv, att = 0, burstLength = 0.001, signal; //Variables
	burstEnv = Env.perc(0, burstLength); //envelope times
	Out.ar(0, PinkNoise.ar(EnvGen.kr(burstEnv, doneAction:2))); //Noise burst
}).send(s);

r = Task({
	inf.do({
		0.3.wait;
		Synth("chime", target:s);
	})
}).play(SystemClock);
)

r.stop;

//9.6 chimes

(
var chime, freqSpecs, totalHarm = 10;

freqSpecs = `[
	Array.fill(totalHarm, {rrand(100, 1200)}), //freq array
	Array.fill(totalHarm, 
		{rrand(0.3, 1.0)}).normalizeSum.round(0.1), //amp array
	Array.fill(totalHarm, {rrand(2, 4)})]; //decay rate array

SynthDef("chime", { //Beginning of Ugen function
	var signal, burstEnv, att = 0, burstLength = 0.001, masterVol = 0.8;
	burstEnv = Env.perc(0, burstLength); //Define envelope
	signal = PinkNoise.ar(EnvGen.kr(burstEnv)); //Noise burst
	signal = Klank.ar(freqSpecs, signal);
	Out.ar(0, signal*EnvGen.kr(Env.perc(0, 4), levelScale: MouseY.kr(masterVol, 0), doneAction:2));
}).send(s);

r = Task({
	inf.do({
		1.wait;
		Synth("chime", target:s);
	})
}).play(SystemClock);
)

r.stop;

// .write and .record are handled differently.
// .write is SC3's NRT (non-real-time) mode. See the Server helpfiles.
// .record is most easily handled by as below, although other mechanisms exist.
 
//9.7 Synth.record

s.prepareForRecord("test");

{ PinkNoise.ar }.play(s);

s.record;

s.stopRecording;

// order of execution is tricky with this! Read the [Order-of-execution] helpfile.


//Just for Fun

//9.8 Subtracitive Synthesis Fun

// This patch is actually two patches in one, added together at the end.
// In sc3, it's better to put each in its own process (to modularize them)
// so they can be controlled independently.

// First the chimes:
// Chime sound specs are randomly generated once and played repeatedly.
// In this case, Cottle's synth works pretty much as is, with minor edits.

(
c = SynthDef("chimes", {
var totalInst, totalPartials, baseFreq;
totalInst = 12; //Total number of chimes
totalPartials = 15; //Number of partials
baseFreq = rrand(200, 1000); //Base frequency for chimes
Out.ar(0,
	Mix.ar(
	Array.fill(totalInst,
		{
		Pan2.ar(
			Klank.ar(`[
				Array.fill(totalPartials, {baseFreq*rrand(1.0, 12.0)}),
				Array.fill(totalPartials, {rrand(0.3, 0.9)}),
				Array.fill(totalPartials, {rrand(0.5, 6.0)})], 
				Decay.ar(
					Dust.ar(0.2, 0.1), //Times per second, amp
					0.001, //decay rate
					PinkNoise.ar //Noise
				)), 1.0.rand2) //Pan position
		})
	)
)
}).play(s);

// For cavern, the Klank specs are generated randomly each event.
// These can't be passed in to a SynthDef argument (at least, not easily).
// Since the timing need not be so precise, what we can do is
// generate and send a new SynthDef for each event.

// The LFSaw amp control needs to apply to all cavern synths played.
// So, put it on a control bus.

k = { LFSaw.kr(1/60, mul: 0.4, add: 0.5) }.play(s, 0);	// kr bus 0

r = Task({
	inf.do({
		SynthDef("cavern", {
			Out.ar(0, RLPF.ar(Klank.ar(
				`[ //frequency, amplitudes, and decays
					Array.fill(30, {100 * rrand(1, 12) * rrand(1.0, 1.1)}),
					Array.fill(10, {rrand(1.0, 5.0)}).normalizeSum
				],
				GrayNoise.ar([rrand(0.03, 0.1), rrand(0.03, 0.1)])
				) * 0.09, 1000 //overall freq cutoff
			) * EnvGen.kr(Env.linen(4, 7, 4), doneAction:2)
				* In.kr(0, 1)	// get amp control value from kr bus 0
			);
		}).play(s);
		5.wait;
	});
}).play(SystemClock);
);
//End Patch

r.stop;
c.free;
k.free;


Link to this Page