Freeing a Phasor when it reaches its end + how to playback sample indexed slices with Patterns?

Hey I have a solution for this that I have used for while with flucoma, it’s kind of complex but allows you to sort the slices by any of the flucoma measures.

(
~clearAnalysisDict = {|dict|
	dict.keysValuesDo{ |key, value|
		if(value.isKindOf(Buffer)){
			value.free;
		};
		dict.removeAt(key);
	};
	dict = ();
};

~getSlices = {|file, dict, threshold|
	dict.put(\filepath, file);
	dict.put(\file, Buffer.readChannel(s, file, channels: [0]));
	dict.put(\indices, Buffer(s));
	//nrt onset slice of source
	FluidBufOnsetSlice.processBlocking(s, dict.at(\file), metric: 9, threshold: threshold, indices: dict.at(\indices), action: {"found slices".postln});
};

~sortSlices = {|measure=\centroid, dict|
	//get
	var indices = dict.at(\indices);
	var file = dict.at(\file);
	var spec, stats, meanfeatures;
	//get and set new analysis buffers
	dict.put(\spec, Buffer(s));
	dict.put(\stats, Buffer(s));
	dict.put(\meanfeatures, Buffer(s));
	//vars
	spec = dict.at(\spec);
	stats = dict.at(\stats);
	meanfeatures = dict.at(\meanfeatures);
	//analysis
	indices.loadToFloatArray(action: {
		arg fa;
		//iterate through adjacent pairs of indices (tuple like)
		fa.doAdjacentPairs{
			arg start, end, i;
			var numSamps = end - start;
			// i.postln;
			//compute spectral features per fft frame (w selected feature)
			FluidBufSpectralShape.processBlocking(s, file, start, numSamps, features: spec, select:[measure]);
			//buf stats channels: mean std skew kurtosis min median max
			FluidBufStats.processBlocking(s, spec, stats: stats, select:[\mean]);
			FluidBufCompose.processBlocking(s, stats, destination: meanfeatures, destStartFrame: i);
		};
		//get indices
		dict.put(\onsetArr, fa);
		dict.put(\size, fa.size);
		//get INDICES of sorted features
		meanfeatures.loadToFloatArray(action: { arg fa; dict.put(\sortedIndices, fa.order) });
		"done analysis".postln;
	});

};

~analyzeSlices = {|file, dict, thresh, metric|
	~clearAnalysisDict.(dict);
	~getSlices.(file, dict, thresh);
	~sortSlices.(metric, dict);
};


~getSlice = {|slice, dict|
	var sortedIndices = dict.at(\sortedIndices);
	var onsets = dict.at(\onsetArr);
	var startIdx = sortedIndices.wrapAt(slice);
	var endIdx = startIdx + 1;
	var startOnset = onsets.at(startIdx);
	var endOnset = onsets.at(endIdx);
	[startOnset, endOnset];
};

~pGetSlice = {|generator, dict|
	Pcollect ({ |i| ~getSlice.(i, dict).asRef; }, generator);
};

SynthDef(\segPlayer, {|buf, slice = #[0, 1], oneshot=1|
	var bufFrames, sampsDur, offset, startsamp, endsamp, numChans, sig, pan, panned, env, posRate, line, phasor, phase;
    
	bufFrames = BufFrames.ir(buf);
	numChans = buf.numChannels;

	offset = \offset.kr(0);
	startsamp = slice[0] + offset;
	endsamp = slice[1] + offset;
	sampsDur = endsamp - startsamp;

	posRate = \posRate.kr(1);

	//select looped or oneshot NOTE: doneaction controlled by release
	line= Line.ar(
			start: startsamp,
			end: endsamp,
			dur: (sampsDur / s.sampleRate) * BufRateScale.kr(buf) * posRate.reciprocal
	);

    phasor = Phasor.ar(
		rate: (1 / bufFrames * BufRateScale.kr(buf)) * posRate,
		start: startsamp,
		end: endsamp,
		resetPos: startsamp
	);
    
	phase = Select.ar(oneshot, [phasor, line]);
	
    sig = BufRd.ar(numChans, buf, line, interpolation: 2);

	//handle channels
	pan = \pan.kr(0);
	panned = case
	{numChans == 1} {Pan2.ar(sig, pan)}
	{numChans == 2} {Balance2.ar(sig[0], sig[1], pan)}
	{numChans > 2} { var splay = Splay.ar(sig); Balance2.ar(splay[0], splay[1], pan); } ;
	
	//envelope
	env = EnvGen.ar(Env.perc(\atk.kr(0.01), \rel.kr(0.5), curve: \curve.kr(-4)), gate: \gate.kr(1), doneAction: 2);
	
	//out
	sig = panned * env * \gain.kr(0).dbamp;
	sig = sig * \amp.kr(1);
	Out.ar(\out.kr(0), sig);
}).add;
)

Where the usage is something like:

(
~sliceBuf = Dictionary();
// eg centroid, spread, skewness, kurtosis, rolloff, flatness, crest
~analyzeSlices.(file: Platform.resourceDir +/+ "sounds/a11wlk01.wav", dict: ~sliceBuf, thresh: 0.1, metric: \centroid);
)

(
	Pdef(\seg_pattern,
		Pbind(
			\instrument, \segPlayer,
			\amp, 1,
			\buf, ~sliceBuf.at(\file),
			//lowest to highest
			\slice, ~pGetSlice.(Pseries(0, 1, inf).wrap(0, 32), ~sliceBuf),
			\dur, 0.25,
			\oneshot, 0
		)
	).play(t);
)
2 Likes