Monthly Archives: March 2021

Sawtooth Minimalism

Sawtooth Minimalism

My colleague, Jackie Walduck, was showing students how to create phase music by putting loops out of synch in a DAW. Then I went home from teaching and created a PD patch to play low-pass filtered sawtooth waves, as an example for a different class. I got curious how much easier it would be in SuperCollider and created a tiny program that did the same thing and set it going at the same time. Then I started it again. The phasing was fixed, but it called to mind the shifting relationships of earlier that day.





This version is only SuperCollider, but it uses a non band limitted sawtooth to mimic phasor~ in PD. This version is 7 minutes long, but the proper version should be about 3 hours. I get lost in the phasing and feel relaxed.

I’ve attached the code below. If you want to to last longer, look for the number 340 – that’s 5’40” in seconds. Make it as long as you’d like.

s.waitForBoot({
	var scale, dur, pb, steps=16, loops = 4, loop_calc, env_dur, db = -14, fade_in=40, decay = 20;

	loop_calc = {|note_dur, section_dur|
		var loop_dur, total_dur;

		loop_dur = note_dur * steps * loops;
		total_dur = loop_dur * (section_dur/loop_dur).ceil;
	};

	SynthDef(\saw, {|out=0, freq, amp, dur, pan|
		var saw, env, lop, pan2;
		saw = LFSaw.ar(freq);
		lop = LPF.ar(saw, freq*4);
		env = EnvGen.kr(Env.triangle(dur), doneAction:2);
		pan2 = Pan2.ar(lop, pan, env);
		Out.ar(out, pan2 * amp);
	}).add;

	//s.prepareForRecord("/home/celesteh/Documents/Pieces/SawtoothMinimalism/take-" ++ Date.getDate.rawSeconds ++".wav" , 2);

	s.sync;

	//s.record;

	dur = 60 / 250; // 250 BPM

	env_dur = loop_calc.(dur, 340) - (fade_in+ decay);
	env_dur.postln;

	scale = Scale.minorPentatonic(Tuning.choose(5));

	pb = {|limit = 7|
		Pbind(
			\instrument, \saw,
			\db, db,
			\dur, dur,
			\scale, scale,
			\degree, Prout({
				var loop;

				inf.do({
					loop = steps.collect({ /*scale.degrees[5.rand]*/ limit.rand });
					loop = loop.scramble;
					loops.do({
						loop.do({|step|
							step.yield;
						})
					})
				})
			}),
			//\midinote, Pkey(\add) + offset,
			\out, 0
		);
	};

	s.sync;

	Pseq([
		Pbindf(pb.value, \octave, 4, \pan, 0, \continue, Pn(1,loops*steps)),
		Ptpar([
			0, Pseq([
				Pbindf(pb.value, \octave, 4, \pan, -75, \continue, Pn(1, loops*steps*4)),
				Pbindf(pb.value(5), \octave, [4, 5], \pan, -1, \db, Penv([db, db-2, db, db], [fade_in,decay, env_dur])),
				Pbindf(pb.value, \octave, 4, \pan, -0.88, \db, Pn(db+1, 1), \degree, [0, 2], \dur, dur*8)
			] ),
			0.0008.rrand(dur/10), Pseq([
				Pbindf(pb.value, \octave, 5, \pan, 75, \continue, Pn(1, loops*steps*4)),
				Ptpar([
					0, Pseq([Pbindf(pb.(5), \octave, [4,5], \pan, 1, \db, Penv([db, db-2, db, db], [fade_in,decay, env_dur])),
						Pbindf(pb.value, \octave, 4, \pan, 0.88, \db, Pn(db+1, 1), \degree, [1, 4], \dur, dur*8)]),
					(dur * 0.3).rrand(dur*0.7), Pseq([ Pbindf(pb.value,
						\octave, 5,
						\pan, Pwhite(-0.1, 0.1),
						\dur, Pwhite((dur / -700), (dur/700)) + dur,
						\db, Penv([-60, db+1, db, db], [fade_in, decay, env_dur])),
					Pbindf(pb.value, \octave, 5, \pan, 0, \db, Pn(db+1, 1), \degree, [0, 3, 5], \dur, dur*8)
					])
				])
			])

		]),

		Pbind(
			\dur, dur,
			\freq, Prout({
				\rest.yield;
				//s.stopRecording;
			})
		)
	]).play;
});

Play