SC: 2D Plot Example Modification

In my local SuperCollider user’s group, one of our members requested this so I’m sharing with the community.

It’s derived from @tedmoore’s tutorial but traversing the KdTree using a synth instead of using a visual plot.

I’ve been hacking it adding filters and fx. If anyone wants to try a modification to the synth it could be a fun thing to try.

// the folder containing the corpus
~folder = FluidFilesPath();

// load into a buffer
~loader = FluidLoadFolder(~folder).play(s,{"done loading folder".postln;});

// sum to mono (if not mono)
(
if(~loader.buffer.numChannels > 1){
	~src = Buffer(s);
	~loader.buffer.numChannels.do{
		arg chan_i;
		FluidBufCompose.processBlocking(s,
			~loader.buffer,
			startChan:chan_i,
			numChans:1,
			gain:~loader.buffer.numChannels.reciprocal,
			destination:~src,
			destGain:1,
			action:{"copied channel: %".format(chan_i).postln}
		);
	};
}{
	"loader buffer is already mono".postln;
	~src = ~loader.buffer;
};
)

// slice the buffer in non real-time
(
~indices = Buffer(s);
FluidBufOnsetSlice.processBlocking(s,~src,metric:9,threshold:0.05,indices:~indices,action:{
	"found % slice points".format(~indices.numFrames).postln;
	"average duration per slice: %".format(~src.duration / (~indices.numFrames+1)).postln;
});
)

// analysis
(
~analyses = FluidDataSet(s);
~indices.loadToFloatArray(action:{
	arg fa;
	var mfccs = Buffer(s);
	var stats = Buffer(s);
	var flat = Buffer(s);
	var playback_info_dict = Dictionary.newFrom([
		"cols",2,
		"data",Dictionary.new;
	]);

	fa.doAdjacentPairs{
		arg start, end, i;
		var num = end - start;

		// add playback info for this slice to this dict
		playback_info_dict["data"][i] = [start,num];

		FluidBufMFCC.processBlocking(s,~src,start,num,features:mfccs,numCoeffs:13,startCoeff:1);
		FluidBufStats.processBlocking(s,mfccs,stats:stats,select:[\mean]);
		FluidBufFlatten.processBlocking(s,stats,destination:flat);

		~analyses.addPoint(i,flat);
		"analyzing slice % / %".format(i+1,fa.size-1).postln;

		if((i%100) == 99){s.sync;}
	};

	s.sync;
	~ds_playback = FluidDataSet(s).load(playback_info_dict);
	~analyses.print;
});
)

(
~umapped = FluidDataSet(s);
FluidUMAP(s,numNeighbours:15,minDist:0.9).fitTransform(~analyses,~umapped,action:{"umap done".postln});
)

// normalize
(
~normed = FluidDataSet(s);
FluidNormalize(s).fitTransform(~umapped,~normed);
)

// fit a kdtree
~tree = FluidKDTree(s).fit(~normed);

~point = Buffer.alloc(s,2);
~playbufinfo = Buffer.alloc(s,2);

(
a = {
	var start, num;
	var env, phs, sig, dursecs, trig;
	var snd;
	FluidKrToBuf.kr(LFSaw.kr([0.009,0.7],[1.0.rand,1.0.rand]).range(0,1), ~point);
	trig = \t_trig.kr(0) + Impulse.kr(LFNoise0.ar(3).range(0.1,3.1));
	~tree.kr(trig, ~point, ~playbufinfo, 1, ~ds_playback);
	# start, num = FluidBufToKr.kr(~playbufinfo);
	dursecs = num / BufSampleRate.ir(~src);
	phs = Phasor.ar(0,BufRateScale.ir(~src),start,start+num);
	env = EnvGen.ar(Env.asr(0.03, releaseTime: dursecs-0.03), trig);
	sig = BufRd.ar(1,~src, phs) * env;
	snd = GrainIn.ar(2, Dust.ar(100 * LFNoise0.ar(80).range(0.7,2.1)), 2, Mix.ar(sig), LFNoise0.ar(100).range(-1,1), -1, 512, 1);
	snd = RLPF.ar(snd, LFTri.kr(1/32).exprange(800,15000), 0.8);
	snd = Limiter.ar(snd, 0.8);
	snd = LeakDC.ar(snd);
	snd = snd * Lag3.kr(\amp.kr(0.7), 1);
	snd = JPverb.ar(snd, 5, 0, 10) * 0.3 + snd;
	snd * EnvGen.kr(Env.asr(attackTime: \attack.kr(0.7), sustainLevel: 1.0, releaseTime: \rel.kr(3)), gate: \gate.kr(1), doneAction: Done.none);
};
);
2 Likes