From 524d95aec0eb4b59cb762d7b86f08a7472885e7d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 3 Sep 2018 16:55:45 +0100 Subject: [PATCH] fdNMF.cpp / .sc + fdNMF/tests.scd: Refactor for new client / parmater archiecture (see fluid_decomposition commit ec1fe1332c522a64b04e5210d1e48266f6b6512b) --- fdNMF/fdNMF.cpp | 267 +++++++++------------- fdNMF/tests.scd | 49 +++- release-packaging/fdNMF/classes/fdfNMF.sc | 35 ++- 3 files changed, 178 insertions(+), 173 deletions(-) diff --git a/fdNMF/fdNMF.cpp b/fdNMF/fdNMF.cpp index 777e0cc..9092952 100644 --- a/fdNMF/fdNMF.cpp +++ b/fdNMF/fdNMF.cpp @@ -1,174 +1,123 @@ -// FD_BufNMF, an NRT buffer NMF Processor + // FD_BufNMF, an NRT buffer NMF Processor // A tool from the FluCoMa project, funded by the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899) -#include "SC_PlugIn.h" +#include "fdNRTBase.hpp" + #include "algorithms/STFT.hpp" #include "data/FluidTensor.hpp" #include "clients/nrt/NMFClient.hpp" +#include "clients/common/FluidParams.hpp" +#include "util/TupleUtils.hpp" + +#include "SC_PlugIn.h" + +#include +#include + +static InterfaceTable *ft; //Using statements for fluidtensor using fluid::FluidTensor; using fluid::FluidTensorView; using fluid::nmf::NMFClient; -static InterfaceTable *ft; - -void BufNMF(World *world, struct SndBuf *srcBuf, struct sc_msg_iter *msg) -{ - size_t srcFrameCount = srcBuf->frames; - size_t srcChanCount = srcBuf->channels; - - long dstBufNum = msg->geti(); - long dictBufNum = msg->geti(); - long actBufNum = msg->geti(); - size_t rank = msg->geti(); - size_t iterations = msg->geti(); - size_t fftSize = msg->geti(); - size_t windowSize = msg->geti(); - size_t hopSize = msg->geti(); - - if (dstBufNum == -1 && dictBufNum == -1 && actBufNum == -1) { - Print("fdNMF is not happy because there are no output buffer specified.\n"); - return; - } - - SndBuf *dstBuf, *dictBuf, *actBuf; - size_t dstFrameCount, dictFrameCount, actFrameCount; - size_t dstChanCount, dictChanCount, actChanCount; - - // check sanity of audio destination buffer - if (dstBufNum != -1){ - if (dstBufNum >= world->mNumSndBufs){ - Print("fdNMF is not happy because the destination buffer does not exist.\n"); - return; - } - - dstBuf = world->mSndBufs + dstBufNum; - - if (srcBuf->data == dstBuf->data){ - Print("fdNMF is not happy because the destination buffer is the same as the source buffer.\n"); - return; - } - - dstFrameCount = dstBuf->frames; - dstChanCount = dstBuf->channels; - - if (dstChanCount < (rank * srcChanCount)) { - Print("fdNMF is not happy because the destination buffer has a lower channel count than the number of ranks.\n"); - return; - } - - if (dstFrameCount < srcFrameCount) { - Print("fdNMF is not happy because the destination buffer shorter than the source buffer.\n"); - return; - } - } - - // check sanity of dictionary destination buffer - if (dictBufNum != -1){ - if (dictBufNum >= world->mNumSndBufs){ - Print("fdNMF is not happy because the destination buffer does not exist.\n"); - return; - } +namespace fluid { + namespace sc{ + + class BufNMF: public NRTCommandBase + { + /* + - srcbuf num + – src start frame + - src numframes + – src start chan + – src num chans + – resynths dst + – dicts dst + – acts dst + - 'overwrite' flag [-1:1] + + - rank + - iterations + – window size + – hop size + – fft size + -– boundary flag + -– rand seed + */ + + public: + using client_type = NMFClient; + using NRTCommandBase::NRTCommandBase; + + + ~BufNMF() + { +// if(src) delete src; +// if(resynth) delete resynth; +// if(dict) delete dict; +// if(act) delete act; + } + + void runCommand(World* world, void* replyAddr, char* completionMsgData, size_t completionMsgSize) + { + cmd(world, "AsyncNMF", replyAddr, completionMsgData, completionMsgSize); + } + + bool process(World* world) + { + //sanity check the parameters + bool parametersOk; + NMFClient::ProcessModel processModel; + std::string whatHappened;//this will give us a message to pass back if param check fails + std::tie(parametersOk,whatHappened,processModel) = NMFClient::sanityCheck(mParams); + if(!parametersOk) + { + Print("fdNMF: %s \n", whatHappened.c_str()); + return false; + } + //Now, we can proceed + NMFClient nmf(processModel); + nmf.process(); + mModel = processModel; + + src = static_cast(parameter::lookupParam("Source Buffer", mParams).getBuffer()); + resynth = static_cast(parameter::lookupParam("Resynthesis Buffer", mParams).getBuffer()); + dict = static_cast(parameter::lookupParam("Dictionary Buffer", mParams).getBuffer()); + act = static_cast(parameter::lookupParam("Activation Buffer", mParams).getBuffer()); + + return true; + } + + bool postProcess(World* world) + { + + + if(mModel.resynthesise) + resynth->assignToRT(world); + if(mModel.returnDictionaries) + dict->assignToRT(world); + if(mModel.returnActivations) + act->assignToRT(world); + + return true; + } + + bool postComplete(World* w) { return true; } + + private: + + NMFClient::ProcessModel mModel; + SCBufferView* src; + SCBufferView* resynth; + SCBufferView* dict; + SCBufferView* act; + };//class + } //namespace sc +}//namespace fluid - dictBuf = world->mSndBufs + dictBufNum; - - if (srcBuf->data == dictBuf->data){ - Print("fdNMF is not happy because the destination buffer is the same as the source buffer.\n"); - return; - } - - dictFrameCount = dictBuf->frames; - dictChanCount = dictBuf->channels; - - if (dictChanCount < (rank * srcChanCount)) { - Print("fdNMF is not happy because the destination buffer has a lower channel count than the number of ranks.\n"); - return; - } - - if (dictFrameCount < (fftSize / 2 + 1)) { - Print("fdNMF is not happy because the destination buffer shorter than the source buffer.\n"); - return; - } - } - - // check sanity of activations destination buffer - if (actBufNum != -1){ - if (actBufNum >= world->mNumSndBufs){ - Print("fdNMF is not happy because the destination buffer does not exist.\n"); - return; - } - - actBuf = world->mSndBufs + actBufNum; - - if (srcBuf->data == actBuf->data){ - Print("fdNMF is not happy because the destination buffer is the same as the source buffer.\n"); - return; - } - - actFrameCount = actBuf->frames; - actChanCount = actBuf->channels; - - if (actChanCount < (rank * srcChanCount)) { - Print("fdNMF is not happy because the destination buffer has a lower channel count than the number of ranks.\n"); - return; - } - - if (actFrameCount < (srcFrameCount / hopSize + 1)) { - Print("fdNMF is not happy because the destination buffer shorter than the source buffer.\n"); - return; - } - } - - // make fuildtensorviewers of the SC interleaved input buffer - FluidTensorView in_view ({0,{srcFrameCount, srcChanCount}},srcBuf->data); - - //setup the nmf - NMFClient nmf(rank ,iterations, fftSize, windowSize, hopSize); - - //for each channels - for (int j=0;j audio_in(in_view.col(j)); - - //Process, with resynthesis if needs be - if (dstBufNum != -1){ - nmf.process(audio_in,true); - } else { - nmf.process(audio_in,false); - } - - //Copy audio outputs if they are requested - if (dstBufNum != -1){ - FluidTensorView out_view ({0,{dstFrameCount, dstChanCount}},dstBuf->data); - - for (int i = 0; i < rank; ++i) - { - out_view.col(i + (j*rank)) = nmf.source(i); - } - } - //Copy dictionaries if they are requested - if (dictBufNum != -1){ - FluidTensorView out_view ({0,{dictFrameCount, dictChanCount}},dictBuf->data); - - for (int i = 0; i < rank; ++i) - { - out_view.col(i + (j*rank)) = nmf.dictionary(i); - } - } - //Copy activations if they are requested - if (actBufNum != -1){ - FluidTensorView out_view ({0,{actFrameCount, actChanCount}},actBuf->data); - - for (int i = 0; i < rank; ++i) - { - out_view.col(i + (j*rank)) = nmf.activation(i); - } - } - } -} PluginLoad(OfflineFluidDecompositionUGens) { - ft = inTable; - DefineBufGen("BufNMF", BufNMF); + ft = inTable; + registerCommand(ft, "BufNMF"); } diff --git a/fdNMF/tests.scd b/fdNMF/tests.scd index c29ce1d..f42996b 100644 --- a/fdNMF/tests.scd +++ b/fdNMF/tests.scd @@ -1,30 +1,61 @@ + s.reboot //////////////////////////// // test for efficiency ( -b = Buffer.read(s,"/Users/pa/Documents/documents@hudd/research/projects/fluid corpus navigation/research/denoise_stn/sources/01-mix.wav"); +b = Buffer.read(s,"/Users/owen/Desktop/denoise_stn/sources/01-mix.wav", numFrames:88200); +// s.sync; ~fft_size = 2048; ~frame_size = 1024; ~hop_size = 256; ~which_rank = 0; ) +// ( +// c = Buffer.alloc(s,b.numFrames,5); +// x = Buffer.alloc(s,(~fft_size / 2 +1),5); +// y = Buffer.alloc(s,(b.numFrames / ~hop_size + 1) ,5); +// ) + ( -c = Buffer.alloc(s,b.numFrames,5); -x = Buffer.alloc(s,(~fft_size / 2 +1),5); -y = Buffer.alloc(s,(b.numFrames / ~hop_size + 1) ,5); +c = Buffer.new(s, numFrames:0, numChannels:1); +x = Buffer.new(s,numFrames:0,numChannels:1); +y = Buffer.new(s,numFrames:0,numChannels:1); ) + +( // without sources -d = Main.elapsedTime; b.fdNMF(nil, x, y, 5, 100, ~fft_size,~frame_size,~hop_size,{e = Main.elapsedTime; (e-d).postln}) +r = Routine{ + t = Main.elapsedTime; + FDNMF.nmf(s,b,0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size); + s.sync; + u = Main.elapsedTime; + (u-t).postln; +}.play +); + + +c.query +x.query // with sources -d = Main.elapsedTime; b.fdNMF(c, x, y, 5, 100, ~fft_size,~frame_size,~hop_size,{e = Main.elapsedTime; (e-d).postln}) +( +r = Routine{ + t = Main.elapsedTime; + FDNMF.nmf(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size); + s.sync; + u = Main.elapsedTime; + (u-t).postln; +}.play +) + //look at the dictionaries and activations x.plot; y.plot; - +d.plot; +c.plot; //null test of the sum of sources {(PlayBuf.ar(5,c.bufnum,doneAction:2).sum)+(-1*PlayBuf.ar(1,b.bufnum,doneAction:2))}.play @@ -56,10 +87,10 @@ x.plot; y.plot; ( { var chain; - chain = FFT(LocalBuf(~fft_size), WhiteNoise.ar(BufRd.kr(5,y.bufnum,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_rank]*0.15),0.25,1); + chain = FFT(LocalBuf(~fft_size), WhiteNoise.ar(BufRd.kr(5,d.bufnum,Phasor.ar(1,1/~hop_size,0,(b.numFrames / ~hop_size + 1)),0,1)[~which_rank]*0.15),0.25,1); chain = chain.pvcollect(~fft_size, {|mag, phase, index| - [mag * BufRd.kr(5,x.bufnum,DC.kr(index),0,1)[~which_rank]]; + [mag * BufRd.kr(5,c.bufnum,DC.kr(index),0,1)[~which_rank]]; }); [0,IFFT(chain)]; diff --git a/release-packaging/fdNMF/classes/fdfNMF.sc b/release-packaging/fdNMF/classes/fdfNMF.sc index d4f7aee..6f8c650 100644 --- a/release-packaging/fdNMF/classes/fdfNMF.sc +++ b/release-packaging/fdNMF/classes/fdfNMF.sc @@ -1,18 +1,43 @@ // adds an instance method to the Buffer class -+ Buffer { - fdNMF { arg dstBuf, dictBuf, actBuf, rank = 1, iterations = 100, fftSize = 2048, windowSize = 2048, hopSize = 512, action; +FDNMF { + /*fdNMF { arg dstBuf, dictBuf, actBuf, rank = 1, iterations = 100, fftSize = 2048, windowSize = 2048, hopSize = 512, action; var resp; if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw }; - // responder idea stolen from getToFloatArray +/* // responder idea stolen from getToFloatArray resp = OSCFunc({ arg msg; if(msg[1]== '/b_gen' && msg[2]== bufnum, { resp.clear; action.value(bufnum); }); - }, '/done', server.addr); + }, '/done', server.addr);*/ - server.listSendMsg([\b_gen, bufnum, "BufNMF", if(dstBuf.isNil, -1, {dstBuf.bufnum}), if(dictBuf.isNil, -1, {dictBuf.bufnum}), if(actBuf.isNil, -1, {actBuf.bufnum}), rank, iterations, fftSize, windowSize, hopSize]) + // server.listSendMsg([\b_gen, bufnum, "BufNMF", if(dstBuf.isNil, -1, {dstBuf.bufnum}), if(dictBuf.isNil, -1, {dictBuf.bufnum}), if(actBuf.isNil, -1, {actBuf.bufnum}), rank, iterations, fftSize, windowSize, hopSize]) + server.sendMsg(\cmd, \BufNMF, bufnum, if(dstBuf.isNil, -1, {dstBuf.bufnum}), if(dictBuf.isNil, -1, {dictBuf.bufnum}), if(actBuf.isNil, -1, {actBuf.bufnum}), rank, iterations, fftSize, windowSize, hopSize); + + }*/ + + + *nmf { arg server, srcBuf, startAt = 0, nFrames = -1,startChan = 0,nChans = -1, dstBuf, dictBuf, dictFlag = 0, actBuf, actFlag = 0, rank = 1, iterations = 100, windowSize = 2048, hopSize = 512, fftSize = 2048, action; + var resp; + + server = server ? Server.default; + + if(srcBuf.bufnum.isNil) { Error("Invalid buffer").format(thisMethod.name, this.class.name).throw}; + + dstBuf.bufnum.postln; + + server.sendMsg(\cmd, \BufNMF, + if(srcBuf.isNil, -1, {srcBuf.bufnum}), + startAt, nFrames, startChan, nChans, + if(dstBuf.isNil, -1, {dstBuf.bufnum}), + if(dictBuf.isNil, -1, {dictBuf.bufnum}), + dictFlag, + if(actBuf.isNil, -1, {actBuf.bufnum}), + actFlag, + rank, iterations, windowSize, hopSize,fftSize); } + + }