Super Cool SuperCollider Synthesis Challenge (Prize)?

Super Cool SuperCollider Synthesis Challenge - SC³

Anyone interested in doing a challenge?

Try to replicate one sound or phrase using only SuperCollider in one week.
I was thinking something like this to begin with as it’s already got a recipe, so beginner’s like me can participate (you can use any sound design methods you want):

Boards of Canada - bass synth line -

If you don’t like this or think it’s too easy or boring let me know!

I was thinking it would be cool to not allow any extensions (sc3-plugins allowed) What you you think?

We’ll then vote on the winner after the week is up. Points based on how close it is to the original sound, and your creative use of SC.

I’ll get a shirt printed with a SuperCollider themed design as a prize and mail it to you! ( I have to wait for the winner so I can get your size)

Of course, if no one is interested, i’ll just awkwardly retreat into isolation for a while.

All thoughts and suggestions welcome!

1 Like

Fun challenge, thanks! Here’s what I came up with, not perfect and could of course be tweaked endlessly but I unfortunately have other things to do with my day :stuck_out_tongue: I just went for replicating the synth patch, not programming the exact melody or anything.

( // this first
var lfo = { |freq, phase, gate|
  var phasor = (Phasor.kr(gate, freq * 2pi / ControlRate.ir, 0, 2pi) + (phase * 2pi)).wrap(0, 2pi);
  SinOsc.kr(0, phasor);
};

var vco = { |freq, amp|
  var slop = 0.01;
  var rate = freq + LFNoise1.ar(LFNoise1.kr(1).range(0.2, 0.3), mul: slop * (freq / 1000));
  var phase = Phasor.ar(DC.ar(0), rate / SampleRate.ir, 0, 1) * 2pi;
  var k = 12000 * (SampleRate.ir/44100) / (freq * log10(freq));
  var sinSig = SinOsc.ar(0, phase);
  var cosSig = SinOsc.ar(0, phase + (pi/2));
  var sqSig = tanh(sinSig * k);
  var sawSig = sqSig * (cosSig + 1) * 0.5;
  [sqSig, sawSig] * amp;
};

SynthDef(\roygbiv, { |out = 0, midinote = 40, gate = 0|
  var note = midinote.lag2(0.03);
  var freq1 = (note + (lfo.(1.75, 0.75, gate) * 0.2)).midicps;
  var freq2 = (note - 12 + (lfo.(0.98, 0, gate) * 0.25)).midicps;
  var sig = vco.(freq1, 0.4)[1] + vco.(freq2, 0.1)[0] + WhiteNoise.ar(0.04);
  var env = Env.adsr(0.001, 3.3, 0.3, 0.17).ar(0, gate);
  var cutoff = 80 * (env * LFNoise1.kr(0.2).range(40, 45)).midiratio;
  sig = DFM1.ar(sig, cutoff, 0.05, 2.1);
  sig = sig * Env.adsr(0.001, 0, 1, 0.18).ar(0, gate);
  Out.ar(out, sig!2 * 0.22);
}).add;
)


( // then this
x.free; y.free; r.stop;

x = Synth(\roygbiv);

y = {
  var dry = BHiPass.ar(In.ar(0, 2), 80);
  var wet = NHHall.ar(LPF.ar(dry, 3000), 1.3, hiFreq: 2000, hiRatio: 0.1);
  ReplaceOut.ar(0, dry + (wet * 0.85));
}.play(addAction: 'addToTail');

r = fork {
  loop {
    x.set(\midinote, rrand(38, 52), \gate, 1);
    exprand(0.2, 2).wait;
    x.set(\gate, 0);
    exprand(0.01, 0.03).wait;
  }
}
)


// stop sound
r.stop; x.set(\gate, 0);

// free synths
x.free; y.free;
1 Like

Except maybe PlayBuf on a Buffer containing the original sound. :wink:

3 Likes

HA! Yes, exactly :stuck_out_tongue:

