First Track made with Supercollider

That’s my first (ambient kind of) track made entirely with Supercollider. Not sure about the kick: maybe the track would better work without it? Only mixed on headphones yet and I need to make the car test.

I recorded the single tracks into Reaper and made some fine adjustements there. The reverb was replaced in Reaper by the Abyss reverb from Saike and Galactic 3 from Airwindows.

If someone is interested in the source code (partially used ChatGPT, especially for the routing stuff):

TempoClock.default.tempo = 60/60;

////////////////////////////////////////////////////////////
// SERVER
////////////////////////////////////////////////////////////

(
s.options.numOutputBusChannels = 18;
s.options.numInputBusChannels = 2;
s.reboot;
)

s.meter;

////////////////////////////////////////////////////////////
// REVERB BUS + REVERB SYNTH
////////////////////////////////////////////////////////////

(
s.waitForBoot({
	~verbBus = Bus.audio(s, 2);

	SynthDef(\globalVerbStem, {
		arg inbus = 0, mainOut = 0, stemOut = 16, mix = 1, room = 1, damp = 0.2, amp = 0.95;
		var dry, wet;
		dry = In.ar(inbus, 2);
		wet = FreeVerb2.ar(dry[0], dry[1], mix, room, damp);
		wet = wet * amp;

		Out.ar(mainOut, wet);
		Out.ar(stemOut, wet);
	}).add;

	s.sync;

	~verbSynth = Synth.tail(s, \globalVerbStem, [
		\inbus, ~verbBus,
		\mainOut, 0,
		\stemOut, 16,
	]);

	"reverb ready".postln;
});
)

////////////////////////////////////////////////////////////
// SYNTHDEFS
////////////////////////////////////////////////////////////

