Slow drone improv


#1

This was kicking around from a few weeks ago, from a SynthDef I was working on. Some slow moving slop.


#2

hmmm you should definitely visit aus and play at unsound / dark mofo

I feel very inspired after listening to that. and curious as to what your code looks like, tbh

thank you for sharing your beautiful and terrifying creation!


#3

Hahah, would love to - know anyone booking :smile:? I’ll be going to Unsound Krakow in the fall, it’s actually been a long-time dream festival.


#4

Thank you for the amazing music.
Would it be rude asking you if you intend to plan to share some lines of your code?

best,
prko


#5

Happy to share the code when I’m back at my laptop.


#6

David Sefton (sorry - linkedin was the best I could find) curates unsound adelaide. he’ll probably be in Krakow I would say, so maybe keep an eye out?

David Walsh curates dark mofo.

can’t say I’m chummy with either of them hehe but yeah. do you have a website / bandcamp?


#7

I post personal things to http://ffffsssscccc.tumblr.com

It’s a little out of date - most of my time in the last year went into this:
http://artificia.org/lecreuset.html (a somewhat more formal collaborative project - as much as a year-long sequence of improvisation rituals could ever be called “formal”)


#8

dooeeeep. this is v relevant content for me. where are you based btw?


#9

Seattle! Though I’ll be in Berlin with some short trips to nearby places Sept+Oct this fall.


#10

Sounds beautiful, congrats !


#11

made an account to say this is great and is similar to what i’m trying to accomplish with supercollider! would love to pick your brain//see your code heh


#12

This is a bit of a mess - it’s just a couple hours of pretty rapid experimentation and iteration that ended with the above, and the code reflects it. Pretty sure the recording is roughly this patch - making changes to freq values and one or two parameters and letting the Ndef fadeTime crossfade between states.

The valuable parts of the def:

  • filtered VarSaw is the source sound
  • The summed VarSaws are run through a bank of Plucks tuned to the f’s of the saws, modulated very slowly. When the Pluck freq passes close to one of the f’s of the VarSaws, you get a large amplitude spike at that f/upper partials.x
  • The Plucks are distorted w/ a SoftClipAmp - the amplitude spikes above are folded into distortion and so make more harmonic content that modulates with the modulation of the Pluck f.
  • The signals are Splay'd to two channels, doubled at +1 octave, a given a little reverb.
  • A feedbacked version of the signal gets some delay + various kinds of distortion, and fed back into the chain before the Plucks.

IIRC the feedback adds some good texture, but provides surprisingly little to the overall sound. The PeakFollower is really important - it adds some strange amplitude modulation sounds as it tries to track some of the weird phase interactions of the VarSaws.

If you want a SuperCollider refactoring challenge, try breaking this synth up so you can get roughly the same acoustic result / signal chain, but where you can play individual Synths for each freq separately, and trigger by e.g. a Pbind. I’ve pretty much got it working like that elsewhere, but it’s definitely not so straightforward to do.


(
// Ndef(\flanger).fadeTime = 20

Ndef(\flanger, {
	var sig, sigPluck, local, f, delay, cutoff, rate, width, pluckOffset, pluckCoef, bassTrig;
	var pulse, f0, famp, dist;

	sig = Ndef(\in).ar(2)[0];

	f0 = Select.kr(LFTri.kr(1/64).range(0, 3), [44, 43, 41]).lag(12);
	f = [
		43,
		// f0,
		43.05,
		50
	];

	f = ([
		f[0], f[0] + 0.1, f[1], f[2], f[0] + 12, f[1] + 12,
	]).postln;

	f = f.midicps;
	f = f.scramble;

	famp = 1;

	rate = 4;

	pluckOffset = 5;
	pluckCoef = 0.9;

	width = SinOsc.kr(1/34).range(0.1, 0.9);

	pulse = Impulse.ar(rate / 2) + Impulse.ar(rate / 2, 1/2.2);
	pulse = pulse * LFNoise2.ar(20).range(-20, 0).dbamp;
	pulse = Decay.ar(pulse, LFNoise2.ar(1).exprange(0.03, 0.8));

	delay = f / 2;

	cutoff = SinOsc.kr(1/60, rrand(0, 2.0)).lincurve(-1, 1, 100, 3400, 3);

	sig = BLowPass4.ar(VarSaw.ar(f, 0, 0, famp).sum, cutoff, 0.8);
	sig = LeakDC.ar(sig);
	bassTrig = Impulse.ar(1/20);

	local = LocalIn.ar(2);
	local = Rotate2.ar(local[0], local[1], SinOsc.kr(1/16).range(-1, 1));
	local = SoftClipAmp8.ar(local, 0.4);
	local = BLowPass.ar(local, cutoff);
	local = PitchShift.ar(local, 4 / f, [0.5, 4], 0.01).sum;
	local = Decimator.ar(local, f * 8, 16).sum;
	local = -20.dbamp * local;
	local = local + Splay.ar(DelayC.ar(local, 4, { rrand(0.0, 4.0) } ! 6, 0.5));

	sigPluck = delay.collect({
		|delay, i|
		delay = [
			LFCub.ar(1 / rrand(30, 36), 2.0.rand).range(delay - pluckOffset, delay + pluckOffset)
			+ SinOsc.ar(1/2).range(-0.2, 0.2),
			delay * 4.05
		];
		PeakFollower.kr(sig, 0.9) * Pluck.ar(
			sig + local, sig,
			4,
			delay.reciprocal,
			9,
			[pluckCoef, 0.1],
			[1, -16.dbamp]
		).sum;
	});

	sig = SoftClipAmp8.ar(sig + sigPluck, -30.dbamp, 1);
	sig = Splay.ar(sig);
	sig = sig + PitchShift.ar(sig, 2, 0.25);

	sig = LeakDC.ar(sig);
	LocalOut.ar(sig);

	sig = sig + (-20.dbamp * AdCVerb.ar(HPF.ar(sig, 900), 9));

	sig = -6.dbamp * sig;

}).play;
)

