Hi @tedmoore,
thanks for the answer. I reckon I know what you mean. I have copied the whole code, which is basically part of the teaching material you have make. The last part is the OSC part with the idiomatic change. Also, I have added a OSC message sender so there is no need to have touchOSC at the same time that keeps the OSCdef that receives the message.
I was wondering about snapping the red circle to the coordinates of the nearest point given by FluidKDTree. In other words, that the red point snaps on top of the point that is sounding. Similar how FluidPlotter works. However, I need to figure out how to do it.
Open to any suggestion. thx
~folder = FluidFilesPath();
~loader = FluidLoadFolder(~folder).play(s,{"done loading folder".postln;});
(
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
(
~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;
});
)
// 5. ANALYSIS
(
~analyses = FluidDataSet(s);
~indices.loadToFloatArray( action: {
arg fa;
var mfccs = Buffer(s);
var stats = Buffer(s);
var flat = Buffer(s);
fa.doAdjacentPairs{
arg start, end, i;
var num = end - start;
FluidBufMFCC.processBlocking(
s,
~src,
start,
num,
features: mfccs, //Buffer de salida
numCoeffs: 13,
startCoeff:1
);
FluidBufStats.processBlocking(
s,
mfccs,
stats: stats,
select: [\mean]
);
FluidBufFlatten.processBlocking(
s,
stats,
destination: flat //el Buffer de salida
);
~analyses.addPoint(i,flat);
"analyzing slice % / %".format(i+1,fa.size-1).postln;
};
s.sync;
~analyses.print;
});
)
(
~umapped = FluidDataSet(s);
FluidUMAP(
s,
numNeighbours: 15,
minDist: 0.9
).fitTransform(~analyses, ~umapped, action:{"umap done".postln});
)
(
~normed = FluidDataSet(s);
FluidNormalize(s).fitTransform(~umapped,~normed);
)
~tree = FluidKDTree(s).fit(~normed);
// 8. PLAYER
(
~play_slice = {
arg index;
{
var startsamp = Index.kr(~indices, index);
var stopsamp = Index.kr(~indices, index + 1);
var phs = Phasor.ar(
0,
BufRateScale.ir(~src),
startsamp,
stopsamp);
var sig = BufRd.ar(1,~src,phs);
var dursecs = (stopsamp - startsamp) / BufSampleRate.ir(~src);
var env;
dursecs = min(dursecs,1);
env = EnvGen.kr(
Env([0,1,1,0], [0.03,dursecs-0.06,0.03]),
doneAction:2
);
sig.dup * env;
}.play;
};
)
// DRAWING, PLAYER, RED POINT. OSC CONTROLLED
(
var circle, w, uv, fp;
var xpos = 0;
var ypos = 0;
var point = Buffer.alloc(s, 2);
var previous = nil;
circle = {
|x, y, r|
Pen.fillColor = Color.new255(255, 0, 0, 80);
Pen.strokeColor = Color.red;
Pen.addArc(x@y + ((r@r)), r, 0, 2pi);
Pen.fillStroke;
};
w = Window("Monitor", bounds: Rect(0, 0, 800, 800)).front;
w.view.background_(Color.hsv(1, 1, 1, alpha: 0));
~normed.dump({
arg dict;
dict.postln;
defer{
fp = FluidPlotter(
w,
dict: dict,
xmin: 0,
xmax: 1,
ymin: 0,
ymax: 1,
bounds: Rect(0, 0, w.bounds.width, w.bounds.height));
uv = UserView(w, Rect(0, 0, w.bounds.width, w.bounds.height))
.drawFunc = { |uvview|
circle.value(
xpos * w.bounds.width,
ypos * w.bounds.height,
10
);
};
}
});
OSCdef(\xy, {
arg msg;
msg.postln;
xpos = msg[1];
ypos = msg[2];
fork{
point.setn(0, [xpos, ypos]);
~tree.kNearest(point, 1, {
arg nearest;
if(nearest != previous){
nearest.postln;
~play_slice.(nearest.asInteger);
previous = nearest;
};
};)
};
defer{uv.refresh};
}, '/3/xy');
)
// Test with random point to not use OSCtouch
(
m = NetAddr.new("localhost", 57120);
~rndmesg = Routine{loop {
m.sendMsg("/3/xy", 1.0.rand, 1.0.rand);
1.wait;
}};
~rndmesg.play;
)
~rndmesg.stop;