SuperCollider: Control rate versions of slicers (FluidBufAmpSlice.kr)

Playing around circle buffers following a conversation with @rodrigo.constanzo. The goal is to detect and analyze events at different timelscales and derive metrics (onset density, aproximate tempo, trend, and variation) from them.

In the code below, I expect the FluidBufAmpSlice.kr to perform an analysis and populate the ~indices Buffer when I give it a \t_trig message. What actually happens is that the ~indices remains empty. Additionally, the server will occasionally crash and exit when I examine the empty ~indices. I have tried variations with PlayBuf and different Buf Slicer objects.

Am I misunderstanding how the .kr version of FluidBufAmpSlice is supposed to work?

~record = Buffer.alloc(s,s.sampleRate * 1);
~indices = Buffer(s);

(
a = { |t_trig,buf,indices|
	var input = SoundIn.ar(0);
	var sig = RecordBuf.ar(input, buf, loop: 1);
	FluidBufAmpSlice.kr(source: buf, indices: indices, fastRampUp: 3, fastRampDown: 383, slowRampUp: 2205, slowRampDown: 2205, onThreshold: 16, offThreshold: 4.66665, floor: -55, minSliceLength: 1323, highPassFreq: 2000, trig: t_trig);
}.play(args: [\buf, ~record, \indices, ~indices]);
);

~record.plot;
a.set(\t_trig, 1);
~indices;
~indices.getn(1, {|msg| msg.postln});
a.free;
1 Like
( // Allocate Buffers
~record = Buffer.alloc(s,s.sampleRate * 1);
~indices = Dictionary.new;
~indices[\AmpSlice] = Dictionary.new;
~indices[\AmpSlice][\Indices] = Buffer(s);
~indices[\AmpSlice][\Stats] = Buffer(s);
~indices[\OnsetSlice] = Dictionary.new;
~indices[\OnsetSlice][\Indices] = Dictionary.new;
~indices[\OnsetSlice][\Stats] = Buffer(s);
~stats = Buffer(s);
~deltas = { |myArray|
	var deltas = Array.newClear(myArray.size - 1);
	myArray.doAdjacentPairs { arg a, b, index;
		deltas[index] = b - a;
	};
	deltas;
};
);

( // Synth
a = { |source,indices,stats,t_trig|
	var input = SoundIn.ar(0);
	var sig = RecordBuf.ar(input, source, loop: 1);
	var phasor = Phasor.ar(0, BufRateScale.kr(source), 0, BufFrames.kr(source)).poll(Trig1.ar(t_trig,SampleDur.ir));
	var gate = (phasor < 1);
	BufWr.ar(input,source,phasor);
	FluidBufToKr.kr(stats,numFrames:7);
	SendReply.ar((phasor < 1), '/analyzeBuffer', [source, indices, stats]);
}.play(args: [
	\source, ~record,
	\indices, ~indices[\AmpSlice][\Indices],
	\stats, ~indices[\AmpSlice][\Stats]
]);
);

( // Callback
OSCFunc({|msg|
	var source  = s.cachedBufferAt(msg[3].asInteger);
	var indices = s.cachedBufferAt(msg[4].asInteger);
	var stats   = s.cachedBufferAt(msg[5].asInteger);
	msg.postln;
	FluidBufOnsetSlice.process(s,source,indices:indices,action:{
		arg features;
		FluidBufStats.process(s, features, stats: stats, action: { |msg| msg.postln });
		// [ 22528.0, 8704.0, 0.0, 1.0, 13824.0, 13824.0, 31232.0 ]
	});
},'/analyzeBuffer');
);

Is there a smart/idiomatic way to get the deltas into a buffer?

staying on the server, that is not super smooth but a FluidBufCompose ninja could do it.

Imagine you have a buffer that has 4 indices:

10 20 30 40

you could compose @numframes (length - 1) to itself offsetted. Now this is power ninja use :slight_smile:

//fake source buffer
b = Buffer.loadCollection(s,(1..4) * 10)

//destivation for deltas
c = Buffer(s)

c.zero

// copy the left part of the subtractions (20,30,40)
FluidBufCompose.process(s,b,startFrame: 1,numFrames: b.numFrames - 1, gain: 1,destination: c, destGain: 1)

// adds a negative copy of an offset version of the array (-10,-20,-30)
FluidBufCompose.process(s,b,startFrame: 0,numFrames: b.numFrames - 1, gain: -1,destination: c, destGain: 1)

// voilà
c.getn(0,c.numFrames,{|x|x.postln})

Now you could run that as kr as bufcompose has that too.