I’ll give this a few days for people to see, and even if only a few people participate, I’m still going to make the shirt.

So do we have 3 people including myself? That’s enough for me if that’s it. :slight_smile:

I went back and forth with the envelope type for quite a while. At first I tried to stick with a sustaining envelope, but I kept having Synths overlap. Eventually I switched over to a retriggerable env, instantiating the Synth just once, and triggering the envelope during the sequence.

I still wasn’t quite abIe to get the combo of how the envelope modulates the cutoff, along with the monosynth env retriggering, and portamento effect. And the overall timbre doesn’t quite have that same …lush squiggle for lack of a better word, but so it goes!

// bassline [midi note, length]
(
~loop = [
	// bar 1
	[42, 1.75],
	[49, 0.25],
	[54, 0.25],
	[49, 0.25],
	[47, 0.50],
	[42, 1.00],

	// bar 2
	[45, 1.50],
	[52, 0.50],
	[38, 2.00],

	// bar 3
	[45, 1.50],
	[47, 0.25],
	[45, 0.75],
	[52, 1.50],

	// bar 4
	[47, 4.00],

	// bar 5
	[42, 1.75],
	[47, 0.25],
	[49, 0.25],
	[54, 0.25],
	[42, 1.50],

	// bar 6
	[45, 1.75],
	[52, 0.25],
	[38, 1.50],
	[52, 0.50],

	// bar 7
	[45, 1.50],
	[52, 0.50],
	[50, 1.50],
	[52, 0.50],

	// bar 8
	[37, 4.00]
]
)

// based on SH-101 architecture + delay and reverb
(
SynthDef(\roygbiv, { |freq, pw = 0.55, spw = 0.5, co = 650, res = 1, gate = 1, atk = 0.01, sus = 1, rel = 0.1, 	out = 0, portamento = 0, envScale = 1, delScale = 0.01|
	var pulse, saw, subOsc, noise, lfo, env;
	var glide, pitchCtrl, coCtrl;
	var mix, lpf, delay, reverb, sig;

	glide = VarLag.kr(0, 0.6, 0, -2, start: portamento);

	lfo = LFTri.kr(0.5);

	pitchCtrl = freq + lfo.range(-0.8, 0.8) + glide;
	pulse = Pulse.ar(pitchCtrl, pw + lfo.range(-0.07, 0.07));
	saw = LFSaw.ar(pitchCtrl);
	subOsc = Pulse.ar(pitchCtrl + Rand(-0.06, 0.06) / 2, spw);
	noise = WhiteNoise.ar;

	env = EnvGen.ar(Env.linen(atk, sus, rel, 0.5, 4), \trig.tr(1), timeScale: envScale, doneAction: 2);

	mix = Mix([pulse * 0.7, saw * 0.5, subOsc * 0.5, noise * 0.25]) * env;
	mix = Mix([mix.distort * 0.4, mix * 0.7]);

	coCtrl = (co + (env * 500));
	lpf = LPF.ar(LPF.ar((mix.softclip * 0.1) + (mix * 0.9), coCtrl), coCtrl) * 1.5;

	delay = DelayC.ar(lpf, 1, delScale);
	reverb = FreeVerb.ar(Mix([delay.softclip * 0.1, delay * 0.6, lpf * 0.8]) ! 2, 0.3, 0.45, 0.5);

	sig = (delay * 0.1) + (reverb * 0.25) + (lpf * 0.85);

	Out.ar(out, sig)
}).add
)

