Add RT query proto-examples to help files

nix
Owen Green 6 years ago
parent 806a3414d7
commit 6a9e91cb5a

@ -72,7 +72,7 @@ fork{
) )
// Make a new tree, and fit it to the dataset // Make a new tree, and fit it to the dataset
~tree = FluidKDTree(s); ~tree = FluidKDTree(s,numNeighbours:5,lookupDataSet:~ds);
//Fit it to the dataset //Fit it to the dataset
~tree.fit(~ds); ~tree.fit(~ds);
@ -83,23 +83,57 @@ fork{
//Return labels of k nearest points to a new point //Return labels of k nearest points to a new point
(
~p = [ 1.0.linrand,1.0.linrand ]; ~p = [ 1.0.linrand,1.0.linrand ];
~tree.numNeighbours = 5;
~tmpbuf = Buffer.loadCollection(s, ~p, 1, { ~tmpbuf = Buffer.loadCollection(s, ~p, 1, {
~tree.kNearest(~tmpbuf,5, { |a|~nearest = a;}) ~tree.kNearest(~tmpbuf,{ |a|a.postln;~nearest = a;})
}); });
)
// Labels of nearest points // Labels of nearest points
~nearest.postln; ~nearest.postln;
// Values // Values
(
fork{ fork{
~nearest.do{|n| ~nearest.do{|n|
~ds.getPoint(n, ~tmpbuf, {~tmpbuf.getn(0, 2, {|x|x.postln})}); ~ds.getPoint(n, ~tmpbuf, {~tmpbuf.getn(0, 2, {|x|x.postln})});
s.sync; s.sync;
} }
} }
)
//Distances of the nearest points //Distances of the nearest points
~tree.kNearestDist(~tmpbuf, 5, { |a| a.postln }); ~tree.kNearestDist(~tmpbuf, { |a| a.postln });
::
subsection:: Server Side Queries
code::
(
~inputPoint = Buffer.alloc(s,2);
~predictPoint = Buffer.alloc(s,10);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)
(
~tree.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint);
{
var trig = Impulse.kr(ControlRate.ir/2);
var point = 2.collect{TRand.kr(0,1,trig)};
point.collect{|p,i| BufWr.kr([p],~inputPoint,i)};
Poll.kr(trig,point);
Out.kr(~pitchingBus.index,[trig]);
Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,Array.iota(5)));
Silent.ar;
}.play(~tree.synth,addAction:\addBefore);
)
:: ::