#13

Thank you very much!


#14

Thanks Scott, this is very interesting.

Ambient drone type music (Tim Hecker, Loscil, etc) is kind of a mystery to me in that I don’t really have any idea how to even start making it.


#15

awesome, thank you so much! i’ve been playing around with this rather than working all afternoon, oops. using Ndef as opposed to a SynthDef was not something i had done before but wow it’s a really fun way to vary parameters.

also, inserting a tanh(a*sig) at the end and varying a is so satisfying. such a nice base for a wall of distortion. gonna dig into what you suggested and start ripping it apart.


#16

It’s also worth trying the Shaper UGen with some reasonable cheby values - that should produce a more clean and controllable distortion.

In spite of the LeakDC's, I’m still allowing a very large amount of DC offset to creep into the signal, which is another big component I didn’t mention. If you add a bunch more LeakDC's to the Synth and eliminate all DC, the synth gets a whole lot less interesting very fast.

The big references points for this and some other things I’ve been doing recently are the really amorphous, undifferentiatable, continuously varying bits of Roland Kayn’s music:


And maybe the slow sweeping-around-a-central-drone thing Scelsi does (can’t find a great example right now, perhaps pieces of https://www.youtube.com/watch?v=kM0KCuDqlvg)

#17

Thought I would try my hand at your refactor challenge. I think I got it part way there…I split it up into 6 different sound sources but all going into the same feedback and fx loop. I can see how it could be taken a lot further. (Not sure how to format the code in the forum.)

/////////////////////////////////////////
// create proxy space
~p = ~p ? ProxySpace.new(s, \p);
~p.fadeTime = 20;

/////////////////////////////////////////
// freq lfo
(~p[\freq][0] = {
	
	var f1 = \f1.kr(43);
	var f2 = \f2.kr(43.05);
	var f3 = \f3.kr(50);
	var f = [f1, f2, f3];
	f = ([
		f[0], f[0] + 0.1, f[1], f[2], f[0] + 12, f[1] + 12,
	]);
		
	f = f.midicps;
	f.scramble;
};
~p[\freq][2] = \set -> Pbind(
	\f1, Pseq([33, 43], inf),
	\delta, 16
);
);

/////////////////////////////////////////
// cutoff lfo
(~p[\cutoff][0] = {
	SinOsc.kr(1/60, rrand(0, 2.0)).lincurve(-1, 1, 100, 3400, 3);
});

/////////////////////////////////////////
// synths
(
~p[\s0][0] = {
	var freq = ~p[\freq].kr()[0];
	var amp = \amp.kr(1);
	var cutoff = ~p[\cutoff].kr;
	var sig = VarSaw.ar(freq, 0, 0, amp);
	sig = BLowPass4.ar(sig, cutoff, 0.8);
	LeakDC.ar(sig);
};
~p[\s1][0] = {
	var freq = ~p[\freq].kr()[1];
	var amp = \amp.kr(1);
	var cutoff = ~p[\cutoff].kr;
	var sig = LFTri.ar(freq, 0, amp);
	sig = DFM1.ar(sig, cutoff, 0.2);
	LeakDC.ar(sig);
};
~p[\s2][0] = {
	var freq = ~p[\freq].kr()[2];
	var amp = \amp.kr(1);
	var sig = PMOsc.ar(freq, freq * 2.1, SinOsc.kr(LFNoise2.kr(0.1).range(1,5)).range(1,6));
	LeakDC.ar(sig);
};
~p[\s3][0] = {
	var freq = ~p[\freq].kr()[3];
	var amp = \amp.kr(1);
	var cutoff = ~p[\cutoff].kr;
	var sig = SinOscFB.ar(freq, SinOsc.kr(0.1).range(0.1, 0.7), amp);
	sig = RLPF.ar(sig, cutoff, 0.2);
	LeakDC.ar(sig);
};
~p[\s4][0] = {
	var freq = ~p[\freq].kr()[4];
	var amp = \amp.kr(1);
	var cutoff = ~p[\cutoff].kr;
	var sig = Pulse.ar(freq, 0.5, amp);
	sig = BLowPass4.ar(sig, cutoff, 0.8);
	LeakDC.ar(sig);
};
~p[\s5][0] = {
	var freq = ~p[\freq].kr()[5];
	var amp = \amp.kr(1);
	var sig = SinOsc.ar(freq, 0, amp);
	sig
};
);

~p[\mix][0] = \mix -> {~p[\s0]};
~p[\mix][1] = \mix -> {~p[\s1]};
~p[\mix][2] = \mix -> {~p[\s2]};
~p[\mix][3] = \mix -> {~p[\s3]};
~p[\mix][4] = \mix -> {~p[\s4]};
~p[\mix][5] = \mix -> {~p[\s5]};
~p[\mix].set(\mix0, 1, \mix1, 0.5, \mix2, 0.2, \mix3, 0.5, \mix4, 0.5, \mix5, 1);

/////////////////////////////////////////
// main fx
(~p[\flanger2][0] = {
	
	var f = \freq.kr([1,1,1,1,1,1]);
	
	var cutoff = \cutoff.kr(100);
	
	var sig = \sig.ar([0]);
	
	var delay = f / 2;
	
	var osc2 = {arg delay, sig, local;
		
		var pluckOffset = 5;
		var pluckCoef = 0.9;
		
		var sigPluck = delay.collect({|delay, i|
			delay = [
				LFCub.ar(1 / rrand(30, 36), 2.0.rand).range(delay - pluckOffset, delay + pluckOffset)
				+ SinOsc.ar(1/2).range(-0.2, 0.2),
				delay * 4.05
			];
			PeakFollower.kr(sig, 0.9) * Pluck.ar(
				sig + local, sig,
				4,
				delay.reciprocal,
				9,
				[pluckCoef, 0.1],
				[1, -16.dbamp]
			).sum;
		});
		sigPluck;
	};
	
	var fb = {arg f = 1, cutoff = 1;
		var local = LocalIn.ar(2);
		local = Rotate2.ar(local[0], local[1], SinOsc.kr(1/16).range(-1, 1));
		local = SoftClipAmp8.ar(local, 0.4);
		local = BLowPass.ar(local, cutoff);
		local = PitchShift.ar(local, 4 / f, [0.5, 4], 0.01).sum;
		local = Decimator.ar(local, f * 8, 16).sum;
		local = -20.dbamp * local;
		local = local + Splay.ar(DelayC.ar(local, 4, { rrand(0.0, 4.0) } ! 6, 0.5));
		local;
	};
	
	var clip = {arg in, sigPluck;
		var sig = SoftClipAmp8.ar(in + sigPluck, -30.dbamp, 1);
		sig = Splay.ar(sig);
		sig = sig + PitchShift.ar(sig, 2, 0.25);		
		LeakDC.ar(sig);
	};
	
	var local = fb.(f, cutoff);
	var pluck = osc2.(delay, sig, local);
	sig = clip.(sig, pluck);
	LocalOut.ar(sig);
	sig;
});

/////////////////////////////////////////
// reverb
(~p[\flanger2].filter(4, {arg in;
	AdCVerb.ar(HPF.ar(in, \hpf.kr(900)), 9);
}).set(\wet4, 0.3)
);

/////////////////////////////////////////
// route
~p[\flanger2] <<>.freq ~p[\freq];
~p[\flanger2] <<>.cutoff ~p[\cutoff];
~p[\flanger2] <<>.sig ~p[\mix];

/////////////////////////////////////////
// play
~p[\flanger2].play;
~p[\flanger2].stop;

#18

To make a code block in discourse you wrap it in triple-backticks. There’s a nice overview of formatting here


#19

I didn’t know Roland Kayn’s work. I thought I was familiar with the Dutch Electronics avant-garde stuff, but clearly not. The synthesizer he used is pretty intimidating.

This guy worked with him, and his album is pretty nice from what I could hear on Youtube:

A new Tim Hecker track dropped today which also seemed kind of relevant:

Honestly no idea what it is he does, other than heavy use of distortion and probably granular processing of samples, but some of this sounds like feedback to me.


#20

Super cool. Different in a few respects from how I did it (mostly because I decided not to use NodeProxy in the end), but it’s really nice to see someone else’s approach. I haven’t used the \set -> Pbind() pattern much, that could solve some problems for me.