// play sequence
(
var bpm = 84;
var t = TempoClock(bpm / 60);
var tt = t.tempo;
var i = 0;
var prevPitch = 0;
var synth;
{
	4.do{
		synth = Synth(\roygbiv, [
			\atk, 0.01,
			\delScale, tt * 0.2,
			\freq, (~loop[i][0]).midicps,
		]);
		~loop.size.do{
			synth.set(
				\freq, (~loop[i][0]).midicps,
				\portamento, (~loop[i][0]).midicps - prevPitch.midicps,
				\envScale, ~loop[i][1],
				\sus, 3,
				\trig, 1,
			);
			if (i > 0){ synth.set(\atk, 0.2) };
			~loop[i][1].wait;
			prevPitch = ~loop[i][0];
			i = i + 1;
			if (i >= ~loop.size) { i = 0 };
		};
		synth.set(\envScale, 1, \atk, 0.01, \sus, 0.01, \rel, 0.5, \trig, 1);
	}
}.fork(t)
)
1 Like

Right on then.

I’m going to post my code by Friday, just to give anyone a few days in case there’s another person that shows up, then we can vote over the weekend.

Whomever wins can send me your t-shirt size and address and I’ll mail you the shirt!

(It’ll probably take a while as I will have to get it printed from a 3rd party, mail it to me, then mail it to you)

I love this idea - I need some practical challenges to work on - and this sounds perfect.

I also don’t expect to be winning any contests - but that’s completely fine by me.

Excellent idea. Count me in. Barely competent, but in

Great @grahamsw !!!

There are no hard deadlines, but I’m almost done with mine and going to post in a day or so, just been swamped with work. Post whenever you feel like and then we’ll all decide who’s is best!

Right, so here’s mine. I couldn’t quite get the timbre right, or the attack, but I think it’s ok. :slight_smile:

(

t = TempoClock(120/60).permanent_(true);
t.tempo_(85/60);
t.schedAbs(t.nextBar, {t.beatsPerBar_(7)});
~metro = {
	("beat :" +((t.beats.floor)%t.beatsPerBar+1)).postln;
	("bar :" +(t.bar)).postln;
		"".postln;
		1;
	};
t.schedAbs(t.nextBar, {~metro.value});


SynthDef(\rgb, {
	var osc1, osc2, sig, lfo1, lfo2, env, lpenv, filter1, filter2, noise, verb;
	var freq = \freq.kr(130, 0.2);
	var gate = \gate.kr(1);
	env = EnvGen.kr(Env.new([0, 1, 1, 0], [0.005, 0.1, 0.1], releaseNode: 2, loopNode: 1), gate, doneAction:2);
	lpenv = EnvGen.kr(Env.new([freq*8.45, freq*2+50, freq*2+50, 0], [1.2, 0.1, 0.1], releaseNode: 2, loopNode: 1), gate);
	lfo1 = LFPar.kr(0.6, (2)).range((0.16), 0.7);
	lfo2 = LFPar.kr(0.9, (-1)).range((-0.18), 0.9);
	osc1 = PulseDPW.ar(freq + lfo1) + SawDPW.ar(freq + lfo1);
	osc2 = Pulse.ar((freq + lfo2)/2, 0.8);
	noise = WhiteNoise.ar()*0.36;
	sig = Mix.new( [osc1, osc2, noise] );
	sig = RLPF.ar(sig, lpenv, 1);
	sig = RLPF.ar(sig, freq*4-100, 0.6);
	Out.ar(\out.kr(0), (sig * env));
}).add;


Pbind(
	\instrument, \rgb,
	\midinote,  Pseq([42,47,54,49,47,42,45,52, 50, 45, 47, 45, 52, 47   ],inf),
	\dur, Pseq([2,0.25,0.25,0.25,0.5,2,1.5,0.25,2,1.5,0.25,1,1,2], inf),
	\legato, 1,
	\out, 0
).play(t);
)

I’m going to listen to these and give my vote to best over the next day

Y’all do the same!

Cheers.

Anyone else, even those not making the code, are encouraged to vote!!!

@thresholdpeople Well, out of you 2 I think yours is slightly better. I didn’t know that was an SH-101. It was a tough one for me, I think @Eric_Sluyter had the noise / osc ratio and envelope a bit more correct, but yours had that slight detune that just made it for me, and the pattern, of course.