(
SynthDef(\kick, {
	arg mainOut = 0, stemOut = 2, amp = 0.5, pan = 0, atk = 0.001, dec = 0.22, upfq = 110;

	var pitchEnv, bodyEnv, body, click, clickEnv, sig;
	pitchEnv = EnvGen.kr(Env([upfq, 55], [0.08], -2));

	bodyEnv = EnvGen.kr(
		Env.perc(atk, dec, 1, -4),
		doneAction: 2
	);

	body = SinOsc.ar(pitchEnv) * bodyEnv;

	clickEnv = EnvGen.kr(
		Env.perc(0.0005, 0.01, 1, -8)
	);

	click = BPF.ar(WhiteNoise.ar(1), 2500, 0.8) * clickEnv * 0.25;

	sig = body + click;
	sig = (sig * 4).tanh * 0.4;
	sig = Pan2.ar(sig * amp, pan);

	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\bass, {
	arg mainOut = 0, stemOut = 4, freq = 40, amp = 0.15, pan = 0,
	atk = 2, dec = 1, sus = 0.8, rel = 6,
	ffreq = 120, fmax = 1200, rq = 0.3;

	var osc, sub, env, fenv, drift, sig;

	drift = SinOsc.kr(0.03).range(0.995, 1.005);
	osc = Saw.ar(freq * [0.995, 1.0, 1.005] * drift);
	osc = Mix(osc) * 0.5;
	sub = SinOsc.ar(freq * 0.5, 0, 0.2);

	env = EnvGen.ar(Env.perc(atk, rel), doneAction: 2);
	fenv = EnvGen.ar(Env([ffreq, fmax], [rel * 0.7], \exp));

	sig = osc + sub;
	sig = RLPF.ar(sig, fenv, rq);
	sig = sig * env * amp;
	sig = Pan2.ar(sig, pan);

	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\whitenoise, {
	arg mainOut = 0, stemOut = 6, verbBus = 0, atk = 1, dec = 4, amp = 0.2, pan = 0, filtfreq = 8000, send = 0.2;

	var env, sig, comb;

	env = EnvGen.ar(Env.perc(atk, dec, 1, -0.1), doneAction: 2);

	sig = PinkNoise.ar;
	sig = HPF.ar(sig, 4000);
	sig = LPF.ar(sig, filtfreq);

	comb = CombL.ar(sig, 0.1, 0.04, 0.5) * 0.12;
	sig = sig + comb;
	sig = sig * LFNoise1.ar(8).range(0, 1);

	sig = tanh(sig * 1.1);
	sig = sig * env * amp;
	sig = Pan2.ar(sig, pan);

	Out.ar(verbBus, sig * send);
	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\sinenoise, {
	arg mainOut = 0,
	stemOut = 8,
	verbBus = 0,
	amps = 0.05,
	ampn = 0.05,
	freq = 440,
	pan = 0,
	reln = 0.3,
	rels = 0.8,
	send = 0,
	lpf = 14000;

	var envn, envs;
	var white, pink, noiseMix;
	var sine, sig;

	envn = EnvGen.ar(
		Env.perc(0.001, reln, 1, -4),
		doneAction: 0
	);

	envs = EnvGen.ar(
		Env.perc(0.001, rels, 1, -4),
		doneAction: 2
	);

	white = WhiteNoise.ar;
	pink = PinkNoise.ar;

	noiseMix = XFade2.ar(
		white,
		pink,
		SinOsc.kr(0.2)
	) * envn;

	sine = SinOsc.ar(freq) * envs;

	sig = (noiseMix * 0.3 * ampn) + (sine * amps);
	sig = HPF.ar(sig, 200);
	sig = LPF.ar(sig, lpf);
	sig = Pan2.ar(sig, pan);

	Out.ar(verbBus, sig * send);
	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\spectrumSwarm, {
	arg mainOut = 0, stemOut = 8, verbBus = 0,
	freq = 3520, amp = 0.3, panSpread = 0.8, rel = 1, dust = 1, send = 0;

	var freqs, sig, grains, env;

	freqs = freq * [0.5, 1, 1.01, 1.5, 2, 2.02, 3];
	grains = Dust.ar(dust);
	env = EnvGen.ar(Env.perc(0.001, rel), doneAction: 2);

	sig = Mix.fill(freqs.size, {
		var osc;
		osc = SinOsc.ar(
			freqs.choose * LFNoise1.kr(0.5).range(0.99, 1.01)
		);
		osc * grains
	});

	sig = HPF.ar(sig, 300);
	sig = Splay.ar(sig, panSpread);
	sig = sig * env * amp;

	Out.ar(verbBus, sig * send);
	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\saw, {
	arg mainOut = 0, stemOut = 10, verbBus = 0,
	freq = 220, amp = 0.08, atk = 2, rel = 6, pan = 0,
	ffreq = 900, rq = 0.25, detune = 0.015, send = 0.5;

	var sig, env, freqs, drift, cutoff, widthMod;

	drift = LFNoise1.kr([0.03, 0.05, 0.07, 0.11, 0.13]).range(1 - detune, 1 + detune);
	freqs = freq * drift * [0.5, 0.99, 1, 1.01, 2];

	sig = Saw.ar(freqs);

	widthMod = SinOsc.kr(0.02).range(0.4, 1.0);
	sig = Splay.ar(sig, widthMod);

	env = EnvGen.ar(Env.perc(atk, rel, 1, -4), doneAction: 2);

	cutoff = ffreq * LFNoise1.kr(0.04).range(0.6, 1.2);
	cutoff = cutoff.clip(80, 2000);

	sig = RLPF.ar(sig, cutoff, rq);
	sig = LPF.ar(sig, cutoff * 1.2);
	sig = BRF.ar(sig, LFNoise1.kr(0.03).range(300, 1200), 0.3);
	sig = tanh(sig * 1.4);
	sig = HPF.ar(sig, 300);

	sig = sig * env * amp;
	sig = Balance2.ar(sig[0], sig[1], pan);

	Out.ar(verbBus, sig * send);
	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


SynthDef(\klank, {
	arg mainOut = 0, stemOut = 12, verbBus = 0, freq = 440, amp = 0.05, pan = 0, rel = 2, send = 0.1;

	var harm, amps, ring, exc, sig, env;

	harm = [1, 2, 3, 4];
	amps = [0.2, 0.15, 0.1, 0.05];
	ring = [1, 1.2, 0.8, 1.5];

	exc = PinkNoise.ar(0.02) * Decay2.ar(Impulse.ar(1), 0.001, 0.1);
	sig = Klank.ar(`[harm, amps, ring], exc, freq);

	env = EnvGen.kr(Env.perc(0.01, rel), doneAction: 2);

	sig = sig * env * amp;
	sig = Pan2.ar(sig, pan);

	Out.ar(verbBus, sig * send);
	Out.ar(mainOut, sig);
	Out.ar(stemOut, sig);
}).add;


////////////////////////////////////////////////////////////
// PATTERNS
////////////////////////////////////////////////////////////

~mixGain = 2.5;

Pdef(\drum1,
	Pbind(
		\instrument, \kick,
		\dur, Pseq([0.25, Prand([1, Rest(1)]), Prand([0.5, Rest(0.5)]), 1, Prand([1, Rest(1)])], inf),
		\amp, Pwhite(0.1, 0.12) * ~mixGain,
		\dec, Pwhite(0.25, 0.5),
		\upfq, Pwhite(100, 120)
	)
);


Pdef(\hh,
	Pbind(
		\instrument, \whitenoise,
		\verbBus, ~verbBus,
		\dur, Pseq([Rest(1.25), Prand([0.25, Rest(0.25)]), Rest(3.75 + 1.75), Prand([0.25, Rest(0.25)]), Rest(0.25)], inf),
		\amp, Pwhite(0.02, 0.03) * ~mixGain,
		\dec, Pwhite(4, 8),
		\atk, Pwhite(0.01, 0.1),
		\filtfreq, Pwhite(5000, 6000),
		\timingOffset, Pwhite(0.001, 0.01),
		\send, 0.5
	)
);


Pdef(\bass,
	Pbind(
		\instrument, \bass,
		\dur, Pseq([4, 2], inf),
		\freq, Pseq([45, 52, 60, 48].midicps, inf),
		\amp, 0.06 * ~mixGain,
		\atk, 0.02,
		\dec, 0.5,
		\rel, 5,
		\ffreq, 80,
		\fmax, Pseg(Prand([500, 650, 800, 700, 600, 550] * 1.5, inf), 28),
		\rq, 0.1
	)
);

Pdef(\sines,
	Pbind(
		\instrument, \sinenoise,
		\verbBus, ~verbBus,
		\scale, Scale.minor,
		\root, 9,
		\degree, Pwrand([0,2,4,5], [0.4,0.3,0.2,0.1], inf),
		\octave, Pwhite(4,6),
		\dur, Prand([2, 1, Prand([0.5, Rest(0.5)], Rest(4)), 2, 1], inf),
		\reln, Pwhite(0.0004, 0.02),
		\rels, Pwhite(0.08, 5),
		\amps, Pwhite(0.01, 0.02) * ~mixGain,
		\ampn, Pwhite(0.01, 0.02),
		\pan, Pwhite(-0.8,0.8),
		\send, 0.4
	)
);

Pdef(\sines2,
	Pbind(
		\instrument, \sinenoise,
		\verbBus, ~verbBus,
		\scale, Scale.minor,
		\root, 9,
		\degree, Pwrand([0,2,4,5], [0.4,0.3,0.2,0.1], inf),
		\octave, Pwhite(7,8),
		\dur, Prand([2, 0.5, Prand([1, Rest(1)], Rest(2)), 1, 4], inf),
		\reln, Pwhite(0.01, 0.02),
		\rels, Pwhite(0.08, 5),
		\amps, Pwhite(0.005, 0.01) * ~mixGain,
		\ampn, Pwhite(0.01, 0.02) * ~mixGain,
		\pan, Pwhite(-0.8,0.8),
		\send, 0.6
	)
);

Pdef(\tom,
	Pbind(
		\instrument, \sinenoise,
		\verbBus, ~verbBus,
		\dur, Pseq([Rest(1), 0.25, Rest(0.25), 1.25, Rest(1)], inf),
		\reln, Pwhite(0.001, 0.012),
		\rels, 0.1,
		\amps, Pwhite(0.10, 0.12) * ~mixGain,
		\ampn, Pwhite(0.00015, 0.002) * ~mixGain,
		\pan, Pwhite(-0.02, 0.02),
		\lpf, Pwhite(8000, 9000),
		\freq, Pseq([220, 220, 440], inf),
		\send, 0.01
	)
);

Pdef(\noise3,
	Pbind(
		\instrument, \sinenoise,
		\verbBus, ~verbBus,
		\dur, Prand([Pseq([0.1, 0.1, 0.1, 0.1]), Rest(4)], inf),
		\reln, Pwhite(0.0001, 0.012),
		\rels, 0.1,
		\amps, Pwhite(0.002, 0.003) * ~mixGain,
		\ampn, Pwhite(0.04, 0.07) * ~mixGain,
		\pan, Pwhite(-0.8,0.8),
		\send, 1
	)
);

Pdef(\particles,
	Pbind(
		\instrument, \spectrumSwarm,
		\verbBus, ~verbBus,
		\dur, Pwhite(0.05, 0.2),
		\freq, Pseg(Pseq([800, 2000, 5000, 1200], inf), Pseq([12, 18, 10], inf), \exp),
		\rel, Pwhite(0.03, 0.2),
		\dust, Pwhite(0.25, 1),
		\amp, Pwhite(0.01, 0.02) * ~mixGain,
		\send, 0.8
	)
);

Pdef(\burst,
	Pbind(
		\instrument, \sinenoise,
		\verbBus, ~verbBus,
		\dur, Pseq([0.25, 0.25, 0.25, Rest(8)], inf),
		\freq, Pexprand(1000, 12000),
		\reln, 0.005,
		\rels, 0.15,
		\amps, Pwhite(0.01, 0.03) * ~mixGain,
		\ampn, Pwhite(0.6, 1.2) * ~mixGain,
		\pan, Pwhite(-1, 1)
	)
);

Pdef(\sawpad,
	Pbind(
		\instrument, \saw,
		\verbBus, ~verbBus,
		\dur, Pseq([4], inf),
		\midinote, Pseq([45, 52, 60, 48], inf) + Pdup(4, Prand([12, 24], inf)),
		\atk, Pwhite(2.0, 4.0),
		\rel, Pwhite(5.0, 10.0),
		\amp, Pwhite(0.03, 0.035) * ~mixGain,
		\ffreq, Pwhite(500, 2200),
		\rq, Pwhite(0.18, 0.35),
		\pan, Pwhite(-0.4, 0.4),
		\send, 0.5
	)
);

Pdef(\pklank,
	Pbind(
		\instrument, \klank,
		\verbBus, ~verbBus,
		\freq, Pseq([440, 659.29, 523.25, 440], inf),
		\amp, Pwhite(0.8, 0.9) * ~mixGain,
		\pan, Pwhite(-1,1),
		\rel, Pwhite(0.5, 4),
		\dur, Pwhite(0.2, 4),
		\send, 0.8
	)
);


// Play the whole thing
~r = Routine({

	"intro".postln;

	Pdef(\pklank).play(quant: 0);
	Pdef(\bass).play(quant: 0);

	32.wait;

	"sines".postln;
	Pdef(\sines).play(quant: 0);
	Pdef(\particles).play(quant: 0);

	32.wait;

	"drum".postln;
	Pdef(\drum1).play(quant: 0);
	Pdef(\tom).play(quant: 0);
	Pdef(\sines2).play(quant: 0);
	Pdef(\pklank).stop;

	32.wait;

	"bring in saw".postln;
	Pdef(\sawpad).play(quant: 0);
	Pdef(\noise3).play(quant: 0);

	64.wait;

	"hh".postln;
	Pdef(\pklank).play(quant: 0);
	Pdef(\hh).play(quant: 0);
	Pdef(\sines2).stop;

	64.wait;

	"kick break".postln;
	Pdef(\drum1).stop;
	Pdef(\hh).stop;
	Pdef(\tom).stop;
	Pdef(\particles).stop;
	Pdef(\noise3).stop;

	32.wait;

	"kick back".postln;
	Pdef(\drum1).play(quant: 0);
	Pdef(\hh).play(quant: 0);
	Pdef(\tom).play(quant: 0);
	Pdef(\particles).play(quant: 0);
	Pdef(\noise3).play(quant: 0);
	Pdef(\pklank).stop;

	32.wait;

	"bring in pklank and sines2".postln;
	Pdef(\pklank).play(quant: 0);
	Pdef(\sines2).play(quant: 0);

	48.wait;

	"stop percussion".postln;
	Pdef(\tom).stop;
	Pdef(\noise3).stop;
	Pdef(\particles).stop;
	Pdef(\drum1).stop;
	Pdef(\hh).stop;

	16.wait;

	"stop both sines".postln;
	Pdef(\sines).stop;
	Pdef(\sines2).stop;

	32.wait;

	"stop bass and pklank".postln;
	Pdef(\bass).stop;
	Pdef(\pklank).stop;

	32.wait;
	Pdef(\sawpad).stop;

}).play;
)


(
~r.stop;
Pdef.all.do(_.stop);
)

4 Likes

Cool!

For future reference, SuperCollider does have some decentish reverbs. JPVerb and GreyHole are available as part of the DEIND plugins. And also Nathan Hall’s plugin (available as part of the extensions collection) can be pretty decent. Though more reverb options would be nice :slight_smile:

You can also run VSTs in SuperCollider.

Not that there’s anything wrong with doing it the way you did - but it’s nice to have options.

2 Likes

Yes, I think recording directly from SC is better.I also will remove the kick and some sounds that do not fit that well.

Finally:

The title changed. Really happy with this. This time directly recorded from SC into Reaper (2 channels).

3 Likes

are the higher sine and saw tones too loud and does it hurt your ears?

Maybe the top end balance could be a bit quieter, but they don’t hurt me (tho I’m a bit deaf from years of dance music at clubs in my youth!). Love the track.
Best,
Paul

1 Like

thanks; i will tame it a bit

Also, re: reverbs in SC, as well as @cian’s suggestions, just wanted to mention convolution reverbs using PartConv.
There are tons of free Impulse samples online which cover a lot of options, some of which are sampled from expensive high end reverb units as well as all kinds of real spaces. They go from very realistic to very synthetic with experimental FX too.
Maybe worth trying if you ever need something different.
Best,
Paul

ps. see Convolution reverb and impulse responses for extra code examples

2 Likes

thanks. In this case i like the reverb.