@ -95,7 +95,7 @@ fork{
// Fit into 4 clusters // Fit into 4 clusters
( (
~kmeans.fitPredict(~dataset,~clusters, 4, action: {|c| ~kmeans.fitPredict(~dataset,~clusters,action: {|c|
"Fitted.\n # Points in each cluster:".postln; "Fitted.\n # Points in each cluster:".postln;
c.do{|x,i| c.do{|x,i|
("Cluster" + i + "->" + x.asInteger + "points").postln; ("Cluster" + i + "->" + x.asInteger + "points").postln;
@ -145,23 +145,23 @@ w.refresh;
w.front; w.front;
) )
//Querying on the server using busses and buffers: ::
//This is the equivalent of predictPoint, but wholly on the server
//FluidKMeans is accessed via its own synth, so we need to use subsection:: Server Side Queries
//a bus to communicate with it. The inBus receives a trigger to query, using data
//from inBuffer; a trigger is then send to outBus with the prediction in outBuffer This is the equivalent of predictPoint, but wholly on the server
FluidKMeans is accessed via its own synth, so we need to use
a bus to communicate with it. The inBus receives a trigger to query, using data
from inBuffer; a trigger is then send to outBus with the prediction in outBuffer
code::
( (
~ib = Bus.audio(s); // input bus must be audio (for now) ~ib = Bus.control(s); // input bus must be audio (for now)
~ob = Bus.control(s); //output bus can be kr ~ob = Bus.control(s); //output bus can be kr
~tempPoint = Buffer.alloc(s,1,2); ~tempPoint = Buffer.alloc(s,1,2);
~inpPoint = Buffer.alloc(s,2); ~inpPoint = Buffer.alloc(s,2);
~outPoint = Buffer.alloc(s,1); ~outPoint = Buffer.alloc(s,1);
) )
~tempPoint.getn(0,2,{|x|x.post})
~inpPoint.getn(0,2,{|x|x.post})
//We make two Synths. One, before FluidKMeans, generates a random point and sends //We make two Synths. One, before FluidKMeans, generates a random point and sends
//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus //a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus
( (
@ -169,13 +169,12 @@ w.front;
~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); ~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching //pitching
{ {
var trig = Impulse.kr(1); var trig = Impulse.kr(100);
var point = WhiteNoise.kr(1.dup); var point = WhiteNoise.kr(1.dup);
var copied; var copied;
BufWr.kr(point,~tempPoint,0);
Poll.kr(trig, point, [\pointX,\pointY]); Poll.kr(trig, point, [\pointX,\pointY]);
copied = Done.kr(FluidBufFlatten.kr(~tempPoint, ~inpPoint, trig: trig)); point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)};
Out.ar(~ib.index,K2A.ar(copied > Delay1.kr(copied))); Out.kr(~ib.index,[trig]);
}.play(~kmeans.synth,addAction:\addBefore); }.play(~kmeans.synth,addAction:\addBefore);
//catching //catching
{ {
@ -183,20 +182,17 @@ w.front;
}.play(~kmeans.synth,addAction:\addAfter); }.play(~kmeans.synth,addAction:\addAfter);
) )
// to sonify the output, here are random values alternating quadrant. // to sonify the output, here are random values alternating quadrant, generated more quickly as the cursor moves rightwards
( (
//Set properties on FluidKMeans: //Set properties on FluidKMeans:
~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); ~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching //pitching
{ {
var count, trig, point, copied; var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir / 2));
trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,1000).poll); var point = 2.collect{ TIRand.kr(0,3,trig).linlin(0,3,-1,1) };
count = Stepper.kr(trig,0,0,3); point.collect{|p,i| BufWr.kr([p],~inpPoint,i)};
point = Latch.kr(WhiteNoise.ar([0.1,0.1],[count.div(2)-0.5,count.mod(2)-0.5]),trig); Out.kr(~ib.index,[trig]);
BufWr.kr(point,~tempPoint,0); T2A.ar(trig)*0.1;
copied = Done.kr(FluidBufFlatten.kr(~tempPoint, ~inpPoint, trig: trig));
Out.ar(~ib.index,K2A.ar(copied > Delay1.kr(copied)));
trig*0.1;
}.play(~kmeans.synth,addAction:\addBefore); }.play(~kmeans.synth,addAction:\addBefore);
//catching //catching
{ {

@ -145,5 +145,58 @@ w.drawFunc = {
w.refresh; w.refresh;
w.front; w.front;
) )
::
subsection::Server Side Queries
This is the equivalent of predictPoint, but wholly on the server
FluidKMeans is accessed via its own synth, so we need to use
a bus to communicate with it. The inBus receives a trigger to query, using data
from inBuffer; a trigger is then send to outBus with the prediction in outBuffer
code::
(
~ib = Bus.control(s); // input bus must be audio (for now)
~ob = Bus.control(s); //output bus can be kr
~inpPoint = Buffer.alloc(s,2);
~outPoint = Buffer.alloc(s,1);
)
//We make two Synths. One, before FluidKMeans, generates a random point and sends
//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus
(
//Set properties on FluidKMeans:
~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching
{
var trig = Impulse.kr(100);
var point = WhiteNoise.kr(1.dup);
Poll.kr(trig, point, [\pointX,\pointY]);
point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)};
Out.kr(~ib.index,[trig]);
}.play(~classifier.synth,addAction:\addBefore);
//catching
{
Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster);
}.play(~classifier.synth,addAction:\addAfter);
)
// to sonify the output, here are random values alternating quadrant.
(
//Set properties on FluidKMeans:
~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint);
//pitching
{
var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll);
var point = 2.collect{TIRand.kr(0,3,trig).linlin(0,3,-1,1)};
point.collect{|p,i| BufWr.kr([p],~inpPoint,i)};
Out.kr(~ib.index,[trig]);
T2A.ar(trig)*0.1;
}.play(~classifier.synth,addAction:\addBefore);
//catching
{
SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1);
}.play(~classifier.synth,addAction:\addAfter);
)
:: ::

@ -89,15 +89,13 @@ d = Dictionary.with(
~testdata.collect{|x, i| [i.asString, [x]]}.flatten)]) ~testdata.collect{|x, i| [i.asString, [x]]}.flatten)])
); );
~targetdata.plot
~source.print; ~source.print;
~target.print; ~target.print;
~test.print; ~test.print;
) )
// Now make a regressor and fit it to the source and target, and predict against test // Now make a regressor and fit it to the source and target, and predict against test
//grab the output data whilst we're at it, so we can inspect //grab the output data whilst we're at it, so we can inspect
( (
@ -110,8 +108,45 @@ d = Dictionary.with(
}); });
) )
//We should see a single cycle of a chirp //We should see a single cycle of a chirp
~outputdata.plot; ~outputdata.plot;
::
subsection:: Server Side Queries
code::
//Setup
(
~inputPoint = Buffer.alloc(s,1);
~predictPoint = Buffer.alloc(s,2);
~avgBuf = Buffer.alloc(s,10,2);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)
(
~regressor.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint);
~inputSynth = {
var input = Saw.kr(2).linlin(-1,1,0,1);
var trig = Impulse.kr(ControlRate.ir/10);
BufWr.kr(input,~inputPoint,0);
Out.kr(~pitchingBus.index,[trig]);
};
~inputSynth.play(~regressor.synth,addAction:\addBefore);
~outputSynth = {
Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,0),"mapped value")
};
~outputSynth.play(~regressor.synth,addAction:\addAfter);
~outputSynth.scope
)
:: ::