Really nice job guys.

I learned a lot from this. I’d love to hear any criticisms on mine, and y’all need to vote too!! :slight_smile:

Yeah I think @Eric_Sluyter’s is really on point sonically. I plugged in the riff and Routine from my code into Eric’s code and it sounds tight. It’s impressive, with the random notes it sounded in the same vein, but yeah, an approximation, but with the progression it, it had a lot of that same nuance that the track does. I’m not able to abstract the sound of a random note from the progression itself while working on my own projects… it’s always about the interaction of the elements in the event, so to speak that make it what it is. So for me, I had to figure out the sequencing part, or else I there was no way I was going to be able to get there.

Also mid-last week I was going through James Harkins’s Pattern guides, and learned about PmonoArtic, which seems like it’d really be the ticket here, and much more efficient than the way I’m currently sequencing.

I haven’t had a chance to check yours out yet, @poison0ak, will do so this evening.

To be honest voting for the best version doesn’t interest me much. I see it as a learning experience, and to work on things and maybe in a way that I don’t normally work. So I like the exercise, and being able to see how others go about achieving something similar is always interesting and a lot can be learned that way too.

I’d be interested in doing this with some other parts of other tracks. I did something similar years ago, remaking the intro to Kraftwerk’s Mensch Maschine in Reason. It was a great learning experience. (also one James Harkins’s Pattern guides mentioned someone’s take on Spacelab, as one of the pieces of example code in the Supercollider application folder. It’s the entire track, with patterns sequencing all the events. I haven’t dug in super deep to it but on a listen, it’s really thorough!)

Would anyone else be down for something like this semi-regularly?

Boris

@thresholdpeople I would definitely be down to continue doing this, even weekly. I’m not super into making it competitive either, I just thought it might get some people interested, and I wanted to engage in the community. Plus I felt like designing a supercollider shirt, ha. With this many people participating, we could all tie…

But yes, I think it would be awesome to continue this and would participate. Perhaps the “winner” could be the one that makes up the next thing to create? I think it would be less deliberation about what to do that way? Either way…yeah, let’s do it.

1 Like

Although I’m not participating at this moment, I’m wondering if it makes sense to prepare and submit the winning entry (or some of them) for inclusion in the SCLOrkSynths quark (GitHub - SCLOrkHub/SCLOrkSynths: Collection of SuperCollider SynthDefs (synth definitions) for use with Patterns)

1 Like

The ONLY gripe I would have about that is that it would hold us to those conventions listed.

While that could make it easier for some to learn but having those conventions across all submissions, I personally learn just as good, if not better sometimes, by having to figure out how someone did something (variables not standardizes). Also, the main point to me is to see the differences in coding styles and not hinder creativity. I guess it might be better for everyone in the end though?

I don’t know I’m torn, what do yall think? I think either way we should archive them. I am keeping a master list for submission somewhere.

@shiihs You should vote on these though to break the tie so I can make the shirt!

maybe post here: http://sccode.org/
submissions can be tagged so that they can be grouped together

Yep! That’s what I’m going to do after the voting here. Care to vote @droptableuser ?? :slight_smile:

While I totally agree with you, (and mind I only did a really quick glance at the instructions), it seems their standard for arg names is nearly the same as what I use, and most of the examples I’ve seen use too. I think the only difference is that I typically have been writing \atk for attack and they request \att.

I don’t mind either option. but sccode.org and tagging may be a more direct approach as a study aid for various implementations of a similar idea?

Or even just a thread on here for each new track. Though sccode is easier to read for longer lines.

I think a bit of distortion, and some short delay and reverb to add that extra tapey fuzz would add that missing bit!

That’s what I pretty much did with mine. I was out of steam toward the end, so I didn’t really put much effort into the reverb, but in listening over and over to BoC’s version I thought that some of the lush graininess was coming from that type of processing in their signal chain. And of course tape sound…!