diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6c76610..1612b4c 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -92,6 +92,14 @@ struct IsModel>> using type = typename ClientWrapper::isModelObject; }; + +template +struct IsModel> +{ + using type = typename ClientWrapper::isModelObject; +}; + + template using IsModel_t = typename IsModel::type; diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 5ebd7cb..dca6794 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -1,16 +1,7 @@ -FluidKMeans : FluidManipulationClient { +FluidKMeans : FluidDataClient { var <>k; - *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } - - init {|uid| - id = uid; - } - fit{|dataset,k, maxIter = 100, action| this.k = k; this.prSendMsg(\fit, diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 541a87e..fd0315d 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -9,6 +9,7 @@ FluidProxyUgen : UGen { init { |pluginname...args| this.pluginname = pluginname; inputs = args++Done.none++0; + rate = inputs.rate; } name{ @@ -50,17 +51,23 @@ FluidManipulationClient { ^super.newCopyArgs(server ?? {Server.default}).baseinit(*args) } + makeDef { |defName...args| + ^SynthDef(defName,{ + var ugen = FluidProxyUgen.kr(this.class.name, *args); + this.ugen = ugen; + ugen + }); + } + + updateSynthControls {} + baseinit { |...args| var makeFirstSynth,synthMsg; id = UniqueID.next; postit = {|x| x.postln;}; keepAlive = true; defName = (this.class.name.asString ++ id).asSymbol; - def = SynthDef(defName,{ - var ugen = FluidProxyUgen.kr(this.class.name, *args); - this.ugen = ugen; - ugen - }); + def = this.makeDef(defName,*args); synth = Synth.basicNew(def.name, server); synthMsg = synth.newMsg(RootNode(server)); def.doSend(server,synthMsg); @@ -70,10 +77,12 @@ FluidManipulationClient { if(keepAlive){ synth = Synth(defName,target: RootNode(server)); synth.onFree{clock.sched(0,onSynthFree)}; + this.updateSynthControls; } }; CmdPeriod.add({synth = nil}); synth.onFree{clock.sched(0,onSynthFree)}; + this.updateSynthControls; } free{ @@ -118,6 +127,71 @@ FluidManipulationClient { } } +FluidDataClient : FluidManipulationClient { + + var " + x.asInteger + "points").postln; } }); - +) // Cols of kmeans should match dataset, size is the number of clusters ~kmeans.cols; @@ -121,6 +122,9 @@ fork{ } ) +//or faster by sorting the IDs +~clusters.dump{|x|~assignments = x.at("data").atAll(x.at("data").keys.asArray.sort{|a,b|a.asInteger < b.asInteger}).flatten.postln;} + //Visualise: we're hoping to see colours neatly mapped to quandrants... ( d = ((~points + 1) * 0.5).flatten(1).unlace; @@ -141,4 +145,57 @@ w.refresh; 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 +//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 +( +~ib = Bus.audio(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: +~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); +//pitching +{ + var trig = Impulse.ar(10); + var point = [WhiteNoise.kr,WhiteNoise.kr]; + BufWr.kr(point[0],~inpPoint,0); + BufWr.kr(point[1],~inpPoint,1); + Poll.kr(T2K.kr(trig),point,[\pointX,\pointY]); + Out.ar(~ib.index,[trig]); +}.play(~kmeans.synth,addAction:\addBefore); +//catching +{ + Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); +}.play(~kmeans.synth,addAction:\addAfter); +) + +// to sonify the output, here are random values alternating quadrant. +( +//Set properties on FluidKMeans: +~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); +//pitching +{ + var count,trig,point; + trig = Impulse.ar(MouseX.kr(0,1).exprange(0.5,1000)); + count = Stepper.ar(trig,0,0,3); + point = Latch.ar(WhiteNoise.ar([0.1,0.1],[count.div(2)-0.5,count.mod(2)-0.5]),trig); + BufWr.kr(point[0],~inpPoint,0);//annoying but triggered bufcompose or some other sort of entry here. (imagine having 20 mfccs here) + BufWr.kr(point[1],~inpPoint,1); + // Poll.kr(T2K.kr(trig),point,[\pointX,\pointY]); + Out.ar(~ib.index,[trig]); + trig*0.1; +}.play(~kmeans.synth,addAction:\addBefore); +//catching +{ + SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); +}.play(~kmeans.synth,addAction:\addAfter); +) :: diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index a0c0738..02db480 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -27,7 +27,7 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidDataSetQuery",ft); makeSCWrapper("FluidLabelSet",ft); makeSCWrapper("FluidKDTree",ft); - makeSCWrapper("FluidKMeans",ft); + makeSCWrapper("FluidKMeans",ft); makeSCWrapper("FluidKNNClassifier",ft); makeSCWrapper("FluidKNNRegressor",ft); makeSCWrapper("FluidNormalize",ft);