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