@ -64,6 +64,7 @@ EXAMPLES::
code:: code::
s.boot; s.boot;
//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidNormalize //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidNormalize
// FluidNormalize.dumpAllMethods
( (
~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav";
~raw = FluidDataSet(s,\norm_help_raw); ~raw = FluidDataSet(s,\norm_help_raw);
@ -123,4 +124,40 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature);
~plot2 = ~normedarray.flatten(1).unlace.plot("Normalized",Rect(410,0,400,400)).plotMode=\bars; ~plot2 = ~normedarray.flatten(1).unlace.plot("Normalized",Rect(410,0,400,400)).plotMode=\bars;
) )
//Server side queries
//Setup
(
~tempPoint = Buffer.alloc(s,2);
~predictPoint = Buffer.alloc(s,2);
~avgBuf = Buffer.alloc(s,10,2);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)
(
~normalizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint);
//Pitching (no pun intended): read frames out of buffer and pass to standardize
{
var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio)));
var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:9);
var trig = HPZ1.ar(counter) < 0;
//average 10 frames: one could use the MovingAverage extension here
var avg;
BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter);
avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:10.collect{|x|x})) * 0.1;
//assemble data point
BufWr.kr(avg[0],~tempPoint,0);
BufWr.kr(avg[1],~tempPoint,1);
Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]);
Out.kr(~pitchingBus.index,[T2K.kr(trig)]);
}.play(~normalizer.synth,addAction:\addBefore);
{
Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"])
}.play(~normalizer.synth,addAction:\addAfter);
)
:: ::

@ -121,7 +121,6 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature);
) )
//Visualise the 2D projection of our original 12D data //Visualise the 2D projection of our original 12D data
( (
d = ~reducedarray.flatten(1).unlace.deepCollect(1, { |x| x.normalize}); d = ~reducedarray.flatten(1).unlace.deepCollect(1, { |x| x.normalize});
@ -141,3 +140,36 @@ w.refresh;
w.front; w.front;
) )
:: ::
subsection:: Server Side Queries
Let's map our learned PCA dimensions to the controls of a processor
code::
(
~inputPoint = Buffer.alloc(s,12);
~predictPoint = Buffer.alloc(s,2);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)
(
~pca.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint);
{
var mapped;
var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio)));
var mfcc = FluidMFCC.kr(audio)[1..12];
var smoothed = LagUD.kr(mfcc,1*ControlDur.ir,500*ControlDur.ir);
var trig = Impulse.kr(ControlRate.ir / 2);
smoothed.collect{|coeff,i| BufWr.kr([coeff],~inputPoint,i)};
Out.kr(~pitchingBus,[trig]);
mapped = Latch.kr(BufRd.kr(2,~predictPoint).linlin(-3,3,0,3),In.kr(~catchingBus));
CombC.ar(audio,3,mapped[0],mapped[1]*3)
}.play(~pca.synth,addAction:\addBefore);
)
::

@ -50,7 +50,7 @@ ARGUMENT:: destBuffer
A link::Classes/Buffer:: to contain the standardize value A link::Classes/Buffer:: to contain the standardize value
ARGUMENT:: action ARGUMENT:: action
A function to run when processing is complete A function to run when processing is complete
[].unlace
EXAMPLES:: EXAMPLES::
code:: code::
@ -113,6 +113,44 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature);
~rawarray.flatten(1).unlace.plot("Unstandardized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; ~rawarray.flatten(1).unlace.plot("Unstandardized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars;
~plot2 = ~stdarray.flatten(1).unlace.plot("Standardized",Rect(410,0,400,400)).plotMode=\bars; ~plot2 = ~stdarray.flatten(1).unlace.plot("Standardized",Rect(410,0,400,400)).plotMode=\bars;
) )
::
subsection::Server Side Querying
Because FluidStandardize runs in its own link::Classes/Synth:: on the server, communication is done via control-rate link::Classes/Bus:: objects for triggering and link::Classes/Buffer:: objects for passing and retreiving data.
code::
//Setup
(
~tempPoint = Buffer.alloc(s,2);
~predictPoint = Buffer.alloc(s,2);
~avgBuf = Buffer.alloc(s,10,2);
~pitchingBus = Bus.control;
~catchingBus = Bus.control;
)
(
~standardizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint);
//Pitching (no pun intended): read frames out of buffer and pass to standardize
{
var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio)));
var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:9);
var trig = HPZ1.ar(counter) < 0;
//average 10 frames: one could use the MovingAverage extension here
var avg;
BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter);
avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:10.collect{|x|x})) * 0.1;
//assemble data point
BufWr.kr(avg[0],~tempPoint,0);
BufWr.kr(avg[1],~tempPoint,1);
Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]);
Out.kr(~pitchingBus.index,[T2K.kr(trig)]);
}.play(~standardizer.synth,addAction:\addBefore);
//catching
{
Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"])
}.play(~standardizer.synth,addAction:\addAfter);
)
:: ::

Loading…
Cancel
Save