From a160a86c7675075c33b1a3a42eaca189372cdb4c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 5 Aug 2019 12:21:40 +0100 Subject: [PATCH 001/550] Merge threading into ClientMessaging, re-add argument parsing from sc_msg_iter --- include/FluidSCWrapper.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index e23494d..747e41f 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -369,6 +369,8 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } + auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } + auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } auto fromArgs(World *w, ArgType args, BufferT::type, int) { @@ -393,6 +395,8 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase return a.value(); } }; + template + using ArgumentSetter = Setter; template using ControlSetter = Setter; From 53b9d7dc0f92c290815d5b70e06cdff9bf7af12b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 5 Aug 2019 15:52:40 +0100 Subject: [PATCH 002/550] Starting to add messages to SCWrapper --- include/FluidSCWrapper.hpp | 63 ++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 747e41f..4d8d5d5 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -361,28 +361,35 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { using FloatControlsIter = impl::FloatControlsIter; - // Iterate over arguments via callbacks from params object - template - struct Setter + template + struct ParamReader { - static constexpr size_t argSize = C::getParameterDescriptors().template get().fixedSize; - - auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } - auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } - auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } - auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } + + static auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } + static auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } + static auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } + static auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } - auto fromArgs(World *w, ArgType args, BufferT::type, int) + static auto fromArgs(World *w, ArgType args, BufferT::type, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } - auto fromArgs(World *w, ArgType args, InputBufferT::type, int) + static auto fromArgs(World *w, ArgType args, InputBufferT::type, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } + }; + + + + // Iterate over arguments via callbacks from params object + template + struct Setter + { + static constexpr size_t argSize = C::getParameterDescriptors().template get().fixedSize; typename T::type operator()(World *w, ArgType args) { @@ -390,17 +397,49 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase using LiteralType = typename ParamLiteralConvertor::LiteralType; for (size_t i = 0; i < argSize; i++) - a[i] = static_cast(fromArgs(w, args, a[0], 0)); + a[i] = static_cast(ParamReader::fromArgs(w, args, a[0], 0)); return a.value(); } }; + template using ArgumentSetter = Setter; template using ControlSetter = Setter; + //Sets up a single /u_cmd + template + struct SetupMessage + { + void operator()(const T& message) + { +// class_addmethod(getClass(), (method)invokeMessage, message.name,A_GIMME, 0); + auto ft = getInterfaceTable(); + ft->fDefineUnitCmd(message.name, invokeMessage); + } + }; + + template + static void invokeMessage(FluidSCWrapper* x,sc_msg_iter* args) + { + using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; + invokeMessageImpl(x,s,ac,av,IndexList()); + } + + template + static void invokeMessageImp(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) + { + using ArgTuple = typename Client::MessageSetType::template MessageDescriptorAt::ArgumentTypes; + ArgTuple args; + (void)std::initializer_list{(std::get(args) = (ParamReader::fromArgs(x->mWorld,inArgs,std::get(args)),0))...}; + + } + + + + public: using Client = C; using ParameterSetType = typename C::ParamSetType; From f54fcfad9f710a97c8180deb3b5a01260fb7a098 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 7 Aug 2019 14:14:42 +0100 Subject: [PATCH 003/550] Deal with message return types, and fix thread-completeness logic --- include/FluidSCWrapper.hpp | 257 +++++++++++++++++++++++++++++++------ 1 file changed, 217 insertions(+), 40 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index f0de16b..8c5b09b 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -8,11 +8,15 @@ #include +#include +#include #include #include #include #include +#include + namespace fluid { namespace client { @@ -114,9 +118,9 @@ public: mOutputConnections.emplace_back(true); mOutputs.emplace_back(nullptr, 0, 0); } - + for (int i = 0; i < static_cast(mClient.controlChannelsOut()); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } - + mCalcFunc = make_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); } @@ -161,6 +165,7 @@ template class NonRealTime: public SCUnit { using ParamSetType = typename Client::ParamSetType; + public: static void setup(InterfaceTable *ft, const char *name) @@ -184,26 +189,35 @@ public: mClient.setSynchronous(false); mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); mWorld->ft->fSendMsgFromRT(mWorld,mFifoMsg); + + //we want to poll thread roughly every 20ms + checkThreadInterval = static_cast(0.02 / controlDur()); set_calc_function(); }; /// The calc function. Checks to see if we've cancelled, spits out progress, launches tidy up when complete void poll(int) { - if(!mClient.done()) - { +// if(!mClient.done()) +// { out0(0) = static_cast(mClient.progress()); - } - else { +// } +// else { + if(0 == pollCounter++) + { + mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this, + postProcess, exchangeBuffers, tidyUp, destroy, + 0, nullptr); + } + + pollCounter %= checkThreadInterval; + // if(mClient.state() == kDone) // mDone = true; - mCalcFunc = mWorld->ft->fClearUnitOutputs; +// mCalcFunc = mWorld->ft->fClearUnitOutputs; // if(!mDone) - mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), this, - postProcess, exchangeBuffers, tidyUp, destroy, - 0, nullptr); - - } + +// } } /// To be called on NRT thread. Validate parameters and commence processing in new thread @@ -228,22 +242,25 @@ public: { auto w = static_cast(data); Result r; - w->mClient.checkProgress(r); - if(r.status() == Result::Status::kCancelled) - { - std::cout << Wrapper::getName() << ": Processing cancelled \n"; - return false; - } + ProcessState s = w->mClient.checkProgress(r); - if(!r.ok()) + if(s==ProcessState::kDone || s==ProcessState::kDoneStillProcessing) { - std::cout << "ERROR: " << Wrapper::getName() << ": " << r.message().c_str() << '\n'; - return false; + if(r.status() == Result::Status::kCancelled) + { + std::cout << Wrapper::getName() << ": Processing cancelled \n"; + return false; + } + + if(!r.ok()) + { + std::cout << "ERROR: " << Wrapper::getName() << ": " << r.message().c_str() << '\n'; + return false; + } + + return true; } - -// w->mDone = true; - - return true; + return false; } /// swap NRT buffers back to RT-land @@ -252,13 +269,13 @@ public: static bool tidyUp(World *world, void *data) { return static_cast(data)->tidyUp(world); } /// Now we're actually properly done, call the UGen's done action (possibly destroying this instance) - static void destroy(World *world, void *data) + static void destroy(World*, void*) { - auto w = static_cast(data); +// auto w = static_cast(data); // if(w->mDone) // { - int doneAction = static_cast(w->in0(static_cast(w->mNumInputs - 1))); - world->ft->fDoneAction(doneAction,w); +// int doneAction = static_cast(w->in0(static_cast(w->mNumInputs - 1))); +// world->ft->fDoneAction(doneAction,w); // } } @@ -315,6 +332,8 @@ private: char * mCompletionMessage = nullptr; void * mReplyAddr = nullptr; const char *mName = nullptr; + size_t checkThreadInterval; + size_t pollCounter{0}; protected: ParamSetType mParams; Client mClient; @@ -367,24 +386,31 @@ using FluidSCWrapperBase = FluidSCWrapperImpl, is template class FluidSCWrapper : public impl::FluidSCWrapperBase { + using FloatControlsIter = impl::FloatControlsIter; template struct ParamReader { - + + static auto fromArgs(World *, sc_msg_iter* args, std::string, int) + { + return std::string(args->gets("")); + } + + static auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } static auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } static auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } static auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } - static auto fromArgs(World *w, ArgType args, BufferT::type, int) + static auto fromArgs(World *w, ArgType args, BufferT::type&, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } - static auto fromArgs(World *w, ArgType args, InputBufferT::type, int) + static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); @@ -417,35 +443,155 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template using ControlSetter = Setter; + //CryingEmoji.png: SC API hides all the useful functions for sending + //replies back to the language with things like, uh, strings and stuff. + //We have Node_SendReply, which assumes you are sending an array of float, + //and must be called only from the RT thread. Thanks. + //So, we do in reverse what the SendReply Ugen does, and parse + //an array of floats as characters in the language. VomitEmoji.png + + struct ToFloatArray + { + static size_t allocSize(SCBufferAdaptor*){ return 1; } + static size_t allocSize(double){ return 1; } + static size_t allocSize(float){ return 1; } + static size_t allocSize(intptr_t){ return 1; } + static size_t allocSize(std::string s){ return s.size(); } + static size_t allocSize(FluidTensor s) + { + size_t count = 0; + for(auto& str: s) count += (str.size() + 1); + return count; + } + template + static size_t allocSize(FluidTensor s) { return s.size() ; } + + + static void convert(float* f, SCBufferAdaptor* buf) { f[0] = buf->bufnum(); } + static void convert(float* f, double d) { f[0] = static_cast(d); } + static void convert(float* f, intptr_t i) { f[0] = i; } + static void convert(float* f, std::string s) { std::copy(s.begin(), s.end(), f); } + static void convert(float* f, FluidTensor s) + { + for(auto& str: s) + { + std::copy(str.begin(), str.end(), f); + f += str.size(); + *f++ = 0; + } + } + template + static void convert(float* f, FluidTensor s) + { + static_assert(std::is_convertible::value,"Can't convert this to float output"); + std::copy(s.begin(), s.end(), f); + } + }; + + + //So, to handle a message to a plugin we will need to + // (1) Launch the invovation of the message on the SC NRT Queue using FIFO Message + // (2) Run the actual function (maybe asynchronously, in our own thread) + // (3) Launch an asynchronous command to send the reply back (in Stage 3) + + template + struct MessageDispatch + { + static constexpr size_t Message = N; + FluidSCWrapper* wrapper; + ArgTuple args; + Ret result; + std::string name; + }; + //Sets up a single /u_cmd template struct SetupMessage { void operator()(const T& message) { -// class_addmethod(getClass(), (method)invokeMessage, message.name,A_GIMME, 0); auto ft = getInterfaceTable(); - ft->fDefineUnitCmd(message.name, invokeMessage); + ft->fDefineUnitCmd(getName(), message.name, launchMessage); } }; template - static void invokeMessage(FluidSCWrapper* x,sc_msg_iter* args) + static void launchMessage(Unit* u,sc_msg_iter* args) { + FluidSCWrapper* x = static_cast(u); using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; - invokeMessageImpl(x,s,ac,av,IndexList()); + launchMessageImpl(x,args,IndexList()); } template - static void invokeMessageImp(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) + static void launchMessageImpl(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) { - using ArgTuple = typename Client::MessageSetType::template MessageDescriptorAt::ArgumentTypes; - ArgTuple args; - (void)std::initializer_list{(std::get(args) = (ParamReader::fromArgs(x->mWorld,inArgs,std::get(args)),0))...}; + using MessageDescriptor = typename Client::MessageSetType::template MessageDescriptorAt; + using ArgTuple = typename MessageDescriptor::ArgumentTypes; + using ReturnType = typename MessageDescriptor::ReturnType; + using IndexList = typename MessageDescriptor::IndexList; + using MessageData = MessageDispatch; + auto ft = getInterfaceTable(); + void* msgptr = ft->fRTAlloc(x->mWorld,sizeof(MessageData)); + MessageData* msg = new(msgptr) MessageData; + ArgTuple& args = msg->args; + (void)std::initializer_list{(std::get(args) = ParamReader::fromArgs(x->mWorld,inArgs,std::get(args),0),0)...}; + + msg->name = '/' + Client::getMessageDescriptors().template name(); + msg->wrapper = x; + ft->fDoAsynchronousCommand(x->mWorld, nullptr, getName(), msg, + [](World*, void* data) //NRT thread: invocation + { + MessageData* m = static_cast(data); + m->result = ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; + + + + if(!m->result.ok()) + { + printResult(m->wrapper, m->result); + return false; + } + return true; + }, + [](World*, void* data) //RT thread: response + { + MessageData* m = static_cast(data); + messageOutput(m->wrapper,m->name,m->result); + return true; + } + , nullptr, //NRT Thread: No-op + [](World* w, void* data) //RT thread: clean up + { + getInterfaceTable()->fRTFree(w,data); + }, + 0, nullptr); } + template //Call from NRT + static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, std::index_sequence) + { + return x->mClient.template invoke(x->mClient,std::get(args)...); + } + template //call from RT + static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult& result) + { + auto ft = getInterfaceTable(); + //allocate return values + size_t numArgs = ToFloatArray::allocSize(static_cast(result)); + float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); + //copy return data + ToFloatArray::convert(values,static_cast(result)); + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); + } + + static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult&) + { + auto ft = getInterfaceTable(); + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + } public: @@ -474,6 +620,7 @@ public: getName(name); getInterfaceTable(ft); impl::FluidSCWrapperBase::setup(ft, name); + Client::getMessageDescriptors().template iterate(); } static auto& setParams(ParameterSetType& p, bool verbose, World* world, FloatControlsIter& inputs, bool constrain = false) @@ -489,8 +636,38 @@ public: } return p; } + + static void printResult(FluidSCWrapper* x, Result& r) + { + if (!x) return; + + switch (r.status()) + { + case Result::Status::kWarning: + { + if(x->mWorld->mVerbosity > 0) + std::cout << "WARNING: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kError: + { + std::cout << "ERROR: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kCancelled: + { + std::cout << "Task cancelled\n" << '\n'; + break; + } + default: { + } + } + } }; + + + template class Client> void makeSCWrapper(const char *name, InterfaceTable *ft) { From 6960c1b95e6c930e3e5359a0f183260d4b9ffa9b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 7 Aug 2019 14:15:11 +0100 Subject: [PATCH 004/550] Add message testing SC class and test code --- src/FluidMessageTest/CMakeLists.txt | 20 ++++++++++++++++++++ src/FluidMessageTest/FluidMessageTest.cpp | 13 +++++++++++++ tests/TestFluidMessageTest..scd | 13 +++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/FluidMessageTest/CMakeLists.txt create mode 100644 src/FluidMessageTest/FluidMessageTest.cpp create mode 100644 tests/TestFluidMessageTest..scd diff --git a/src/FluidMessageTest/CMakeLists.txt b/src/FluidMessageTest/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidMessageTest/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidMessageTest/FluidMessageTest.cpp b/src/FluidMessageTest/FluidMessageTest.cpp new file mode 100644 index 0000000..ef71f09 --- /dev/null +++ b/src/FluidMessageTest/FluidMessageTest.cpp @@ -0,0 +1,13 @@ + +// 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 +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) { + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidMessageTest", ft); +} diff --git a/tests/TestFluidMessageTest..scd b/tests/TestFluidMessageTest..scd new file mode 100644 index 0000000..63b0218 --- /dev/null +++ b/tests/TestFluidMessageTest..scd @@ -0,0 +1,13 @@ +~messageTest +a = {~messageTest = FluidMessageTest.kr}.play +~messageTest.testReturnStrings(nil,a.nodeID,{|msg| msg.postln}); +~messageTest.testReturnNumbers(nil,a.nodeID,{|msg| msg.postln}); +~messageTest.testReturnOneString(nil,a.nodeID,{|msg| msg.postln}); +~messageTest.testReturnOneNumber(nil,a.nodeID,{|msg| msg.postln}); +b = Buffer.read(s,File.realpath(FluidMessageTest.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); +~messageTest.testAccessBuffer(nil,a.nodeID,b,{|msg| msg.postln}); +b.numFrames +~messageTest.testPassString(nil,a.nodeID,'hello, you big lovely server',1,2,3,{'testPassString Done'.postln}); + +// OSCFunc.trace(true) +// OSCFunc.trace(false) \ No newline at end of file From 1aaf244efb402d9064be4b265d62cdec38c64fc0 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 13 Aug 2019 09:28:14 +0100 Subject: [PATCH 005/550] Cmake: Adding Fluid Manuipulation repo dependency the quick and dirty way --- CMakeLists.txt | 1 + scripts/target_post.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71c94fb..0513eda 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ ENDMACRO() set(FLUID_PATH ~/fluid_decomposition CACHE PATH "The top level of the fluid_decomposition repo") set(LOCAL_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(FLUID_M_PATH ~/dev/fluid_manipulation/fluid_manipulation CACHE PATH "The top level of the fluid_manipulation repo") get_filename_component(FLUID_ABS_PATH "${FLUID_PATH}" ABSOLUTE) diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index 2ce587f..ea4798e 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -27,6 +27,7 @@ target_include_directories( ${PLUGIN} PRIVATE ${LOCAL_INCLUDES} + "${FLUID_M_PATH}/include/" ) target_include_directories( From 3660089bcf6877b58f5ef3e11bc80d973200ab8a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 13 Aug 2019 09:30:44 +0100 Subject: [PATCH 006/550] FluidSCWrapper: Add missing newline to error message so it appears at the correct moment --- include/FluidSCWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 8c5b09b..02195cf 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -631,7 +631,7 @@ public: p.template setParameterValues(verbose, world, inputs); if (constrain)p.constrainParameterValues(); } else { - std::cout << "ERROR: " << getName() << ": parameter count mismatch. Perhaps your binary plugins and SC sources are different versions"; + std::cout << "ERROR: " << getName() << ": parameter count mismatch. Perhaps your binary plugins and SC sources are different versions\n"; //TODO: work out how to bring any further work to a halt } return p; From aa2ea9c98d61062b759c0e23330f1debb7fabf9a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 13 Aug 2019 09:34:58 +0100 Subject: [PATCH 007/550] FluidSCWrapper: Re-enable DoneAction processing, hopefully robustly this time --- include/FluidSCWrapper.hpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 02195cf..9cad17e 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -219,12 +219,14 @@ public: // } } + + static void nop(Unit*, int) {} /// To be called on NRT thread. Validate parameters and commence processing in new thread static void initNRTJob(FifoMsg* f) { auto w = static_cast(f->mData); - + w->mDone = false; Result result = validateParameters(w); if (!result.ok()) @@ -246,6 +248,8 @@ public: if(s==ProcessState::kDone || s==ProcessState::kDoneStillProcessing) { + w->mDone = true; + if(r.status() == Result::Status::kCancelled) { std::cout << Wrapper::getName() << ": Processing cancelled \n"; @@ -269,14 +273,15 @@ public: static bool tidyUp(World *world, void *data) { return static_cast(data)->tidyUp(world); } /// Now we're actually properly done, call the UGen's done action (possibly destroying this instance) - static void destroy(World*, void*) + static void destroy(World* world, void* data) { -// auto w = static_cast(data); -// if(w->mDone) -// { -// int doneAction = static_cast(w->in0(static_cast(w->mNumInputs - 1))); -// world->ft->fDoneAction(doneAction,w); -// } + auto w = static_cast(data); + if(w->mDone && w->mNumInputs > 0) //don't check for doneAction if UGen has no ins + { + int doneAction = static_cast(w->in0(static_cast(w->mNumInputs - 1))); + if(doneAction >= 2) w->mCalcFunc = nop; + world->ft->fDoneAction(doneAction,w); + } } static void doCancel(Unit *unit, sc_msg_iter*) @@ -537,17 +542,16 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase MessageData* msg = new(msgptr) MessageData; ArgTuple& args = msg->args; (void)std::initializer_list{(std::get(args) = ParamReader::fromArgs(x->mWorld,inArgs,std::get(args),0),0)...}; - + msg->name = '/' + Client::getMessageDescriptors().template name(); msg->wrapper = x; + x->mDone = false; ft->fDoAsynchronousCommand(x->mWorld, nullptr, getName(), msg, [](World*, void* data) //NRT thread: invocation { MessageData* m = static_cast(data); m->result = ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; - - if(!m->result.ok()) { printResult(m->wrapper, m->result); From d70a23f745a0e4a2757de441aa29ecf7f56b4e4c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 13 Aug 2019 09:36:19 +0100 Subject: [PATCH 008/550] FluidSCWrapper: Clients no longer templates; find RT/NRT info in ClientWrapper --- include/FluidSCWrapper.hpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 9cad17e..060f901 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -176,7 +176,7 @@ public: /// Final input is the doneAction, not a param, so we skip it in the controlsIterator NonRealTime() : - mControlsIterator{mInBuf,static_cast(static_cast(mNumInputs) - mSpecialIndex - 1)} + mControlsIterator{mInBuf,static_cast(mNumInputs == 0 ? 0 : static_cast(mNumInputs) - mSpecialIndex - 1)} , mParams{Wrapper::Client::getParameterDescriptors()} , mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator,true)} {} @@ -381,7 +381,7 @@ class FluidSCWrapperImpl : pub // Make base class(es), full of CRTP mixin goodness template -using FluidSCWrapperBase = FluidSCWrapperImpl, isNonRealTime, isRealTime>; +using FluidSCWrapperBase = FluidSCWrapperImpl, typename Client::isNonRealTime, typename Client::isRealTime>; } // namespace impl @@ -524,14 +524,14 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static void launchMessage(Unit* u,sc_msg_iter* args) { FluidSCWrapper* x = static_cast(u); - using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; + using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; launchMessageImpl(x,args,IndexList()); } template static void launchMessageImpl(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) { - using MessageDescriptor = typename Client::MessageSetType::template MessageDescriptorAt; + using MessageDescriptor = typename Client::MessageSetType::template MessageDescriptorAt; using ArgTuple = typename MessageDescriptor::ArgumentTypes; using ReturnType = typename MessageDescriptor::ReturnType; using IndexList = typename MessageDescriptor::IndexList; @@ -669,13 +669,10 @@ public: } }; - - - -template class Client> +template void makeSCWrapper(const char *name, InterfaceTable *ft) { - FluidSCWrapper>::setup(ft, name); + FluidSCWrapper::setup(ft, name); } } // namespace client From 4a24c25b8dd681d9c32e6fc5e779402c81b4d6a6 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 13 Aug 2019 09:37:15 +0100 Subject: [PATCH 009/550] Update object stubs with new aliases --- src/FluidAmpSlice/FluidAmpSlice.cpp | 2 +- src/FluidGain/FluidGain.cpp | 2 +- src/FluidHPSS/FluidHPSS.cpp | 2 +- src/FluidLoudness/FluidLoudness.cpp | 2 +- src/FluidMFCC/FluidMFCC.cpp | 2 +- src/FluidMelBands/FluidMelBands.cpp | 2 +- src/FluidMessageTest/FluidMessageTest.cpp | 2 +- src/FluidNMFFilter/FluidNMFFilter.cpp | 2 +- src/FluidNMFMatch/FluidNMFMatch.cpp | 2 +- src/FluidNoveltySlice/FluidNoveltySlice.cpp | 2 +- src/FluidOnsetSlice/FluidOnsetSlice.cpp | 2 +- src/FluidPitch/FluidPitch.cpp | 2 +- src/FluidSTFTPass/FluidSTFTPass.cpp | 2 +- src/FluidSines/FluidSines.cpp | 2 +- src/FluidSpectralShape/FluidSpectralShape.cpp | 2 +- src/FluidTransientSlice/FluidTransientSlice.cpp | 2 +- src/FluidTransients/FluidTransients.cpp | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/FluidAmpSlice/FluidAmpSlice.cpp b/src/FluidAmpSlice/FluidAmpSlice.cpp index 4a3abe0..64738d2 100644 --- a/src/FluidAmpSlice/FluidAmpSlice.cpp +++ b/src/FluidAmpSlice/FluidAmpSlice.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidAmpSlice", ft); + makeSCWrapper("FluidAmpSlice", ft); } diff --git a/src/FluidGain/FluidGain.cpp b/src/FluidGain/FluidGain.cpp index 286b3c0..54e192f 100644 --- a/src/FluidGain/FluidGain.cpp +++ b/src/FluidGain/FluidGain.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidGainUgen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidGain", ft); + makeSCWrapper("FluidGain", ft); } diff --git a/src/FluidHPSS/FluidHPSS.cpp b/src/FluidHPSS/FluidHPSS.cpp index 3b4677f..387e9a3 100644 --- a/src/FluidHPSS/FluidHPSS.cpp +++ b/src/FluidHPSS/FluidHPSS.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidHPSS", ft); + makeSCWrapper("FluidHPSS", ft); } diff --git a/src/FluidLoudness/FluidLoudness.cpp b/src/FluidLoudness/FluidLoudness.cpp index 378fdff..690be0c 100644 --- a/src/FluidLoudness/FluidLoudness.cpp +++ b/src/FluidLoudness/FluidLoudness.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidLoudness", ft); + makeSCWrapper("FluidLoudness", ft); } diff --git a/src/FluidMFCC/FluidMFCC.cpp b/src/FluidMFCC/FluidMFCC.cpp index 8e786ff..2ce26a5 100644 --- a/src/FluidMFCC/FluidMFCC.cpp +++ b/src/FluidMFCC/FluidMFCC.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidMFCC", ft); + makeSCWrapper("FluidMFCC", ft); } diff --git a/src/FluidMelBands/FluidMelBands.cpp b/src/FluidMelBands/FluidMelBands.cpp index 8881261..228c820 100644 --- a/src/FluidMelBands/FluidMelBands.cpp +++ b/src/FluidMelBands/FluidMelBands.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidMelBands", ft); + makeSCWrapper("FluidMelBands", ft); } diff --git a/src/FluidMessageTest/FluidMessageTest.cpp b/src/FluidMessageTest/FluidMessageTest.cpp index ef71f09..778df9c 100644 --- a/src/FluidMessageTest/FluidMessageTest.cpp +++ b/src/FluidMessageTest/FluidMessageTest.cpp @@ -1,7 +1,7 @@ // 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 +#include #include static InterfaceTable *ft; diff --git a/src/FluidNMFFilter/FluidNMFFilter.cpp b/src/FluidNMFFilter/FluidNMFFilter.cpp index 6e20346..90e40f0 100644 --- a/src/FluidNMFFilter/FluidNMFFilter.cpp +++ b/src/FluidNMFFilter/FluidNMFFilter.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidNMFFilter", ft); + makeSCWrapper("FluidNMFFilter", ft); } diff --git a/src/FluidNMFMatch/FluidNMFMatch.cpp b/src/FluidNMFMatch/FluidNMFMatch.cpp index 3a70e75..1b93838 100644 --- a/src/FluidNMFMatch/FluidNMFMatch.cpp +++ b/src/FluidNMFMatch/FluidNMFMatch.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidNMFMatch", ft); + makeSCWrapper("FluidNMFMatch", ft); } diff --git a/src/FluidNoveltySlice/FluidNoveltySlice.cpp b/src/FluidNoveltySlice/FluidNoveltySlice.cpp index b2f5103..22c78cf 100644 --- a/src/FluidNoveltySlice/FluidNoveltySlice.cpp +++ b/src/FluidNoveltySlice/FluidNoveltySlice.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidNoveltySlice", ft); + makeSCWrapper("FluidNoveltySlice", ft); } diff --git a/src/FluidOnsetSlice/FluidOnsetSlice.cpp b/src/FluidOnsetSlice/FluidOnsetSlice.cpp index f60f7a0..dd47e9f 100644 --- a/src/FluidOnsetSlice/FluidOnsetSlice.cpp +++ b/src/FluidOnsetSlice/FluidOnsetSlice.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidOnsetSlice", ft); + makeSCWrapper("FluidOnsetSlice", ft); } diff --git a/src/FluidPitch/FluidPitch.cpp b/src/FluidPitch/FluidPitch.cpp index 4821cc4..81e74c4 100644 --- a/src/FluidPitch/FluidPitch.cpp +++ b/src/FluidPitch/FluidPitch.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidPitch", ft); + makeSCWrapper("FluidPitch", ft); } diff --git a/src/FluidSTFTPass/FluidSTFTPass.cpp b/src/FluidSTFTPass/FluidSTFTPass.cpp index 7447259..f2188aa 100644 --- a/src/FluidSTFTPass/FluidSTFTPass.cpp +++ b/src/FluidSTFTPass/FluidSTFTPass.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidSTFTPass", ft); + makeSCWrapper("FluidSTFTPass", ft); } diff --git a/src/FluidSines/FluidSines.cpp b/src/FluidSines/FluidSines.cpp index 2a2f032..bb4c7fb 100644 --- a/src/FluidSines/FluidSines.cpp +++ b/src/FluidSines/FluidSines.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidSines", ft); + makeSCWrapper("FluidSines", ft); } diff --git a/src/FluidSpectralShape/FluidSpectralShape.cpp b/src/FluidSpectralShape/FluidSpectralShape.cpp index 6668f16..fa6efc7 100644 --- a/src/FluidSpectralShape/FluidSpectralShape.cpp +++ b/src/FluidSpectralShape/FluidSpectralShape.cpp @@ -9,5 +9,5 @@ static InterfaceTable *ft; PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidSpectralShape", ft); + makeSCWrapper("FluidSpectralShape", ft); } diff --git a/src/FluidTransientSlice/FluidTransientSlice.cpp b/src/FluidTransientSlice/FluidTransientSlice.cpp index 5be8ace..1a60332 100644 --- a/src/FluidTransientSlice/FluidTransientSlice.cpp +++ b/src/FluidTransientSlice/FluidTransientSlice.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidTransientSlice", ft); + makeSCWrapper("FluidTransientSlice", ft); } diff --git a/src/FluidTransients/FluidTransients.cpp b/src/FluidTransients/FluidTransients.cpp index 162f7fc..0b7c22f 100644 --- a/src/FluidTransients/FluidTransients.cpp +++ b/src/FluidTransients/FluidTransients.cpp @@ -10,5 +10,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidTransients", ft); + makeSCWrapper("FluidTransients", ft); } From 111d00fc4c0a1d9c5562bd529ffe671dfe3dbeba Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:01:32 +0100 Subject: [PATCH 010/550] SC Wrapper rougue include --- include/FluidSCWrapper.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 060f901..6beebe5 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -15,7 +15,6 @@ #include #include -#include namespace fluid { namespace client { From 70c8cfebda8384ffe8662d4242f3f946aa773d53 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:08:48 +0100 Subject: [PATCH 011/550] =?UTF-8?q?SC=20Wrapper:=20incoming=20/=20outgoing?= =?UTF-8?q?=20data=20=E2=80=93=C2=A0deal=20more=20liberally=20with=20int?= =?UTF-8?q?=20/=20fp=20types=20(for=20message=20returns);=20deal=20with=20?= =?UTF-8?q?goddam=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/FluidSCWrapper.hpp | 74 +++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6beebe5..059d39f 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -399,18 +399,50 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static auto fromArgs(World *, sc_msg_iter* args, std::string, int) { - return std::string(args->gets("")); + const char* recv = args->gets(""); + + return std::string(recv?recv:""); } + static auto fromArgs(World *w, FloatControlsIter& args, std::string, int) + { + //first is string size, then chars + int size = static_cast(args.next()); + char* chunk = static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1)); + + if (!chunk) { + std::cout << "ERROR: " << FluidSCWrapper::getName() << ": RT memory allocation failed\n"; + return std::string{""}; + } + + for(int i = 0; i < size; ++i) + chunk[i] = static_cast(args.next()); + + chunk[size] = 0; //terminate string + + return std::string{chunk}; + } + + + template + static std::enable_if_t::value,T> + fromArgs(World *, FloatControlsIter& args, T, int) { return args.next(); } + + template + static std::enable_if_t::value,T> + fromArgs(World *, FloatControlsIter& args, T, int) { return args.next(); } + + template + static std::enable_if_t::value,T> + fromArgs(World *, sc_msg_iter* args, T, int defVal) { return args->geti(defVal); } - static auto fromArgs(World *, FloatControlsIter& args, LongT::type, int) { return args.next(); } - static auto fromArgs(World *, FloatControlsIter& args, FloatT::type, int) { return args.next(); } - static auto fromArgs(World *, sc_msg_iter* args, LongT::type, int defVal) { return args->geti(defVal); } - static auto fromArgs(World *, sc_msg_iter* args, FloatT::type, int) { return args->getf(); } + template + static std::enable_if_t::value,T> + fromArgs(World *, sc_msg_iter* args, T, int) { return args->getf(); } static auto fromArgs(World *w, ArgType args, BufferT::type&, int) { - typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); + typename LongT::type bufnum = static_cast(ParamReader::fromArgs(w, args, typename LongT::type(), -1)); return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } @@ -456,11 +488,14 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase struct ToFloatArray { - static size_t allocSize(SCBufferAdaptor*){ return 1; } - static size_t allocSize(double){ return 1; } - static size_t allocSize(float){ return 1; } - static size_t allocSize(intptr_t){ return 1; } - static size_t allocSize(std::string s){ return s.size(); } + static size_t allocSize(typename BufferT::type){ return 1; } + + template + static std::enable_if_t::value||std::is_floating_point::value,size_t> + allocSize(T){ return 1; } + + static size_t allocSize(std::string s){ return s.size() + 1; } //put null char at end when we send + static size_t allocSize(FluidTensor s) { size_t count = 0; @@ -470,11 +505,18 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template static size_t allocSize(FluidTensor s) { return s.size() ; } - - static void convert(float* f, SCBufferAdaptor* buf) { f[0] = buf->bufnum(); } - static void convert(float* f, double d) { f[0] = static_cast(d); } - static void convert(float* f, intptr_t i) { f[0] = i; } - static void convert(float* f, std::string s) { std::copy(s.begin(), s.end(), f); } + + static void convert(float* f, typename BufferT::type buf) { f[0] = static_cast(buf.get())->bufnum(); } + + template + static std::enable_if_t::value||std::is_floating_point::value> + convert(float* f, T x) { f[0] = static_cast(x); } + + static void convert(float* f, std::string s) + { + std::copy(s.begin(), s.end(), f); + f[s.size()] = 0; //terminate + } static void convert(float* f, FluidTensor s) { for(auto& str: s) From 74e6a25688939d179ebc19d8b8070216d6302c8d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:12:38 +0100 Subject: [PATCH 012/550] SC Wrapper: To deal with incoming strings (whose size we don't know in advance), we have to change how we deal with unexpected numbers of incoming parameters. Now we just warn, and use defaults in the case of a short fall. --- include/FluidSCWrapper.hpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 059d39f..0615174 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -41,7 +41,10 @@ struct FloatControlsIter } size_t size() const noexcept { return mSize; } - + size_t remain() + { + return mSize - mCount; + } private: float **mValues; size_t mSize; @@ -89,17 +92,6 @@ public: assert(!(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) && "Client can't have both audio and control outputs"); - //If we don't the number of arguments we expect, the language side code is probably the wrong version - //set plugin to no-op, squawk, and bail; - if(mControlsIterator.size() != Client::getParameterDescriptors().count()) - { - mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; - std::cout << "ERROR: " << Wrapper::getName() << " wrong number of arguments. Expected " - << Client::getParameterDescriptors().count() << ", got " << mControlsIterator.size() - << ". Your .sc file and binary plugin might be different versions." << std::endl; - return; - } - mClient.sampleRate(fullSampleRate()); mInputConnections.reserve(mClient.audioChannelsIn()); mOutputConnections.reserve(mClient.audioChannelsOut()); @@ -463,6 +455,13 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase typename T::type operator()(World *w, ArgType args) { + //Just return default if there's nothing left to grab + if(args.remain() == 0) + { + std::cout << "WARNING: " << getName() << " received fewer parameters than expected\n"; + return C::getParameterDescriptors().template makeValue(); + } + ParamLiteralConvertor a; using LiteralType = typename ParamLiteralConvertor::LiteralType; @@ -670,15 +669,9 @@ public: static auto& setParams(ParameterSetType& p, bool verbose, World* world, FloatControlsIter& inputs, bool constrain = false) { - //We won't even try and set params if the arguments don't match - if(inputs.size() == C::getParameterDescriptors().count()) - { - p.template setParameterValues(verbose, world, inputs); - if (constrain)p.constrainParameterValues(); - } else { - std::cout << "ERROR: " << getName() << ": parameter count mismatch. Perhaps your binary plugins and SC sources are different versions\n"; - //TODO: work out how to bring any further work to a halt - } + p.template setParameterValues(verbose, world, inputs); + if(inputs.remain() > 0) std::cout << "WARNING: "<< getName() << " received " << inputs.remain() << " more parameters than expected. Perhaps your binary plugins and SC sources are different versions\n"; + if (constrain) p.constrainParameterValues(); return p; } From 04a4ea890ad42eaf759d3e925f84258cbdae5c0d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:13:17 +0100 Subject: [PATCH 013/550] SC Wrapper: handling for shared client types from incoming string --- include/FluidSCWrapper.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 0615174..e469cee 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -443,6 +443,14 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } + + template + static std::enable_if_t::value,P> + fromArgs(World *w, ArgType args, P&, int) + { + return {fromArgs(w, args, std::string{}, 0).c_str()}; + } + }; From b2a4f32264b9bec4fb2b63cfc4cd0346eea6533c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:14:00 +0100 Subject: [PATCH 014/550] SC Wrapper: Handling for heterogenous return types from messages --- include/FluidSCWrapper.hpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index e469cee..07b8d49 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -512,6 +512,20 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template static size_t allocSize(FluidTensor s) { return s.size() ; } + template + static std::tuple,size_t> allocSize(std::tuple&& t) + { + return allocSizeImpl(std::forward(t), std::index_sequence_for()); + }; + + template + static std::tuple,size_t> allocSizeImpl(std::tuple&& t,std::index_sequence) + { + size_t size{0}; + std::array res; + (void)std::initializer_list{(res[Is] = size,size += ToFloatArray::allocSize(std::get(t)),0)...}; + return std::make_tuple(res,size); //array of offsets into allocated buffer & total number of floats to alloc + }; static void convert(float* f, typename BufferT::type buf) { f[0] = static_cast(buf.get())->bufnum(); } @@ -539,6 +553,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static_assert(std::is_convertible::value,"Can't convert this to float output"); std::copy(s.begin(), s.end(), f); } + + template + static void convert(float* f,std::tuple&& t, std::array offsets, std::index_sequence) + { + (void)std::initializer_list{(convert(f + offsets[Is],std::get(t)),0)...}; + } }; @@ -645,6 +665,19 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); } + template + static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult>& result) + { + auto ft = getInterfaceTable(); + std::array offsets; + size_t numArgs; + std::tie(offsets,numArgs) = ToFloatArray::allocSize(static_cast>(result)); + float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); + ToFloatArray::convert(values,std::tuple(result),offsets,std::index_sequence_for()); + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); + } + + public: using Client = C; From 6073ccadaf56e50be6ff97190d397858c522d8b9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:20:30 +0100 Subject: [PATCH 015/550] Add compile-bundled targets for FluidManipulation objects (needed to share memory space between shared clients) Add compile-bundled Subscriber-Provider Tests target Add SC test files for Messaging, Subscriber Provider --- .../Classes/FluidMessageResponse.sc | 32 +++++ release-packaging/Classes/FluidMessageTest.sc | 120 ++++++++++++++++++ .../Classes/FluidProviderTest.sc | 56 ++++++++ .../Classes/FluidSubscriberTest.sc | 40 ++++++ src/FluidManipulation/CMakeLists.txt | 20 +++ src/FluidManipulation/FluidManipulation.cpp | 14 ++ .../CMakeLists.txt | 20 +++ .../FluidSubscriberProviderTest.cpp | 17 +++ tests/TestFluiSubscriberProvider.scd | 12 ++ tests/TestFluidMessageTest..scd | 3 + 10 files changed, 334 insertions(+) create mode 100644 release-packaging/Classes/FluidMessageResponse.sc create mode 100644 release-packaging/Classes/FluidMessageTest.sc create mode 100644 release-packaging/Classes/FluidProviderTest.sc create mode 100644 release-packaging/Classes/FluidSubscriberTest.sc create mode 100644 src/FluidManipulation/CMakeLists.txt create mode 100644 src/FluidManipulation/FluidManipulation.cpp create mode 100644 src/FluidSubscriberProviderTest/CMakeLists.txt create mode 100644 src/FluidSubscriberProviderTest/FluidSubscriberProviderTest.cpp create mode 100644 tests/TestFluiSubscriberProvider.scd diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc new file mode 100644 index 0000000..5f3782d --- /dev/null +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -0,0 +1,32 @@ +FluidMessageResponse : Object +{ + //selectors is an array of functions + //my cunning thought is that those that need extra data (e..g numbers()) can + //use partial applicaiton + *collectArgs{ |selectors,a| + var response = []; + var idx = 0; + selectors.do{ |selector| + var newThings; + # newThings,idx = selector.value(a, idx); + response = response.add(newThings); + }; + ^response + } + + *string{ |a, offset| + var split = a.find([0],offset); + var res; + if(split.isNil) {"ERROR: can't parse string from server".throw}; + ^[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInt.asAscii},String), split + 1] + } + + *numbers{ |a, n, offset| + ^[a.copyRange(offset, offset + n),offset + n] + } + + *buffer{ |a,server,offset| + server = server ? Server.default ; + ^[Buffer.cachedBufferAt(server, a[offset]), offset + 1] + } +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidMessageTest.sc b/release-packaging/Classes/FluidMessageTest.sc new file mode 100644 index 0000000..f440d37 --- /dev/null +++ b/release-packaging/Classes/FluidMessageTest.sc @@ -0,0 +1,120 @@ +FluidMessageTest : UGen { + + var server; + + *kr{ |doneAction = 0| + + ^this.multiNew('control', doneAction); + } + + testReturnStrings { |server, nodeID, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnStrings'); + + OSCFunc( + { |msg| + var resp =FluidMessageResponse.collectArgs( + 4.collect{string(FluidMessageResponse,_,_)}, msg.drop(3)); + if(action.notNil){action.value(resp);}; + },'/testReturnStrings').oneShot; + + } + + testReturnNumbers{ |server, nodeID, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnNumbers'); + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs( + [numbers(FluidMessageResponse,_,100,_)], msg.drop(3)); + if(action.notNil){action.value(result);}; + },'/testReturnNumbers').oneShot; + } + + testReturnOneString{ |server, nodeID, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnOneString'); + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs( + [string(FluidMessageResponse,_,_)], msg.drop(3)); + if(action.notNil){action.value(result);}; + },'/testReturnOneString').oneShot; + } + + testReturnOneNumber{ |server, nodeID, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnOneNumber'); + + OSCFunc( + { |msg| + var result = msg.drop(3); + if(action.notNil){action.value(result);}; + },'/testReturnOneNumber').oneShot; + } + + testAccessBuffer{ |server, nodeID, buf, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testAccessBuffer', buf.asUGenInput); + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs([numbers(FluidMessageResponse,_,1,_)],msg.drop(3)); + if(action.notNil){action.value(result);}; + },'/testAccessBuffer').oneShot; + } + + testPassString{ |server, nodeID, str, a, b, c, d, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testPassString', str, a, b, c); + + OSCFunc( + { |msg| + if(action.notNil){action.value;}; + },'/testPassString').oneShot; + } + + + testReturnBuffer{ |server, nodeID, b, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnBuffer', b.asUGenInput); + + OSCFunc( + { |msg| + var result = result = FluidMessageResponse.collectArgs([buffer(FluidMessageResponse,_,server,_)],msg.drop(3)); + if(action.notNil){action.value(result);}; + },'/testReturnBuffer').oneShot; + } + + testReturnHetero{ |server, nodeID, action| + + server = server ? Server.default; + + server.sendMsg('/u_cmd',nodeID,this.synthIndex,'testReturnHetero'); + + OSCFunc( + { |msg| + var result = result = FluidMessageResponse.collectArgs([string(FluidMessageResponse,_,_), numbers(FluidMessageResponse,_,2,_)],msg.drop(3)); + if(action.notNil){action.value(result);}; + },'/testReturnHetero').oneShot; + } + + + +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidProviderTest.sc b/release-packaging/Classes/FluidProviderTest.sc new file mode 100644 index 0000000..d9589b2 --- /dev/null +++ b/release-packaging/Classes/FluidProviderTest.sc @@ -0,0 +1,56 @@ +FluidProviderTest : UGen { + +/* var <> server; + var <> nodeID; + + *new{ |server, name| + + + + }*/ + + *kr{ |name| + ^this.multiNew('control',name); + } + + *new1 { |rate, name| + var ascii = name.ascii; + ^super.new1(*[rate, ascii.size].addAll(ascii)); + } + + init { |size...chars| + //Send the number of inputs (size of provider string) as specialIndex, + //so server plugin knows what's going onnode + specialIndex = -1; + inputs = [size].addAll(chars); + } + + addPoint{|server, nodeID, args, action| + this.pr_sendMsg(server, nodeID, 'addPoint',args,action); + } + + updatePoint{|server, nodeID, args, action| + this.pr_sendMsg(server, nodeID, 'updatePoint',args,action); + } + + deletePoint{|server, nodeID, args, action| + this.pr_sendMsg(server,nodeID, 'deletePoint',args,action); + } + + pr_sendMsg { |server, nodeID, msg, args, action,parser| + + server = server ? Server.default; + + server.listSendMsg(['/u_cmd',nodeID.nodeID,this.synthIndex,msg].addAll(args)); + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + } + + + + +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidSubscriberTest.sc b/release-packaging/Classes/FluidSubscriberTest.sc new file mode 100644 index 0000000..8ea6070 --- /dev/null +++ b/release-packaging/Classes/FluidSubscriberTest.sc @@ -0,0 +1,40 @@ +FluidSubscriberTest : UGen { + + var <> providerName; + var <> nodeID; + + *kr { |provider| + ^this.multiNew('control',provider); + } + + *new1 { |rate, provider| + var ascii = provider.ascii; + ^super.new1(*[rate, ascii.size].addAll(ascii)); + } + + init { |size...chars| + //Send the number of inputs (size of provider string) as specialIndex, + //so server plugin knows what's going on + specialIndex = -1; + inputs = [size].addAll(chars); + providerName = chars.collectAs({|x|x.asInt.asAscii}, String); + } + + providerLookup { |server, nodeID, label, action| + this.pr_sendMsg(server, nodeID, 'providerLookup', label, action, + [string(FluidMessageResponse,_,_),numbers(FluidMessageResponse,_,2,_)] ); + } + + pr_sendMsg { |server, nodeID, msg, args, action,parser| + + server = server ? Server.default; + + server.listSendMsg(['/u_cmd',nodeID.nodeID,this.synthIndex,msg].addAll(args)); + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + } +} \ No newline at end of file diff --git a/src/FluidManipulation/CMakeLists.txt b/src/FluidManipulation/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidManipulation/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp new file mode 100644 index 0000000..64738d2 --- /dev/null +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -0,0 +1,14 @@ + +// 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 +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidAmpSlice", ft); +} diff --git a/src/FluidSubscriberProviderTest/CMakeLists.txt b/src/FluidSubscriberProviderTest/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidSubscriberProviderTest/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidSubscriberProviderTest/FluidSubscriberProviderTest.cpp b/src/FluidSubscriberProviderTest/FluidSubscriberProviderTest.cpp new file mode 100644 index 0000000..224dadf --- /dev/null +++ b/src/FluidSubscriberProviderTest/FluidSubscriberProviderTest.cpp @@ -0,0 +1,17 @@ + +// 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 +#include +#include + +static InterfaceTable *ft; + +PluginLoad(FluidSTFTUGen) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidProviderTest", ft); + makeSCWrapper("FluidSubscriberTest", ft); + +} diff --git a/tests/TestFluiSubscriberProvider.scd b/tests/TestFluiSubscriberProvider.scd new file mode 100644 index 0000000..b8f1652 --- /dev/null +++ b/tests/TestFluiSubscriberProvider.scd @@ -0,0 +1,12 @@ + +~provider +~subscriber +a = {~subscriber = FluidSubscriberTest.kr(\bollocks)}.play +b = {~provider = FluidProviderTest.kr(\bollocks)}.play +~provider.addPoint(Server.default,b,['foo', 1,2],{'Added'.postln}) +~subscriber.providerLookup(Server.default,a,['foo'],{|msg| msg.postln}) +~provider.updatePoint(Server.default,b,['foo', 3,4],{'Upadted'.postln}) +~subscriber.providerLookup(Server.default,a,['foo'],{|msg| msg.postln}) +~provider.deletePoint(Server.default,b,['foo'],{'Deleted'.postln}) +~subscriber.providerLookup(Server.default,a,['foo'],{|msg| msg.postln}) + diff --git a/tests/TestFluidMessageTest..scd b/tests/TestFluidMessageTest..scd index 63b0218..54f4a14 100644 --- a/tests/TestFluidMessageTest..scd +++ b/tests/TestFluidMessageTest..scd @@ -8,6 +8,9 @@ b = Buffer.read(s,File.realpath(FluidMessageTest.class.filenameSymbol).dirname.w ~messageTest.testAccessBuffer(nil,a.nodeID,b,{|msg| msg.postln}); b.numFrames ~messageTest.testPassString(nil,a.nodeID,'hello, you big lovely server',1,2,3,{'testPassString Done'.postln}); +~messageTest.testReturnBuffer(nil,a.nodeID,b,{|buffer| b.numFrames.postln}); +~messageTest.testReturnHetero(nil,a.nodeID,{|x|x.postln}); +a.free // OSCFunc.trace(true) // OSCFunc.trace(false) \ No newline at end of file From 80af9ca9ea58b261256c01a00f8631ca3882b273 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 16 Aug 2019 10:21:07 +0100 Subject: [PATCH 016/550] Proper FluidManipulaitonTarget --- src/FluidManipulation/FluidManipulation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 64738d2..20c651e 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -1,7 +1,8 @@ // 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 +#include +#include #include static InterfaceTable *ft; @@ -10,5 +11,6 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidAmpSlice", ft); + makeSCWrapper("FluidDataset", ft); + makeSCWrapper("FluidCorpus", ft); } From 3d599202a55b4c59cab52678d0237fb0d6c8c7d3 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sat, 16 Nov 2019 10:25:51 +0000 Subject: [PATCH 017/550] scripts/target_post.cmake: Add FluidManipulation 3rd party includes to path (will be part of FluidManipulation cmake in due course) --- scripts/target_post.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index ea4798e..a211e05 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -26,8 +26,15 @@ target_link_libraries( target_include_directories( ${PLUGIN} PRIVATE - ${LOCAL_INCLUDES} + "${LOCAL_INCLUDES}" "${FLUID_M_PATH}/include/" + "${FLUID_M_PATH}/thirdparty" +) + +file(GLOB_RECURSE FLUID_MANIPULATION_HEADERS CONFIGURE_DEPENDS "${FLUID_M_PATH}/include/**/*.hpp") + +target_sources( + ${PLUGIN} PUBLIC ${FLUID_MANIPULATION_HEADERS} ) target_include_directories( From 26b341f109e8c3cbfa5f42ce56cfb35444943c57 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sat, 16 Nov 2019 10:26:24 +0000 Subject: [PATCH 018/550] src/FluidManipulation/FluidManipulation.cpp: Update incluedes and object defs --- src/FluidManipulation/FluidManipulation.cpp | 24 ++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 20c651e..01debd8 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -1,8 +1,17 @@ // 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 #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include static InterfaceTable *ft; @@ -11,6 +20,15 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidDataset", ft); - makeSCWrapper("FluidCorpus", ft); + makeSCWrapper("FluidDataSet",ft); + makeSCWrapper("FluidLabelSet",ft); + makeSCWrapper("FluidKDTree",ft); + makeSCWrapper("FluidKMeans",ft); + makeSCWrapper("FluidKNNClassifier",ft); + makeSCWrapper("FluidKNNRegressor",ft); + makeSCWrapper("FluidKNN",ft); + makeSCWrapper("FluidNormalize",ft); + makeSCWrapper("FluidStandardize",ft); + makeSCWrapper("FluidAudioTransport",ft); + makeSCWrapper("FluidBufAudioTransport",ft); } From 0d99d71246d63d46ce3ac22b6f899de788788d91 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 2 Oct 2019 16:10:13 +0100 Subject: [PATCH 019/550] Messages that have side effects on buffers need to make sure they do the nrt->rt buffer swap thing --- include/FluidSCWrapper.hpp | 43 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 07b8d49..88bbea0 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -24,6 +24,26 @@ class FluidSCWrapper; namespace impl { + template + struct AssignBuffer + { + void operator()(const typename BufferT::type &p, World *w) + { + if (auto b = static_cast(p.get())) + b->assignToRT(w); + } + }; + + template + struct CleanUpBuffer + { + void operator()(const typename BufferT::type &p) + { + if (auto b = static_cast(p.get())) b->cleanUp(); + } + }; + + // Iterate over kr/ir inputs via callbacks from params object struct FloatControlsIter { @@ -295,33 +315,17 @@ private: bool exchangeBuffers(World *world) { - mParams.template forEachParamType(world); + mParams.template forEachParamType(world); return true; } bool tidyUp(World *) { - mParams.template forEachParamType(); + mParams.template forEachParamType(); return true; } - template - struct AssignBuffer - { - void operator()(const typename BufferT::type &p, World *w) - { - if (auto b = static_cast(p.get())) b->assignToRT(w); - } - }; - template - struct CleanUpBuffer - { - void operator()(const typename BufferT::type &p) - { - if (auto b = static_cast(p.get())) b->cleanUp(); - } - }; FloatControlsIter mControlsIterator; FifoMsg mFifoMsg; @@ -627,9 +631,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase } return true; }, - [](World*, void* data) //RT thread: response + [](World* world, void* data) //RT thread: response { MessageData* m = static_cast(data); + MessageDescriptor::template forEachArg(m->args, world); messageOutput(m->wrapper,m->name,m->result); return true; } From 676ac97aa5b37b2cd7553bddd72e3a16093d3282 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 24 Nov 2019 23:48:33 +0000 Subject: [PATCH 020/550] release-packaging/Classes/FluidDataSet.sc : first reasonble dataset client --- release-packaging/Classes/FluidDataSet.sc | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 release-packaging/Classes/FluidDataSet.sc diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc new file mode 100644 index 0000000..41afa2a --- /dev/null +++ b/release-packaging/Classes/FluidDataSet.sc @@ -0,0 +1,79 @@ +FluidDataSet : UGen { + + var <> synth, <> server; + + *kr{ |name, dims| + ^this.multiNew('control',name, dims); + } + + *new{ |server, name, dims| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidDataSet.kr(name,dims)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + *new1 { |rate, name, dims| + var ascii = name.ascii; + ^super.new1(*[rate, ascii.size].addAll(ascii)++dims); + } + + init { |size...chars| + //Send the number of inputs (size of provider string) as specialIndex, + //so server plugin knows what's going on + specialIndex = -1; + inputs = [size].addAll(chars); + } + + addPoint{|label, buffer, action| + this.pr_sendMsg(this.server, this.synth, 'addPoint',[label,buffer.asUGenInput],action); + } + + getPoint{|label, buffer, action| + this.pr_sendMsg(this.server, this.synth, 'getPoint',[label,buffer.asUGenInput],action); + } + + updatePoint{|label, buffer, action| + this.pr_sendMsg(this.server, this.synth, 'updatePoint',[label,buffer.asUGenInput],action); + } + + deletePoint{|label,buffer action| + this.pr_sendMsg(this.server, this.synth, 'deletePoint',[label,buffer.asUGenInput],action); + } + + cols {|action| + this.pr_sendMsg(this.server, this.synth, \cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename,action| + this.pr_sendMsg(this.server, this.synth, \read,[filename],action); + } + + write{|filename,action| + this.pr_sendMsg(this.server, this.synth, \write,[filename],action); + } + + size { |action| + this.pr_sendMsg(this.server, this.synth, \size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + clear { |server,node,action| + this.pr_sendMsg(server, node, \clear,[],action); + } + + pr_sendMsg { |server, node, msg, args, action,parser| + + server = server ? Server.default; + + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(result.notNil){action.value(result)}{action.value}; + },'/'++msg + ).oneShot; + + server.listSendMsg(['/u_cmd',node.nodeID,this.synthIndex,msg].addAll(args)); + } +} \ No newline at end of file From 6779b8077b798c05a97157037522492b260e6fbf Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 00:00:31 +0000 Subject: [PATCH 021/550] denoise dataset client --- release-packaging/Classes/FluidDataSet.sc | 27 ++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 41afa2a..7b1503d 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -28,45 +28,42 @@ FluidDataSet : UGen { } addPoint{|label, buffer, action| - this.pr_sendMsg(this.server, this.synth, 'addPoint',[label,buffer.asUGenInput],action); + this.pr_sendMsg(\addPoint,[label,buffer.asUGenInput],action); } getPoint{|label, buffer, action| - this.pr_sendMsg(this.server, this.synth, 'getPoint',[label,buffer.asUGenInput],action); + this.pr_sendMsg(\getPoint,[label,buffer.asUGenInput],action); } updatePoint{|label, buffer, action| - this.pr_sendMsg(this.server, this.synth, 'updatePoint',[label,buffer.asUGenInput],action); + this.pr_sendMsg(\updatePoint,[label,buffer.asUGenInput],action); } deletePoint{|label,buffer action| - this.pr_sendMsg(this.server, this.synth, 'deletePoint',[label,buffer.asUGenInput],action); + this.pr_sendMsg(\deletePoint,[label,buffer.asUGenInput],action); } cols {|action| - this.pr_sendMsg(this.server, this.synth, \cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } read{|filename,action| - this.pr_sendMsg(this.server, this.synth, \read,[filename],action); + this.pr_sendMsg(\read,[filename],action); } write{|filename,action| - this.pr_sendMsg(this.server, this.synth, \write,[filename],action); + this.pr_sendMsg(\write,[filename],action); } size { |action| - this.pr_sendMsg(this.server, this.synth, \size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); } - clear { |server,node,action| - this.pr_sendMsg(server, node, \clear,[],action); + clear { |action| + this.pr_sendMsg(\clear,[],action); } - pr_sendMsg { |server, node, msg, args, action,parser| - - server = server ? Server.default; - + pr_sendMsg { |msg, args, action,parser| OSCFunc( { |msg| var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); @@ -74,6 +71,6 @@ FluidDataSet : UGen { },'/'++msg ).oneShot; - server.listSendMsg(['/u_cmd',node.nodeID,this.synthIndex,msg].addAll(args)); + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); } } \ No newline at end of file From c83fbd7b5497ebe5521451c56c024d611f504180 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 00:02:15 +0000 Subject: [PATCH 022/550] KDTree SC class --- release-packaging/Classes/FluidKDTree.sc | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 release-packaging/Classes/FluidKDTree.sc diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc new file mode 100644 index 0000000..bcd96a1 --- /dev/null +++ b/release-packaging/Classes/FluidKDTree.sc @@ -0,0 +1,54 @@ +FluidKDTree : UGen { + + var <> synth, <> server; + + *kr { + ^this.multiNew('control'); + } + + *new{ |server, name, dims| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidKDTree.kr(name,dims)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + index{|dataset,action| + this.pr_sendMsg(\index,[dataset],action); + } + + kNearest{ |buffer, k,action| + this.pr_sendMsg(\kNearest,[buffer.asUGenInput,k],action,k.collect{string(FluidMessageResponse,_,_)}); + } + + kNearestDist { |buffer, k,action| + this.pr_sendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,3,_)]); + } + + cols { |action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{ |filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{ |filename,action| + this.pr_sendMsg(\write,[filename],action); + } + + pr_sendMsg { |msg, args, action,parser| + + OSCFunc( + { |msg| + var result; + // msg.postln; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} From 50bb88ae141aaab20f4b397f8e0d7cf2124aba98 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 12:01:35 +0000 Subject: [PATCH 023/550] release-packaging/Classes/FluidDataSet.sc: missing comma in arg list --- release-packaging/Classes/FluidDataSet.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 7b1503d..3a9a65b 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -39,7 +39,7 @@ FluidDataSet : UGen { this.pr_sendMsg(\updatePoint,[label,buffer.asUGenInput],action); } - deletePoint{|label,buffer action| + deletePoint{|label,buffer, action| this.pr_sendMsg(\deletePoint,[label,buffer.asUGenInput],action); } From de9447e3a5150dda5096a2b735b997156da6d584 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 12:06:00 +0000 Subject: [PATCH 024/550] release-packaging/Classes/FluidKDTree.sc: Extra args in new (how did this ever work?) --- release-packaging/Classes/FluidKDTree.sc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index bcd96a1..c2ba2c3 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -6,10 +6,10 @@ FluidKDTree : UGen { ^this.multiNew('control'); } - *new{ |server, name, dims| + *new{ |server| var synth, instance; server = server ? Server.default; - synth = {instance = FluidKDTree.kr(name,dims)}.play(server); + synth = {instance = FluidKDTree.kr()}.play(server); instance.server = server; instance.synth = synth; ^instance From 6a9febafea6692b767bd22e6e77fd2eee90de668 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 13:23:25 +0000 Subject: [PATCH 025/550] src/FluidManipulation/FluidManipulation.cpp: Remove non-existent clients --- src/FluidManipulation/FluidManipulation.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 01debd8..cc767b6 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -1,13 +1,10 @@ // 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 #include #include #include #include -#include -#include #include #include #include @@ -24,8 +21,6 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidLabelSet",ft); makeSCWrapper("FluidKDTree",ft); makeSCWrapper("FluidKMeans",ft); - makeSCWrapper("FluidKNNClassifier",ft); - makeSCWrapper("FluidKNNRegressor",ft); makeSCWrapper("FluidKNN",ft); makeSCWrapper("FluidNormalize",ft); makeSCWrapper("FluidStandardize",ft); From 7aa5705bc11410717a4978f58b60331bca635ac9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 25 Nov 2019 13:24:00 +0000 Subject: [PATCH 026/550] =?UTF-8?q?FluidManipulation:=20Remaining=20SClang?= =?UTF-8?q?=20class=20stubs=20=E2=80=93=C2=A0NEEDS=20TESTING?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Classes/FluidAudioTransport.sc | 5 ++ .../Classes/FluidBufAudioTransport.sc | 58 ++++++++++++++ release-packaging/Classes/FluidKMeans.sc | 58 ++++++++++++++ release-packaging/Classes/FluidKNN.sc | 42 ++++++++++ release-packaging/Classes/FluidLabelSet.sc | 76 +++++++++++++++++++ release-packaging/Classes/FluidNRTProcess.sc | 56 ++++++++++++++ release-packaging/Classes/FluidNormalize.sc | 52 +++++++++++++ release-packaging/Classes/FluidStandardize.sc | 52 +++++++++++++ 8 files changed, 399 insertions(+) create mode 100644 release-packaging/Classes/FluidAudioTransport.sc create mode 100644 release-packaging/Classes/FluidBufAudioTransport.sc create mode 100644 release-packaging/Classes/FluidKMeans.sc create mode 100644 release-packaging/Classes/FluidKNN.sc create mode 100644 release-packaging/Classes/FluidLabelSet.sc create mode 100644 release-packaging/Classes/FluidNRTProcess.sc create mode 100644 release-packaging/Classes/FluidNormalize.sc create mode 100644 release-packaging/Classes/FluidStandardize.sc diff --git a/release-packaging/Classes/FluidAudioTransport.sc b/release-packaging/Classes/FluidAudioTransport.sc new file mode 100644 index 0000000..c14e74b --- /dev/null +++ b/release-packaging/Classes/FluidAudioTransport.sc @@ -0,0 +1,5 @@ +FluidAudioTransport : UGen { + *ar { arg in = 0, interpolation=0.0, bandwidth=255,windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize) + } +} diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc new file mode 100644 index 0000000..1268adc --- /dev/null +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -0,0 +1,58 @@ +FluidBufAudioTransport : UGen{ + + var <>synth, <>server; + + *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + source1 = source1.asUGenInput; + source2 = source2.asUGenInput; + + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + destination = destination.asUGenInput; + //NB For wrapped versions of NRT classes, we set the params for maxima to + //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) + + ^this.multiNew(\control, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction); + } + + + *process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action| + var synth, instance; + + + source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; + source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + + + source1 = source1.asUGenInput; + source2 = source2.asUGenInput; + destination = destination.asUGenInput; + + server = server ? Server.default; + server.ifNotRunning({ + "WARNING: Server not running".postln; + ^nil; + }); + synth = { instance = FluidBufSines.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); + forkIfNeeded{ + synth.waitForFree; + server.sync; + if (destination != -1) {destination = server.cachedBufferAt(destination); destination.updateInfo; server.sync;} {destination = nil}; + action.value(destination); + }; + instance.synth = synth; + instance.server = server; + ^instance; + } + + cancel{ + if(this.server.notNil) + {this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; + } +} diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc new file mode 100644 index 0000000..08d315e --- /dev/null +++ b/release-packaging/Classes/FluidKMeans.sc @@ -0,0 +1,58 @@ +FluidKMeans : UGen { + + var <> synth, <> server; + + *kr { + ^this.multiNew('control'); + } + + *new{ |server| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidKMeans.kr()}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + train{|dataset,k, maxIter = 100, buffer, action| + + buffer = buffer ? -1; + + this.pr_sendMsg(\train,[dataset, k,maxIter, buffer.asUGenInput],action); + } + + cluster{ |dataset, labelset, k, maxIter=100, buffer,action| + buffer = buffer ? -1; + this.pr_sendMsg(\cluster,[dataset, labelset, k, maxIter, buffer.asUGenInput],action,k.collect{string(FluidMessageResponse,_,_)}); + } + + predict { |buffer, action| + this.pr_sendMsg(\predict,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + cols { |action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{ |filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{ |filename,action| + this.pr_sendMsg(\write,[filename],action); + } + + pr_sendMsg { |msg, args, action,parser| + + OSCFunc( + { |msg| + var result; + // msg.postln; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc new file mode 100644 index 0000000..0f00935 --- /dev/null +++ b/release-packaging/Classes/FluidKNN.sc @@ -0,0 +1,42 @@ +FluidKNN : UGen { + + var <> synth, <> server; + + *kr { |dims,k| + ^this.multiNew('control'); + } + + *new{ |server,dims,k| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidKNN.kr(dims,k)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + index{|dataset, action| + this.pr_sendMsg(\index,[dataset],action); + } + + classify{ |buffer, labelset, k, action| + this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset, k],action,k.collect{string(FluidMessageResponse,_,_)}); + } + + regress { |buffer,dataset, k, action| + this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset,k],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + pr_sendMsg { |msg, args, action,parser| + + OSCFunc( + { |msg| + var result; + // msg.postln; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc new file mode 100644 index 0000000..85daa86 --- /dev/null +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -0,0 +1,76 @@ +FluidLabelSet : UGen { + + var <> synth, <> server; + + *kr{ |name| + ^this.multiNew('control',name); + } + + *new{ |server, name| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidLabelSet.kr(name)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + *new1 { |rate, name| + var ascii = name.ascii; + ^super.new1(*[rate, ascii.size].addAll(ascii)); + } + + init { |size...chars| + //Send the number of inputs (size of provider string) as specialIndex, + //so server plugin knows what's going on + specialIndex = -1; + inputs = [size].addAll(chars); + } + + addPoint{|id, label, action| + this.pr_sendMsg(\addPoint,[id, label],action); + } + + getPoint{|id, action| + this.pr_sendMsg(\getPoint,[id],action); + } + + updatePoint{|id, label, action| + this.pr_sendMsg(\updatePoint,[id, label],action); + } + + deletePoint{|id, action| + this.pr_sendMsg(\deletePoint,[id],action); + } + + cols {|action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{|filename,action| + this.pr_sendMsg(\write,[filename],action); + } + + size { |action| + this.pr_sendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + clear { |action| + this.pr_sendMsg(\clear,[],action); + } + + pr_sendMsg { |msg, args, action,parser| + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(result.notNil){action.value(result)}{action.value}; + },'/'++msg + ).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidNRTProcess.sc b/release-packaging/Classes/FluidNRTProcess.sc new file mode 100644 index 0000000..35e8ce4 --- /dev/null +++ b/release-packaging/Classes/FluidNRTProcess.sc @@ -0,0 +1,56 @@ +FluidNRTProcess : Object{ + var synth, <> server; + + *kr{ |min, max| + ^this.multiNew('control',min, max); + } + + *new{ |server, min, max| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidNormalize.kr(min,max)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + fit{|dataset, action| + this.pr_sendMsg(\fit,[dataset],action); + } + + normalize{|sourceDataset, destDataset, action| + this.pr_sendMsg(\normalize,[sourceDataset, destDataset],action); + } + + normalizePoint{|sourceBuffer, destBuffer, action| + this.pr_sendMsg(\normalizePoint,[sourceBuffer, destBuffer],action); + } + + cols {|action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{|filename,action| + this.pr_sendMsg(\write,[filename],action); + } + + pr_sendMsg { |msg, args, action,parser| + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(result.notNil){action.value(result)}{action.value}; + },'/'++msg + ).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc new file mode 100644 index 0000000..019646d --- /dev/null +++ b/release-packaging/Classes/FluidStandardize.sc @@ -0,0 +1,52 @@ +FluidStandardize : UGen { + + var <> synth, <> server; + + *kr{ + ^this.multiNew('control'); + } + + *new{ |server| + var synth, instance; + server = server ? Server.default; + synth = {instance = FluidStandardize.kr}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + fit{|dataset, action| + this.pr_sendMsg(\fit,[dataset],action); + } + + standardize{|sourceDataset, destDataset, action| + this.pr_sendMsg(\standardize,[sourceDataset, destDataset],action); + } + + standardizePoint{|sourceBuffer, destBuffer, action| + this.pr_sendMsg(\standardizePoint,[sourceBuffer, destBuffer],action); + } + + cols {|action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{|filename,action| + this.pr_sendMsg(\write,[filename],action); + } + + pr_sendMsg { |msg, args, action,parser| + OSCFunc( + { |msg| + var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(result.notNil){action.value(result)}{action.value}; + },'/'++msg + ).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} \ No newline at end of file From 7aea07c6217f09b224c217179a3a6bfd14e716d1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 11:41:19 +0000 Subject: [PATCH 027/550] Add helpful base client to reduce boiler plate --- .../Classes/FluidManipulationClient.sc | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 release-packaging/Classes/FluidManipulationClient.sc diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc new file mode 100644 index 0000000..3f0740a --- /dev/null +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -0,0 +1,32 @@ +FluidManipulationClient : UGen { + + const < nonBlocking = 0; + var <> synth, <> server; + + *kr { + ^this.multiNew('control', Done.none, nonBlocking); + } + + *new{ |server...args| + var synth, instance; + server = server ? Server.default; + if(server.serverRunning.not,{("ERROR:" + this.asString + "– server not running").postln; ^nil}); + synth = {instance = this.kr(*args)}.play(server); + instance.server = server; + instance.synth = synth; + ^instance + } + + pr_sendMsg { |msg, args, action,parser| + + OSCFunc( + { |msg| + var result; + // msg.postln; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + },'/'++msg).oneShot; + + this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + } +} From c742f4d14e3905f8219f1d3a8dbf869a8a2269a8 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 11:41:50 +0000 Subject: [PATCH 028/550] TB2 classes: Reduce boiler plate --- release-packaging/Classes/FluidDataSet.sc | 38 +++++-------------- release-packaging/Classes/FluidKDTree.sc | 33 ++-------------- release-packaging/Classes/FluidKMeans.sc | 30 +++++++-------- release-packaging/Classes/FluidKNN.sc | 33 +++------------- release-packaging/Classes/FluidLabelSet.sc | 36 ++++-------------- release-packaging/Classes/FluidNormalize.sc | 31 +++------------ release-packaging/Classes/FluidStandardize.sc | 33 ++-------------- 7 files changed, 48 insertions(+), 186 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 3a9a65b..bba6894 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -1,30 +1,20 @@ -FluidDataSet : UGen { +FluidDataSet : FluidManipulationClient { - var <> synth, <> server; + var <>synth, <>server, <>id; *kr{ |name, dims| - ^this.multiNew('control',name, dims); + ^this.new1('control',name, dims); } - *new{ |server, name, dims| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidDataSet.kr(name,dims)}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } - - *new1 { |rate, name, dims| + init { |name, dims| var ascii = name.ascii; - ^super.new1(*[rate, ascii.size].addAll(ascii)++dims); + this.id = name; + // specialIndex = -1; + inputs = [ascii.size].addAll(ascii)++dims++Done.none++FluidManipulationClient.nonBlocking; } - init { |size...chars| - //Send the number of inputs (size of provider string) as specialIndex, - //so server plugin knows what's going on - specialIndex = -1; - inputs = [size].addAll(chars); + asString { + ^id } addPoint{|label, buffer, action| @@ -63,14 +53,4 @@ FluidDataSet : UGen { this.pr_sendMsg(\clear,[],action); } - pr_sendMsg { |msg, args, action,parser| - OSCFunc( - { |msg| - var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(result.notNil){action.value(result)}{action.value}; - },'/'++msg - ).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } \ No newline at end of file diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index c2ba2c3..18f15a1 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -1,22 +1,7 @@ -FluidKDTree : UGen { - - var <> synth, <> server; - - *kr { - ^this.multiNew('control'); - } - - *new{ |server| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidKDTree.kr()}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } +FluidKDTree : FluidManipulationClient { index{|dataset,action| - this.pr_sendMsg(\index,[dataset],action); + this.pr_sendMsg(\index,[dataset.asString],action); } kNearest{ |buffer, k,action| @@ -24,7 +9,7 @@ FluidKDTree : UGen { } kNearestDist { |buffer, k,action| - this.pr_sendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,3,_)]); + this.pr_sendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,k,_)]); } cols { |action| @@ -39,16 +24,4 @@ FluidKDTree : UGen { this.pr_sendMsg(\write,[filename],action); } - pr_sendMsg { |msg, args, action,parser| - - OSCFunc( - { |msg| - var result; - // msg.postln; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(action.notNil){action.value(result)}{action.value}; - },'/'++msg).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 08d315e..69caa79 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -1,34 +1,34 @@ -FluidKMeans : UGen { +FluidKMeans : FluidManipulationClient { - var <> synth, <> server; + /* var <> synth, <> server,k; *kr { - ^this.multiNew('control'); + ^this.multiNew('control',Done.none); } - - *new{ |server| +*/ +/* *new{ |server| var synth, instance; server = server ? Server.default; + this.asString.postln; + if(server.serverRunning.not,{"ERROR: FluidKMeans – server not running".postln; ^nil}); synth = {instance = FluidKMeans.kr()}.play(server); instance.server = server; instance.synth = synth; ^instance - } - - train{|dataset,k, maxIter = 100, buffer, action| + }*/ + fit{|dataset,k, maxIter = 100, buffer, action| buffer = buffer ? -1; - - this.pr_sendMsg(\train,[dataset, k,maxIter, buffer.asUGenInput],action); + this.k = k; + this.pr_sendMsg(\fit,[dataset.asString, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); } - cluster{ |dataset, labelset, k, maxIter=100, buffer,action| - buffer = buffer ? -1; - this.pr_sendMsg(\cluster,[dataset, labelset, k, maxIter, buffer.asUGenInput],action,k.collect{string(FluidMessageResponse,_,_)}); + predict{ |dataset, labelset,action| + this.pr_sendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); } - predict { |buffer, action| - this.pr_sendMsg(\predict,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,1,_)]); + predictPoint { |buffer, action| + this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,1,_)]); } cols { |action| diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index 0f00935..464e7c9 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -1,42 +1,19 @@ -FluidKNN : UGen { - - var <> synth, <> server; +FluidKNN : FluidManipulationClient { *kr { |dims,k| - ^this.multiNew('control'); + ^this.multiNew('control', dims, k, Done.none, super.nonBlocking); } - *new{ |server,dims,k| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidKNN.kr(dims,k)}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } - index{|dataset, action| - this.pr_sendMsg(\index,[dataset],action); + this.pr_sendMsg(\index,[dataset.asUGenInput],action); } classify{ |buffer, labelset, k, action| - this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset, k],action,k.collect{string(FluidMessageResponse,_,_)}); + this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asUGenInput, k],action,k.collect{string(FluidMessageResponse,_,_)}); } regress { |buffer,dataset, k, action| - this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset,k],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset.asUGenInput,k],action,[numbers(FluidMessageResponse,_,1,_)]); } - pr_sendMsg { |msg, args, action,parser| - - OSCFunc( - { |msg| - var result; - // msg.postln; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(action.notNil){action.value(result)}{action.value}; - },'/'++msg).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 85daa86..e011802 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -1,30 +1,19 @@ -FluidLabelSet : UGen { +FluidLabelSet : FluidManipulationClient { - var <> synth, <> server; + var <> synth, <> server, id; *kr{ |name| ^this.multiNew('control',name); } - *new{ |server, name| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidLabelSet.kr(name)}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } - - *new1 { |rate, name| + init { |name| var ascii = name.ascii; - ^super.new1(*[rate, ascii.size].addAll(ascii)); + this.id = name; + inputs = [ascii.size].addAll(ascii)++Done.none++FluidManipulationClient.nonBlocking; } - init { |size...chars| - //Send the number of inputs (size of provider string) as specialIndex, - //so server plugin knows what's going on - specialIndex = -1; - inputs = [size].addAll(chars); + asString { + ^id; } addPoint{|id, label, action| @@ -62,15 +51,4 @@ FluidLabelSet : UGen { clear { |action| this.pr_sendMsg(\clear,[],action); } - - pr_sendMsg { |msg, args, action,parser| - OSCFunc( - { |msg| - var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(result.notNil){action.value(result)}{action.value}; - },'/'++msg - ).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } \ No newline at end of file diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 45205af..40fc0cc 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -1,30 +1,19 @@ -FluidNormalize : UGen { - - var <> synth, <> server; +FluidNormalize : FluidManipulationClient { *kr{ |min, max| - ^this.multiNew('control',min, max); + ^this.multiNew('control',min, max, Done.none, super.nonBlocking); } - *new{ |server, min, max| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidNormalize.kr(min,max)}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } - fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset],action); + this.pr_sendMsg(\fit,[dataset.asUGenInput],action); } normalize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\normalize,[sourceDataset, destDataset],action); + this.pr_sendMsg(\normalize,[sourceDataset.asUGenInput, destDataset.asUGenInput],action); } normalizePoint{|sourceBuffer, destBuffer, action| - this.pr_sendMsg(\normalizePoint,[sourceBuffer, destBuffer],action); + this.pr_sendMsg(\normalizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| @@ -39,14 +28,4 @@ FluidNormalize : UGen { this.pr_sendMsg(\write,[filename],action); } - pr_sendMsg { |msg, args, action,parser| - OSCFunc( - { |msg| - var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(result.notNil){action.value(result)}{action.value}; - },'/'++msg - ).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } \ No newline at end of file diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index 019646d..efa6bd9 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -1,30 +1,15 @@ -FluidStandardize : UGen { - - var <> synth, <> server; - - *kr{ - ^this.multiNew('control'); - } - - *new{ |server| - var synth, instance; - server = server ? Server.default; - synth = {instance = FluidStandardize.kr}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - } +FluidStandardize : FluidManipulationClient { fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset],action); + this.pr_sendMsg(\fit,[dataset.asUGenInput],action); } standardize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\standardize,[sourceDataset, destDataset],action); + this.pr_sendMsg(\standardize,[sourceDataset.asUGenInput, destDataset.asUGenInput],action); } standardizePoint{|sourceBuffer, destBuffer, action| - this.pr_sendMsg(\standardizePoint,[sourceBuffer, destBuffer],action); + this.pr_sendMsg(\standardizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| @@ -39,14 +24,4 @@ FluidStandardize : UGen { this.pr_sendMsg(\write,[filename],action); } - pr_sendMsg { |msg, args, action,parser| - OSCFunc( - { |msg| - var result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(result.notNil){action.value(result)}{action.value}; - },'/'++msg - ).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } \ No newline at end of file From 0e8d773a32bccb94eb768c94e31543909d0c94ba Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 14:03:02 +0000 Subject: [PATCH 029/550] Restore 'k' to KMeans (needed so we know how many things predict() will return) --- release-packaging/Classes/FluidKMeans.sc | 30 +----------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 69caa79..1a679cb 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -1,21 +1,6 @@ FluidKMeans : FluidManipulationClient { - /* var <> synth, <> server,k; - - *kr { - ^this.multiNew('control',Done.none); - } -*/ -/* *new{ |server| - var synth, instance; - server = server ? Server.default; - this.asString.postln; - if(server.serverRunning.not,{"ERROR: FluidKMeans – server not running".postln; ^nil}); - synth = {instance = FluidKMeans.kr()}.play(server); - instance.server = server; - instance.synth = synth; - ^instance - }*/ + var <>k; fit{|dataset,k, maxIter = 100, buffer, action| buffer = buffer ? -1; @@ -42,17 +27,4 @@ FluidKMeans : FluidManipulationClient { write{ |filename,action| this.pr_sendMsg(\write,[filename],action); } - - pr_sendMsg { |msg, args, action,parser| - - OSCFunc( - { |msg| - var result; - // msg.postln; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(action.notNil){action.value(result)}{action.value}; - },'/'++msg).oneShot; - - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); - } } From 89a22adb3cc320c961bf81c504bb7e2c6d4a1a6a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 14:03:33 +0000 Subject: [PATCH 030/550] Correct LabelSet interface --- release-packaging/Classes/FluidLabelSet.sc | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index e011802..1603c6f 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -1,6 +1,6 @@ FluidLabelSet : FluidManipulationClient { - var <> synth, <> server, id; + var <> synth, <> server, <>id; *kr{ |name| ^this.multiNew('control',name); @@ -16,20 +16,16 @@ FluidLabelSet : FluidManipulationClient { ^id; } - addPoint{|id, label, action| - this.pr_sendMsg(\addPoint,[id, label],action); + addLabel{|id, label, action| + this.pr_sendMsg(\addLabel,[id, label],action); } - getPoint{|id, action| - this.pr_sendMsg(\getPoint,[id],action); + getLabel{|id, action| + this.pr_sendMsg(\getLabel,[id],action,[string(FluidMessageResponse,_,_)]); } - updatePoint{|id, label, action| - this.pr_sendMsg(\updatePoint,[id, label],action); - } - - deletePoint{|id, action| - this.pr_sendMsg(\deletePoint,[id],action); + deleteLabel{|id, action| + this.pr_sendMsg(\deleteLabel,[id],action); } cols {|action| From 89c438e6044734e54e83152ec1d366d0769324f3 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 14:04:31 +0000 Subject: [PATCH 031/550] release-packaging/Classes/FluidManipulationClient.sc: Make sendMsg block when in a routine (so we can reliably dump the whole of a label set in sequence). I don't like this idiom though --- .../Classes/FluidManipulationClient.sc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 3f0740a..f20bf17 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -18,15 +18,24 @@ FluidManipulationClient : UGen { } pr_sendMsg { |msg, args, action,parser| + var c = Condition.new(false); OSCFunc( { |msg| - var result; - // msg.postln; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(action.notNil){action.value(result)}{action.value}; + forkIfNeeded{ + var result; + // msg.postln; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + this.server.sync; + c.test = true; + c.signal; + if(action.notNil){action.value(result)}{action.value}; + } },'/'++msg).oneShot; this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + + forkIfNeeded { c.wait }; + } } From eae5d3b12e343662a16fc56a96ebfdd95f71322a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 14:04:53 +0000 Subject: [PATCH 032/550] TB2 objects, baby examples for starters --- .../Examples/dataset/myfirstdataset.scd | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/myfirstdataset.scd diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd new file mode 100644 index 0000000..dbc8401 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd @@ -0,0 +1,92 @@ +// TB2 SC Playground V0 + +/* +Current stinkers: +1) Producing flat datapoints for FluidDataSet (i.e. flattening and cherry picking a multichannel buffer) takes bloody ages due to all the server syncing. I can't work out how to do it reliably outwith a Routine (which would certainly be quicker, to my mind) +2) Functions from the classes don't yet directly return things, and you have to access their return data through actions. This is partly because I don't know what the correct thing to do w/r/t blocking is, so I'm hoping GR will do it properly +*/ + +//STEP 0: start server +s.reboot; + +if(s.hasBooted.not){"Warning: server not running".postln}; + +//STEP 1: Get some files +( +// ~path = nil +// FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]}); +~path="/Users/owen/Documents/16bitBoxes/"; +~audioBuffers = SoundFile.collectIntoBuffers(~path+/+'*',s); +~lookup = Dictionary(n:~audioBuffers.size); +~audioBuffers.do{|b| ~lookup.add(b.path->b)} +) + +//STEP 2: Make a FluidDataSet +~dataset = FluidDataSet.new(s,"mfccs", 96) //12 dims * 4 stats * 2 derivatives +//STEP 3A: EITHER populate the dataset like so (and cry about how long the data point assembly takes) +Routine{ + ~audioBuffers.do{|b| + var tmpMFCCs = Buffer.new(s); + var tmpStats = Buffer.new(s); + var tmpFlat = Buffer.new(s,12 * 4 * 2, 1); + s.sync; + ("Analyzing" + b.path).postln; + FluidBufMFCC.process(s,b,features: tmpMFCCs); + FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); + "stats".postln; + 12.do{|i| + //This takes ages becayse of server syncing :-( + FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8)); + FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 2); + FluidBufCompose.process(s,tmpStats,6,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 3); + FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 4); + FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 6); + FluidBufCompose.process(s,tmpStats,6,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 7); + }; + ~dataset.addPoint(b.path,tmpFlat); + tmpFlat.free; + tmpStats.free; + tmpMFCCs.free; + }; +}.play +//check +~dataset.size({|x| x.postln}) +//save +~dataset.write("/Users/owen/Documents/16bitBoxes/mfccs.json") + +//STEP 3B: OR load in one you rolled eearlier +~dataset.read("/Users/owen/Documents/16bitBoxes/mfccs.json") + +//peek +c = Buffer.new(s) +~dataset.getPoint(~audioBuffers[3].path,c, { c.getn(0,96,{|x| x.postln})}) + +/*************************************/ +//FluidKDTree +~kdtree = FluidKDTree.new(s) +~kdtree.index(~dataset,action:{"index".postln}) +//match +~kdtree.kNearest(c,5,{|x| ~matches = x}) +~kdtree.kNearestDist(c,5,{|x| x.postln}) + +~lookup[~matches[4]].play + +/*************************************/ +//FluidKMeans +~kMeans= FluidKMeans.new(s) +~kMeans.fit(~dataset,k:5,action:{"fit".postln}) +~kMeans.predictPoint(c,{|x|x.postln}) +~labels = FluidLabelSet.new(s,"clusters") +~kMeans.predict(~dataset,~labels, {|x| x.postln}) +~labels.getLabel(~audioBuffers[2].path,action:{|c| c.postln}) +Routine{ + ~labels.size({|x|x[0][0].do {|i| + forkIfNeeded{ + ~audioBuffers[i].path.postln; + ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); + s.sync; + } + } + }); +}.play +~labels.write(~path+/+"labels.json") \ No newline at end of file From aa566041f2a6fdddbbc18841c01a0ed211fa741e Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 15:27:32 +0000 Subject: [PATCH 033/550] dataset / labelset : make sure asString returns a string --- release-packaging/Classes/FluidDataSet.sc | 2 +- release-packaging/Classes/FluidLabelSet.sc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index bba6894..b46642c 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -14,7 +14,7 @@ FluidDataSet : FluidManipulationClient { } asString { - ^id + ^id.asString; } addPoint{|label, buffer, action| diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 1603c6f..eb19f6d 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -13,7 +13,7 @@ FluidLabelSet : FluidManipulationClient { } asString { - ^id; + ^id.asString; } addLabel{|id, label, action| From 2faa8c05abf469bde2a16186b7d50ffbda1f4dbb Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 15:27:53 +0000 Subject: [PATCH 034/550] Add first of PA's examples --- .../dataset/super-simple-1D-example.scd | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd new file mode 100644 index 0000000..0d61ff3 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd @@ -0,0 +1,58 @@ +s.reboot +~ds = FluidDataSet.new(s,\simple1data,1) +~point = Buffer.alloc(s,1,1) +( +Routine{ + 10.do{|i| + ~point.set(0,i); + s.sync; + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) + } +}.play +) + +/*** KDTREE ***/ +~tree = FluidKDTree.new(s) +~tree.index(~ds,action:{"Done indexing".postln}) + +k = 5; //play with this +( +Routine{ + 10.do{|i| + ~point.set(0,i); + s.sync; + ("Neighbours for point" + i).postln; + ~tree.kNearest(~point, k, {|x| ("Labels:" + x).postln}) + } +}.play +) + +/*** KMEANS ***/ + +~kmeans = FluidKMeans.new(s) +~nClusters = 4; //play with this +~kmeans.fit(~ds,~nClusters,100,action:{"Done fitting".postln}) +( +Routine{ + 10.do{|i| + ~point.set(0,i * 10); + s.sync; + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + (i * 10) ++ ":" + x).postln}) + } +}.play +) + +~labels = FluidLabelSet(s,\simple1label); + +~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x[0]).postln}) +( +Routine{ + var n; + ~labels.size({|x| n = x[0][0]}); + n.asInt.do{|i| + ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); + } +}.play +) + + From d169f70af632e65274bcc7d44b44a4113839974e Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 15:56:56 +0000 Subject: [PATCH 035/550] Improvements to return value handling magic --- release-packaging/Classes/FluidKMeans.sc | 4 ++-- release-packaging/Classes/FluidLabelSet.sc | 4 ++-- release-packaging/Classes/FluidMessageResponse.sc | 10 ++++++++-- .../ignore/Examples/dataset/myfirstdataset.scd | 2 +- .../Examples/dataset/super-simple-1D-example.scd | 4 ++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 1a679cb..5bcd129 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -13,11 +13,11 @@ FluidKMeans : FluidManipulationClient { } predictPoint { |buffer, action| - this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); } cols { |action| - this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); } read{ |filename,action| diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index eb19f6d..3712f6e 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -29,7 +29,7 @@ FluidLabelSet : FluidManipulationClient { } cols {|action| - this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); } read{|filename,action| @@ -41,7 +41,7 @@ FluidLabelSet : FluidManipulationClient { } size { |action| - this.pr_sendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\size,[],action,[number(FluidMessageResponse,_,_)]); } clear { |action| diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc index 5f3782d..2723262 100644 --- a/release-packaging/Classes/FluidMessageResponse.sc +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -9,9 +9,11 @@ FluidMessageResponse : Object selectors.do{ |selector| var newThings; # newThings,idx = selector.value(a, idx); - response = response.add(newThings); + response = response ++ newThings; }; - ^response + + if(response.size == 1, + {^response[0]},{^response}) } *string{ |a, offset| @@ -25,6 +27,10 @@ FluidMessageResponse : Object ^[a.copyRange(offset, offset + n),offset + n] } + *number{ |a,offset| + ^[a[offset]]; + } + *buffer{ |a,server,offset| server = server ? Server.default ; ^[Buffer.cachedBufferAt(server, a[offset]), offset + 1] diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd index dbc8401..2a8bd11 100644 --- a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd +++ b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd @@ -80,7 +80,7 @@ c = Buffer.new(s) ~kMeans.predict(~dataset,~labels, {|x| x.postln}) ~labels.getLabel(~audioBuffers[2].path,action:{|c| c.postln}) Routine{ - ~labels.size({|x|x[0][0].do {|i| + ~labels.size({|x|x.do {|i| forkIfNeeded{ ~audioBuffers[i].path.postln; ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd index 0d61ff3..1306e25 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd @@ -44,11 +44,11 @@ Routine{ ~labels = FluidLabelSet(s,\simple1label); -~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x[0]).postln}) +~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) ( Routine{ var n; - ~labels.size({|x| n = x[0][0]}); + ~labels.size({|x| n = x.asInt}); n.asInt.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); } From cc8b283f547b9eaccbbe9ab0615a86ccff3c0af6 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 28 Nov 2019 16:06:30 +0000 Subject: [PATCH 036/550] second-super-simple-1D-example --- .../dataset/super-simple-1D-example2.scd | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd new file mode 100644 index 0000000..30b0a31 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd @@ -0,0 +1,61 @@ +s.reboot +~ds = FluidDataSet.new(s,\simple1data,1) +~point = Buffer.alloc(s,1,1) +( +Routine{ + 10.do{|i| + var d; + if(i<=4,{d=i},{d=i+5}); + ~point.set(0,d); + s.sync; + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) + } +}.play +) + +/*** KDTREE ***/ +~tree = FluidKDTree.new(s) +~tree.index(~ds,action:{"Done indexing".postln}) + +k = 5; //play with this +( +Routine{ + 10.do{|i| + ~point.set(0,i*10); + s.sync; + ("Neighbours for point" + (i*10)).postln; + ~tree.kNearest(~point, k, {|x| ("Labels:" + x).postln}); + ~tree.kNearestDist(~point,k,{|x| ("Distances:" + x).postln}) + } +}.play +) + +/*** KMEANS ***/ + +~kmeans = FluidKMeans.new(s) +~nClusters = 2; //play with this +~kmeans.fit(~ds,~nClusters,100,action:{"Done fitting".postln}) +( +Routine{ + 10.do{|i| + ~point.set(0,i * 10); + s.sync; + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + (i * 10) ++ ":" + x).postln}) + } +}.play +) + +~labels = FluidLabelSet(s,\simple1label); + +~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) +( +Routine{ + var n; + ~labels.size({|x| n = x.asInt}); + n.asInt.do{|i| + ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); + } +}.play +) + + From 4e29aa85545fb5842f1614971b616ebe5320bd3c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 29 Nov 2019 09:43:22 +0000 Subject: [PATCH 037/550] KNN has no args --- release-packaging/Classes/FluidKNN.sc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index 464e7c9..7236957 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -1,19 +1,15 @@ FluidKNN : FluidManipulationClient { - *kr { |dims,k| - ^this.multiNew('control', dims, k, Done.none, super.nonBlocking); - } - index{|dataset, action| - this.pr_sendMsg(\index,[dataset.asUGenInput],action); + this.pr_sendMsg(\index,[dataset.asString],action); } classify{ |buffer, labelset, k, action| - this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asUGenInput, k],action,k.collect{string(FluidMessageResponse,_,_)}); + this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,k.collect{string(FluidMessageResponse,_,_)}); } regress { |buffer,dataset, k, action| - this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset.asUGenInput,k],action,[numbers(FluidMessageResponse,_,1,_)]); + this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset.asString,k],action,[number(FluidMessageResponse,_,_)]); } } From 453d74b4ee6fe5527c998cc3c5736866fbbe19ec Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 29 Nov 2019 11:17:14 +0000 Subject: [PATCH 038/550] simple knn example first bash --- .../super-simple-regressor-example.scd | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd new file mode 100644 index 0000000..8c3f600 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd @@ -0,0 +1,46 @@ +s.reboot + + +~urn = { |n=31416, min=0,max=31415| (min..max).scramble.keep(n) }; + +n = 200 +~idx = ~urn.value(n) +~data = n.collect{|i|sin(~idx[i]/5000)} +( +~simpleInput = FluidDataSet(s,\simpleInput,1); +~simpleOutput = FluidDataSet(s,\simpleOutput,1); +b = Buffer.alloc(s,1,1); +~mappingviz = Buffer.alloc(s,31416,1); +) +( +Routine{ +n.do{|i| + b.set(0,~idx[i]); + s.sync; + ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); + b.set(0,~data[i]); + s.sync; + ~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln}); + ~mappingviz.set(~idx[i].asInt,~data[i]) + } +}.play +) +( +~simpleInput.clear; +~simpleOutput.clear; +) +3%2 +~mappingviz.plot(minval:-1,maxval:1) + +~mappingresult = Buffer.alloc(s,31416,1); + +~knn = FluidKNN(s) +~knn.index(~simpleInput,action:{"index done".postln}) +( + + 512.do{|i| + b.set(0,i); + ~knn.regress(b,~simpleOutput,1,action:{|d|~mappingresult.set(i,d)}); + // if(i%512 == 0, {i.postln; s.sync},{}); +} +) \ No newline at end of file From deb785f4e649440d3a6e974f7ff03a3814365441 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 1 Dec 2019 20:36:38 +0000 Subject: [PATCH 039/550] Update release-packaging/Classes/FluidKDTree.sc and release-packaging/Classes/FluidKMeans.sc for interface changes --- release-packaging/Classes/FluidKDTree.sc | 4 ++-- release-packaging/Classes/FluidKMeans.sc | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 18f15a1..08d1e72 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -1,7 +1,7 @@ FluidKDTree : FluidManipulationClient { - index{|dataset,action| - this.pr_sendMsg(\index,[dataset.asString],action); + fit{|dataset,action| + this.pr_sendMsg(\fit,[dataset.asString],action); } kNearest{ |buffer, k,action| diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 5bcd129..131b377 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -12,8 +12,12 @@ FluidKMeans : FluidManipulationClient { this.pr_sendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); } + getClusters{ |dataset, labelset,action| + this.pr_sendMsg(\getClusters,[dataset.asString, labelset.asString],action); + } + predictPoint { |buffer, action| - this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); + this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,_)]); } cols { |action| From 718f8b5bc6cc7a4cb15d2b409986b50c7e8ff1e9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 1 Dec 2019 22:19:52 +0000 Subject: [PATCH 040/550] Add NMFCross --- release-packaging/Classes/FluidBufNMFCross.sc | 34 +++++++++++++++++++ src/FluidNMFCross/CMakeLists.txt | 20 +++++++++++ src/FluidNMFCross/FluidNMFCross.cpp | 12 +++++++ 3 files changed, 66 insertions(+) create mode 100644 release-packaging/Classes/FluidBufNMFCross.sc create mode 100644 src/FluidNMFCross/CMakeLists.txt create mode 100644 src/FluidNMFCross/FluidNMFCross.cpp diff --git a/release-packaging/Classes/FluidBufNMFCross.sc b/release-packaging/Classes/FluidBufNMFCross.sc new file mode 100644 index 0000000..628be69 --- /dev/null +++ b/release-packaging/Classes/FluidBufNMFCross.sc @@ -0,0 +1,34 @@ +FluidBufNMFCross : UGen{ + + *new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + + source = source.asUGenInput; + target = target.asUGenInput; + output = output.asUGenInput; + source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; + target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; + output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; + + ^super.new1(rate, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, doneAction, blocking); + } + + *kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| + ^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, doneAction); + } + + *process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, action| + ^FluidNRTProcess.new( + server, this, action, [output].select{|x| x!= -1} + ).process( + source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize + ); + } + + *processBlocking { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, action| + ^FluidNRTProcess.new( + server, this, action, [output].select{|x| x!= -1}, blocking: 1 + ).process( + source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize + ); + } +} diff --git a/src/FluidNMFCross/CMakeLists.txt b/src/FluidNMFCross/CMakeLists.txt new file mode 100644 index 0000000..3693881 --- /dev/null +++ b/src/FluidNMFCross/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.3) +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +target_include_directories( + ${PLUGIN} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../../include +) + +target_link_libraries( + ${PLUGIN} PRIVATE FLUID_DECOMPOSITION +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidNMFCross/FluidNMFCross.cpp b/src/FluidNMFCross/FluidNMFCross.cpp new file mode 100644 index 0000000..5749fa5 --- /dev/null +++ b/src/FluidNMFCross/FluidNMFCross.cpp @@ -0,0 +1,12 @@ + +#include +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidBufNMFCross", ft); +} From f61c7db147eb242f36e7b4530b9ab21991160d3b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 3 Dec 2019 13:47:42 +0100 Subject: [PATCH 041/550] interface updates and fixes --- release-packaging/Classes/FluidDataSet.sc | 8 ++++++-- release-packaging/Classes/FluidKNN.sc | 4 ++-- release-packaging/Classes/FluidLabelSet.sc | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index b46642c..fb4b696 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -6,6 +6,10 @@ FluidDataSet : FluidManipulationClient { ^this.new1('control',name, dims); } + *new { |server,name,dims| + ^super.new(server,name,dims); + } + init { |name, dims| var ascii = name.ascii; this.id = name; @@ -29,8 +33,8 @@ FluidDataSet : FluidManipulationClient { this.pr_sendMsg(\updatePoint,[label,buffer.asUGenInput],action); } - deletePoint{|label,buffer, action| - this.pr_sendMsg(\deletePoint,[label,buffer.asUGenInput],action); + deletePoint{|label, action| + this.pr_sendMsg(\deletePoint,[label],action); } cols {|action| diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index 7236957..21a598f 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -1,7 +1,7 @@ FluidKNN : FluidManipulationClient { - index{|dataset, action| - this.pr_sendMsg(\index,[dataset.asString],action); + fit{|dataset, action| + this.pr_sendMsg(\fit,[dataset.asString],action); } classify{ |buffer, labelset, k, action| diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 3712f6e..9abf723 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -6,6 +6,11 @@ FluidLabelSet : FluidManipulationClient { ^this.multiNew('control',name); } + + *new { |server,name| + ^super.new(server,name); + } + init { |name| var ascii = name.ascii; this.id = name; From 0af28a18ba453939d6db6d2b3ec610e4e4d34ae9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 3 Dec 2019 13:48:12 +0100 Subject: [PATCH 042/550] FluidManipulation data classes help files, pass 0 --- .../HelpSource/Classes/FluidDataSet.schelp | 141 ++++++++++++++++++ .../HelpSource/Classes/FluidKDTree.schelp | 78 ++++++++++ .../HelpSource/Classes/FluidKNN.schelp | 68 +++++++++ .../HelpSource/Classes/FluidLabelSet.schelp | 97 ++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidDataSet.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidKDTree.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidKNN.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidLabelSet.schelp diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp new file mode 100644 index 0000000..f09b433 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -0,0 +1,141 @@ +TITLE:: FluidDataSet +summary:: Container for labelled, multidimensional data +categories:: UGens>FluidManipulation +related:: Classes/FluidLabelSet, Classes/FluidKDTree, Classes/FluidKNN, Classes/FluidKMeans +​ +DESCRIPTION:: +A server-side container associating labels with multi-dimensional data. FluidDataSet is identified by its name, and multiple instances of the object with the same name point to the same instance on the server. +​ +CLASSMETHODS:: +​ +PRIVATE::kr + +METHOD:: new +Create a new instance of the dataset, with the given name and dimensionality. If an instance already exists on the server, then the existing dimensionality takes precedence. +​ +ARGUMENT:: server +The link::Classes/Server:: on which to create the data set + +ARGUMENT:: name +A symbol or string with the name of the dataset. +​ +ARGUMENT:: dims +An integer number of dimensions +​ +returns:: The new instance + +INSTANCEMETHODS:: +​ +PRIVATE::init,id + +METHOD:: synth +The internal synth the object uses to communicate with the server +​ +returns:: A link::Classes/Synth:: +​ +METHOD:: server +The server instance the object uses +​ +returns:: A link::Classes/Server:: +​​​ +METHOD:: updatePoint +Update an existing label's data. Will report an error if the label doesn't exist, or if the size of the data does not match the given dimensionality of the dataset. +​ +ARGUMENT:: label +symbol or string with the label +​ +ARGUMENT:: buffer +A link::Classes/Buffer:: containing the updated data +​ +ARGUMENT:: action +A function to run when the server has updated +​​​ +METHOD:: size +Report the number of items currently in the data set +​ +ARGUMENT:: action +A function to run when the server responds, whose argument is the data set size +​​ +METHOD:: addPoint +Add a new point to the data set. Will report an error if the label already exists, or if the size of the data does not match the given dimensionality of the dataset. +​ +ARGUMENT:: label +A symbol or string with the label for the new point +​ +ARGUMENT:: buffer +A link::Classes/Buffer:: with the new data point +​ +ARGUMENT:: action +A function to run when the point has been added +​​ +METHOD:: write +Write the data set to disk as a JSON file. Will not overwrite existing files +​ +ARGUMENT:: filename +Absolute path for the new file +​ +ARGUMENT:: action +A function to run when the file has been written +​​ +METHOD:: asString +​ +returns:: The name of the data set as a string +​ +METHOD:: deletePoint +Remove a point from the data set. Will report an error if the label doesn't exist. +​ +ARGUMENT:: label +symbol or string with the label to remove +​ +ARGUMENT:: action +Function to run when the point has been deleted +​​ +METHOD:: clear +Empty the data set +​ +ARGUMENT:: action +Function to run when the data set has been emptied +​ +METHOD:: getPoint +Retreive a point from the data set into a link::Classes/Buffer::. Will report an error if the label or buffer doesn't exist +​ +ARGUMENT:: label +symbol or string with the label to retreive +​ +ARGUMENT:: buffer +link::Classes/Buffer:: to fill +​ +ARGUMENT:: action +function to run when the point has been retreived +​​ +METHOD:: read +Read a data set from a JSON file on disk +​ +ARGUMENT:: filename +The absolute path of the JSON file to read +​ +ARGUMENT:: action +A function to run when the file has been read +​​ +METHOD:: cols +Report the dimensionality of the data set +​ +EXAMPLES:: + +CODE:: + +// Make a one-dimensional data set called 'simple1data' +~ds = FluidDataSet.new(s,\simple1data,1) +// Make a buffer to use for adding points +~point = Buffer.alloc(s,1,1) +//Add 10 points, using the index as a label. +( +Routine{ + 10.do{|i| + ~point.set(0,i); + s.sync; + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) + } +}.play +) +:: diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp new file mode 100644 index 0000000..1476d77 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -0,0 +1,78 @@ +TITLE:: FluidKDTree +summary:: KD Tree on the server +categories:: FluidManipulation +related:: Classes/FluidDataSet + +DESCRIPTION:: +A server-side K-Dimensional tree for efficient neighbourhood searches of multi-dimensional data. See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algorithms for more on KD Trees + +CLASSMETHODS:: + +INSTANCEMETHODS:: + +METHOD:: read +Set the object's state from a JSON file + +ARGUMENT:: filename +The location of a JSON file on disk + +ARGUMENT:: action +function to run when the data is loaded + +METHOD:: kNearestDist +Get the distances of the K nearest neighbours to a point + +ARGUMENT:: buffer +A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. + +ARGUMENT:: k +The number of neighbours to search + +ARGUMENT:: action +A function that will run when the query returns, whose argument is an array of distances + +returns:: nothing, but could return an array if you like + +METHOD:: fit +Build the tree by scanning the points of a LINK::Classes/FluidDataSet:: + +ARGUMENT:: dataset +The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one. + +ARGUMENT:: action +A function to run when indexing is complete + +METHOD:: write +Write the index of the tree to disk. Currently this will not overwrite extant files. + +ARGUMENT:: filename +The path of a JSON file to write + +ARGUMENT:: action +A function to run when writing is complete + +METHOD:: kNearest +Returns the IDs of the CODE::k:: points nearest to the one passed + +ARGUMENT:: buffer +A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. + +ARGUMENT:: k +The number of neighbours to return + +ARGUMENT:: action +A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet:: + +returns:: Nothing, but could return an array of IDs if you like + +METHOD:: cols +Get the dimensionality of the data that the tree is indexed against + +ARGUMENT:: action +A function that runs when the query returns, whose argument is the dimensionality + +EXAMPLES:: + +code:: +(some example code) +:: diff --git a/release-packaging/HelpSource/Classes/FluidKNN.schelp b/release-packaging/HelpSource/Classes/FluidKNN.schelp new file mode 100644 index 0000000..1366650 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidKNN.schelp @@ -0,0 +1,68 @@ +TITLE:: FluidKNN +summary:: K Nearest Neighbours machine learning +categories:: FluidManipulation +related:: Classes/FluidKDTree + +DESCRIPTION:: +Simple machine learning tasks (classification and regression) using K Nearest Neighbours. + +See +https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbors-classification + +https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbors-regression + + +CLASSMETHODS:: + +INSTANCEMETHODS:: + +METHOD:: fit +'Train' the KNN on a source link::Classes/FluidDataSet:: + +ARGUMENT:: dataset +source link::Classes/FluidDataSet:: + +ARGUMENT:: action +A function to run when fitting is complete + +METHOD:: regress +Map a point between a source link::Classes/FluidDataSet:: used when link::Classes/FluidKNN#index::ing this KNN, and a target data set passed as an argument. + +For this to work, the target data set must have labels in common with the source data set. The code::k:: nearest neighbours to the supplied data point are retrrived from the source tree, and then a mapping is obtained through the average of the equivalently labelled points in the target data set. + +WARNING:: For now the target data set can only be 1D:: + +ARGUMENT:: buffer +The data point to map, in a link::Classes/Buffer:: + +ARGUMENT:: dataset +The target link::Classes/FluidDataSet:: + +ARGUMENT:: k +The number of neighbours to use in the estimation + +ARGUMENT:: action +A function to run when the server responds, taking the value of the regressed point as its argument + +METHOD:: classify +Classify a point, using categories from the supplied label set, which maps labels from the source data set to category IDs. This works by getting the labels of the code::k:: nearest points to the passed data point from the source data set, and looking up their IDs in the passed label set. The most frequently ocurring ID is designated as the class for the point. + +ARGUMENT:: buffer +The data point to classify, in a link::Classes/Buffer:: + +ARGUMENT:: labelset +A link::Classes/FluidLabelSet:: of categories mapped to labels in source data set + +ARGUMENT:: k +The number of neighbours to use in the classificaton + +ARGUMENT:: action +A function to run when the server responds, taking the assigned label as its argument + + + +EXAMPLES:: + +code:: +(some example code) +:: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp new file mode 100644 index 0000000..4bd10f6 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -0,0 +1,97 @@ +TITLE:: FluidLabelSet +summary:: A set of labels associated with IDs +categories:: FluidManipulation +related:: Classes/FluidDataSet, Classes/FluidKMeans + +DESCRIPTION:: +FluidLabelSet is a server-side container of associations between labels (from a link::Classes/FluidDataSet::) and IDs, for instance from some clustering or classification of the points in a data set. + + +CLASSMETHODS:: + +PRIVATE:: kr + +METHOD:: new +Make a new instance of a label set, uniquely identified by its name. Multiple instances to of this class with the same name refer to the same server-side entity. + +ARGUMENT:: server +The link::Classes/Server:: on which to create the label set + +ARGUMENT:: name +symbol or string with the label set's name + +INSTANCEMETHODS:: + +PRIVATE:: init, id, server, synth + +METHOD:: getLabel +Retreive the label associated with an ID. Will report an error if the ID isn't present in the set + +ARGUMENT:: id +symbol or string with the ID to retreive. + +ARGUMENT:: action +A function to run when the server responds, with the label as its argument + +METHOD:: cols +Returns the dimensionality of the link::Classes/FluidDataSet:: associated with this label set + +ARGUMENT:: action +A function to run when the server responds, with the dimensionality as its argument + +METHOD:: write +Write this label set to disk as a JSON file. Will not overwrite existing files. + +ARGUMENT:: filename +Absolute path of file to write + +ARGUMENT:: action +A function to run when the file is written + +METHOD:: read +Read a label set from a JSON file on disk + +ARGUMENT:: filename +Absolute path of the file to read + +ARGUMENT:: action +A function to run when the file is read + +METHOD:: deleteLabel +Remove a id-label pair from the label set + +ARGUMENT:: id +symbol or string with the ID to remove + +ARGUMENT:: action +A function to run when the label has been removed + +METHOD:: size +Report the num er of items in the label set + +ARGUMENT:: action +A function to run when the server responds, taking the size as its argument + +METHOD:: addLabel +Add a label to the label set + +ARGUMENT:: id +symbol or string with the ID for this label + +ARGUMENT:: label +symbol or string with the label to add + +ARGUMENT:: action +function to run when the operation completes + +METHOD:: clear +Empty the label set + +ARGUMENT:: action +Function to run whrn the action completes + +EXAMPLES:: + +code:: +(some example code) +:: \ No newline at end of file From 13498a5e8c91b7e49a1360b3123e2dbc5386dace Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 3 Dec 2019 18:04:17 +0100 Subject: [PATCH 043/550] AudioTransport has two audio inputs --- release-packaging/Classes/FluidAudioTransport.sc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidAudioTransport.sc b/release-packaging/Classes/FluidAudioTransport.sc index c14e74b..674c0bb 100644 --- a/release-packaging/Classes/FluidAudioTransport.sc +++ b/release-packaging/Classes/FluidAudioTransport.sc @@ -1,5 +1,5 @@ FluidAudioTransport : UGen { - *ar { arg in = 0, interpolation=0.0, bandwidth=255,windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; - ^this.multiNew('audio', in.asAudioRateInput(this), interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize) + *ar { arg in = 0, in2 = 0 , interpolation=0.0, bandwidth=255,windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; + ^this.multiNew('audio', in.asAudioRateInput(this), in2.asAudioRateInput(this), interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize) } } From 4306d6b277ae500810f61046c647ac227afba4e1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 3 Dec 2019 18:04:49 +0100 Subject: [PATCH 044/550] More TB2 help stubs (KMeans, (Buf)AudioTransport, and base client) --- .../Classes/FluidAudioTransport.schelp | 47 +++++++++ .../Classes/FluidBufAudioTransport.schelp | 82 ++++++++++++++++ .../HelpSource/Classes/FluidKMeans.schelp | 96 +++++++++++++++++++ .../Classes/FluidManipulationClient.schelp | 51 ++++++++++ 4 files changed, 276 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidAudioTransport.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidKMeans.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidManipulationClient.schelp diff --git a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp new file mode 100644 index 0000000..c9b7c6b --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp @@ -0,0 +1,47 @@ +TITLE:: FluidAudioTransport +summary:: Interpolate between sounds +categories:: FluidManipulation +related:: Classes/FluidBufAudioTransport + +DESCRIPTION:: +Interpolates between the spectra of two sounds using the Optimal Transport algorithm + +See +Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx + + +CLASSMETHODS:: + +METHOD:: ar +Process incoming audio signals + +ARGUMENT:: in +Source A + +ARGUMENT:: in2 +Source B + +ARGUMENT:: interpolation +The amount to interpolate between A and B (0-1, 0 = A, 1 = B) + +ARGUMENT:: bandwidth +Someone tell me + +ARGUMENT:: windowSize +The size of the processing window (kr) + +ARGUMENT:: hopSize +The processing hop size (kr). Default = windowSize / 2 + +ARGUMENT:: fftSize +The processing FFT size (kr). Default = windowSize + +ARGUMENT:: maxFFTSize +The maximum FFT size for processing + + +EXAMPLES:: + +code:: +(some example code) +:: diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp new file mode 100644 index 0000000..f5040ac --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -0,0 +1,82 @@ +TITLE:: FluidBufAudioTransport +summary:: Interpolate between buffers +categories:: FluidManipulation +related:: Classes/FluidAudioTransport + +DESCRIPTION:: +Interpolates between the spectra of two sounds using the Optimal Transport algorithm + +See +Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTIMAL TRANSPORT, DaFx + +CLASSMETHODS:: + +METHOD:: process +Process two audio link::Classes/Buffer:: + +ARGUMENT:: server +The server the process runs on + +ARGUMENT:: source1 +The first source buffer + +ARGUMENT:: startFrame1 +offset into the first source buffer (samples) + +ARGUMENT:: numFrames1 +number of samples to use from first source buffer + +ARGUMENT:: startChan1 +starting channel of first source buffer + +ARGUMENT:: numChans1 +number of channels to process in first source buffer + +ARGUMENT:: source2 +the second source buffer + +ARGUMENT:: startFrame2 +offset into the second source buffer (samples) + +ARGUMENT:: numFrames2 +number of samples to process from second buffer + +ARGUMENT:: startChan2 +starting channel for second buffer + +ARGUMENT:: numChans2 +number of channels to process in second buffer + +ARGUMENT:: destination +buffer for interpolated audio + +ARGUMENT:: interpolation +The amount to interpolate between A and B (0-1, 0 = A, 1 = B) + +ARGUMENT:: bandwidth +Someone tell me + +ARGUMENT:: windowSize +The size of the processing window (kr) + +ARGUMENT:: hopSize +The processing hop size (kr). Default = windowSize / 2 + +ARGUMENT:: fftSize +The processing FFT size (kr). Default = windowSize + +ARGUMENT:: action +Function to run when processing complete, taking the destination buffer as its argument + +INSTANCEMETHODS:: + +private:: synth, server + +METHOD:: cancel +cancel processing on the server + +EXAMPLES:: + +code:: +(some example code) +:: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp new file mode 100644 index 0000000..fa27fd3 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -0,0 +1,96 @@ +TITLE:: FluidKMeans +summary:: Cluster data points with K-Means +categories:: FluidManipulation +related:: Classes/FluidDataSet, Classes/FluidLabelSet, Classes/FluidKNN + +DESCRIPTION:: +Uses the K-Means algorithm to learn clusters from a link::Classes/FluidDataSet:: + +https://scikit-learn.org/stable/tutorial/statistical_inference/unsupervised_learning.html#clustering-grouping-observations-together + +CLASSMETHODS:: + +INSTANCEMETHODS:: + +PRIVATE::k + +METHOD:: predictPoint +Given a trained object, return the cluster ID for a data point in a link::Classes/Buffer:: + +ARGUMENT:: buffer +a link::Classes/Buffer:: containing a data point + +ARGUMENT:: action +A function to run when the server responds, taking the ID of the cluser as its argument + +METHOD:: fit +Identify code::k:: clusters in a link::Classes/FluidDataSet:: + +ARGUMENT:: dataset +A link::Classes/FluidDataSet:: of data points + +ARGUMENT:: k +The number of clusters to identify in the data set + +ARGUMENT:: maxIter +Maximum number of iterations to use partitioning the data + +ARGUMENT:: buffer +Seed centroids for clusters WARNING:: Not yet implemented :: + +ARGUMENT:: action +A function to run when fitting is complete, taking as its argument an array with the number of data points for each cluster + +METHOD:: write +write learned clusters to disk as a JSON file. Will not overwrite existing files + +ARGUMENT:: filename +Absolute path for file + +ARGUMENT:: action +A function to run when the file is written + +METHOD:: read +Read a learned clustering of a data set from a JSON file + +ARGUMENT:: filename +Absolute path of the JSON file + +ARGUMENT:: action +Function to run when the file has been read + +METHOD:: getClusters +Fill a link::Classes/FluidLabelSet:: with the assignments for each point in the passed link::Classes/FluidDataSet:: that was used to train this instance + +ARGUMENT:: dataset +The link::Classes/FluidDataSet:: used to train this instance + +ARGUMENT:: labelset +A link::Classes/FluidLabelSet:: to fill with assignments + +ARGUMENT:: action +A function to run when the operation is complete + +METHOD:: cols +Retreive the dimentionality of the dataset this instance is trained on + +ARGUMENT:: action +A function to run when the server responds, taking the dimensionality as its argument + +METHOD:: predict +Report cluster assignments for previously unseen data + +ARGUMENT:: dataset +A link::Classes/FluidDataSet:: of data points + +ARGUMENT:: labelset +A link::Classes/FluidLabelSet:: to contain assigments + +ARGUMENT:: action +A function to run when complete, taking an array of the counts for each catgegory as its argument + +EXAMPLES:: + +code:: +(some example code) +:: diff --git a/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp b/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp new file mode 100644 index 0000000..3e2d69a --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp @@ -0,0 +1,51 @@ +TITLE:: FluidManipulationClient +summary:: A base case for FluidManipulation classes +categories:: FluidManipulation + +DESCRIPTION:: +A utility base class for FluidManipulation classes, that deals with common functionality (wrapping a link::Classes/Synth:: in the instance, etc.) + +Not intended to be used directly. + +CLASSMETHODS:: + +PRIVATE:: nonBlocking + +METHOD:: kr +The kr of the underlying UGem, which will output progress readings once all messages can be asynchronous. + +If, for whatever reason, you create an instance of a client object using code::kr:: in your own synth, make sure to set the instance's link::Classes/FluidManipulationClient#synth:: and link::Classes/FluidManipulationClient#server::, or nothing will work. + +returns:: An instance + +METHOD:: new +Language-side constructor. Internally, this creates a new synth around an instance of the sub-class being constructed, and maintains a variable pointing to the synth, so that it can be communicated with. + +ARGUMENT:: server +The link::Classes/Server:: this instance is running on + +ARGUMENT:: ... args +Arguments specific to the sub-class at hand + +returns:: A new instance + +INSTANCEMETHODS:: + +private:: pr_sendMsg + +METHOD:: synth +Fluid Manipulation client objects are server-side entities, wrapped in a Node to manage communication between language and object. For this, a link::Classes/Synth:: is needed, as the representation of a server node. + +returns:: the instance's link::Classes/Synth:: + +METHOD:: server +The link::Classes/Server:: that our instance's object is running on + +returns:: a link::Classes/Server:: + + +EXAMPLES:: + +code:: +(some example code) +:: \ No newline at end of file From 1268ef20d3f280df5b7b4d9095998a99ad5dbe89 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 4 Dec 2019 10:50:12 +0100 Subject: [PATCH 045/550] Add 1st NMFCross help --- .../Classes/FluidBufNMFCross.schelp | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp diff --git a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp new file mode 100644 index 0000000..6507705 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp @@ -0,0 +1,72 @@ +TITLE:: FluidBufNMFCross +summary:: Cross Synthesis with Nonnegative Matrix Factorization +categories:: FluidManipulation +related:: Classes/FluidBufNMF, Classes/FluidNMFMatch, Classes/FluidNMFFilter + +DESCRIPTION:: + +Produces hybridisations of two link::Classes/Buffer::s by using Nonnegative Maxtrix Factorization (NMF) + +See Driedger, J., Prätzlich, T., & Müller, M. (2015). Let it Bee-Towards NMF-Inspired Audio Mosaicing. ISMIR, 350–356. http://ismir2015.uma.es/articles/13_Paper.pdf + +The process works by attempting to reconstruct the code::target:: sound using the timbre of the code::source:: sound, resulting in a hybrid whose character depends on how well the target can be represnted by the source's spectral frames. + +In contrast to link::Classes/FluidBufNMF::, the size and content of the bases dictionary are fixed in this application to be the spectrogram of the code::source::. Each spectral frame of code::source:: is a template: be aware that NMF is O(N^2) in the number of templates, so longer code::source:: buffers will take dramatically longer to process. + + +CLASSMETHODS:: + +private:: kr, new1 + +METHOD:: process, processBlocking + +Process two buffers. code::process:: will use its own worker thread on the server, and so avoid blocking the command FIFO queue. For very small jobs, it may be quicker to use code::processBlocking::, which runs directly in the server's queue. + +ARGUMENT:: server +The link::Classes/Server:: on which to process + +ARGUMENT:: source +A link::Classes/Buffer:: whose content will supply the spectral bases used in the hybrid + +ARGUMENT:: target +A link::Classes/Buffer:: whose content will supply the temporal activations used in the hybrid + +ARGUMENT:: output +A link::Classes/Buffer:: to contain the new sound + +ARGUMENT:: timeSparsity +Control the repetition of source templates in the reconstruction by specifying a number of frames within which a template should not be re-used. Units are spectral frames. + +ARGUMENT:: polyphony +Control the spectral density of the output sound by restricting the number of simultaneous templates that can be used. Units are spectral bins. + +ARGUMENT:: iterations +How many iterations of NMF to run + +ARGUMENT:: windowSize +The analysis window size in samples + +ARGUMENT:: hopSize +The analysus hop size in samples (default winSize / 2) + +ARGUMENT:: fftSize +The analsyis FFT size in samples (default = winSize) + +ARGUMENT:: action +A function to run when processing is complete, taking the output buffer as its argument + + +INSTANCEMETHODS:: + + +EXAMPLES:: + +code:: + +~path = File.realpath(FluidBufNMF.class.filenameSymbol).dirname.withTrailingSlash +/+ "../AudioFiles/" +b = Buffer.read(s,~path+/+"~Nicol-LoopE-M.wav") +t = Buffer.read(s,~path+/+"Tremblay-SA-UprightPianoPedalWide.wav") +o = Buffer.new +FluidBufNMFCross.process(s,t,b,o,action:{"Ding".postln}) +o.play +:: \ No newline at end of file From 8bc12b278840d850b8cc6f9d93ff8aea0188fa63 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 4 Dec 2019 11:52:50 +0100 Subject: [PATCH 046/550] Add first schelp for Normalize and Standardize --- .../HelpSource/Classes/FluidNormalize.schelp | 94 +++++++++++++++++++ .../Classes/FluidStandardize.schelp | 77 +++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidNormalize.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidStandardize.schelp diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp new file mode 100644 index 0000000..1848cd3 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -0,0 +1,94 @@ +TITLE:: FluidNormalize +summary:: Normalize a FluidDataSet +categories:: FluidManipulation +related:: Classes/FluidStandardize, Classes/FluidDataSet + +DESCRIPTION:: +Normalize the entries of a link::Classes/FluidDataSet::, or normalize a data point according to the learned bounrds of a data set. On the server. + +See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html + +CLASSMETHODS:: + +private:: kr + +METHOD:: new +Create a new instance + +ARGUMENT:: server +The link::Classes/Server:: on which to run + +ARGUMENT:: min +Minimum output value, default 0 + +ARGUMENT:: max +Maximum output value, default 1 + +returns:: new instance + + +INSTANCEMETHODS:: + +METHOD:: fit +Normalize a link::Classes/FluidDataSet:: strong::in-place:: + +ARGUMENT:: dataset +The link::Classes/FluidDataSet:: to normalize + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: normalize +Normalize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: + +ARGUMENT:: sourceDataset +The link::Classes/FluidDataSet:: to normalize + +ARGUMENT:: destDataset +The link::Classes/FluidDataSet:: to populate with normalized data + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: normalizePoint +Normalize a new data point, using the learned extrema from a previous link::Classes/FluidNormalize#fit::ting + +ARGUMENT:: sourceBuffer +A link::Classes/Buffer:: with the new data point + +ARGUMENT:: destBuffer +A link::Classes/Buffer:: to contain the normalized value + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: read +Load internal state (dimensionality, mins, maxes) from a JSON file + +ARGUMENT:: filename +Absolute path to the JSON file + +ARGUMENT:: action +A function to run when file is loaded + +METHOD:: write +Store the internal state of object on disk as a JSON file. Will not overwrite existing files + +ARGUMENT:: filename +Absolute path of file to write + +ARGUMENT:: action +A function to run when file is written + +METHOD:: cols +Retreive the dimensionality of the data set we have fitted on + +ARGUMENT:: action +A function to run when the server responds, taking the dimensions as its argument + + +EXAMPLES:: + +code:: +(some example code) +:: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp new file mode 100644 index 0000000..5c94be7 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -0,0 +1,77 @@ +TITLE:: FluidStandardize +summary:: Standardize a FluidDataSet +categories:: FluidManipulation +related:: Classes/FluidDataSet, Classes/FluidNormalize + +DESCRIPTION:: +Standardize a link::Classes/FluidDataSet::, i.e. rescale using its mean(s) and standard deviation(s) in each dimension. + +See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html + +CLASSMETHODS:: + + +INSTANCEMETHODS:: + +METHOD:: fit +Standardize a link::Classes/FluidDataSet:: strong::in-place:: + +ARGUMENT:: dataset +The link::Classes/FluidDataSet:: to standardize + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: standardize +Standrdize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: + +ARGUMENT:: sourceDataset +The link::Classes/FluidDataSet:: to standardize + +ARGUMENT:: destDataset +The link::Classes/FluidDataSet:: to populate with standardized data + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: standardizePoint +Standardize a new data point, using the learned statistics from a previous link::Classes/FluidStandardize#fit::ting + +ARGUMENT:: sourceBuffer +A link::Classes/Buffer:: with the new data point + +ARGUMENT:: destBuffer +A link::Classes/Buffer:: to contain the standardize value + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: read +Load internal state (dimensionality, means, deviations) from a JSON file + +ARGUMENT:: filename +Absolute path to the JSON file + +ARGUMENT:: action +A function to run when file is loaded + +METHOD:: write +Store the internal state of object on disk as a JSON file. Will not overwrite existing files + +ARGUMENT:: filename +Absolute path of file to write + +ARGUMENT:: action +A function to run when file is written + +METHOD:: cols +Retreive the dimensionality of the data set we have fitted on + +ARGUMENT:: action +A function to run when the server responds, taking the dimensions as its argument + +EXAMPLES:: + +code:: +(some example code) +:: \ No newline at end of file From 9ea55585f7630e340521e0e03a2ad6ab867fcff9 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 10:54:40 +0000 Subject: [PATCH 047/550] updated 'myfirstdataset' example with less 'WeeFuzzyCentred' paths --- .../Examples/dataset/myfirstdataset.scd | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd index 2a8bd11..7f6e1ac 100644 --- a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd +++ b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd @@ -13,17 +13,19 @@ if(s.hasBooted.not){"Warning: server not running".postln}; //STEP 1: Get some files ( -// ~path = nil -// FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]}); -~path="/Users/owen/Documents/16bitBoxes/"; -~audioBuffers = SoundFile.collectIntoBuffers(~path+/+'*',s); -~lookup = Dictionary(n:~audioBuffers.size); -~audioBuffers.do{|b| ~lookup.add(b.path->b)} +Buffer.freeAll; +FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]; + ~audioBuffers = SoundFile.collectIntoBuffers(~path+/+'*',s); + ~lookup = Dictionary(n:~audioBuffers.size); + ~audioBuffers.do{|b| ~lookup.add(b.path->b)}; + }); ) //STEP 2: Make a FluidDataSet ~dataset = FluidDataSet.new(s,"mfccs", 96) //12 dims * 4 stats * 2 derivatives + //STEP 3A: EITHER populate the dataset like so (and cry about how long the data point assembly takes) +( Routine{ ~audioBuffers.do{|b| var tmpMFCCs = Buffer.new(s); @@ -49,13 +51,25 @@ Routine{ tmpMFCCs.free; }; }.play +) + //check ~dataset.size({|x| x.postln}) + //save -~dataset.write("/Users/owen/Documents/16bitBoxes/mfccs.json") +( +FileDialog.new(fileMode: 0, acceptMode: 1, okFunc:{|x| var file = x[0]; + //if the file exists and is a json, delete it + if ((file.splitext[1] == "json") && (File.existsCaseSensitive(file)), {File.delete(file);"File Overwritten".postln;}); + //if not json, make it so + if (file.splitext[1] != "json", {file = file ++ ".json";}); + // then write + ~dataset.write(file); +}); +) -//STEP 3B: OR load in one you rolled eearlier -~dataset.read("/Users/owen/Documents/16bitBoxes/mfccs.json") +//STEP 3B: OR load in one you rolled earlier +FileDialog.new(fileMode: 0, acceptMode: 0, okFunc:{|x| ~dataset.read(x[0])}); //peek c = Buffer.new(s) @@ -64,12 +78,12 @@ c = Buffer.new(s) /*************************************/ //FluidKDTree ~kdtree = FluidKDTree.new(s) -~kdtree.index(~dataset,action:{"index".postln}) +~kdtree.fit(~dataset,action:{"fit".postln}) //match -~kdtree.kNearest(c,5,{|x| ~matches = x}) +~kdtree.kNearest(c,5,{|x| ~matches = x;}) ~kdtree.kNearestDist(c,5,{|x| x.postln}) -~lookup[~matches[4]].play +~lookup[~matches[4]].postln /*************************************/ //FluidKMeans From f8fe8be9a1c1fdefd8c45b52e91ba4bace7ba351 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 12:24:47 +0000 Subject: [PATCH 048/550] dataset example now with a language side flattener to gain 25x speed --- .../Examples/dataset/myfirstdataset.scd | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd index 7f6e1ac..ebf742d 100644 --- a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd +++ b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd @@ -12,13 +12,13 @@ s.reboot; if(s.hasBooted.not){"Warning: server not running".postln}; //STEP 1: Get some files -( Buffer.freeAll; +( FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]; ~audioBuffers = SoundFile.collectIntoBuffers(~path+/+'*',s); ~lookup = Dictionary(n:~audioBuffers.size); ~audioBuffers.do{|b| ~lookup.add(b.path->b)}; - }); +}); ) //STEP 2: Make a FluidDataSet @@ -27,29 +27,61 @@ FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]; //STEP 3A: EITHER populate the dataset like so (and cry about how long the data point assembly takes) ( Routine{ - ~audioBuffers.do{|b| - var tmpMFCCs = Buffer.new(s); - var tmpStats = Buffer.new(s); - var tmpFlat = Buffer.new(s,12 * 4 * 2, 1); - s.sync; - ("Analyzing" + b.path).postln; - FluidBufMFCC.process(s,b,features: tmpMFCCs); - FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); - "stats".postln; - 12.do{|i| - //This takes ages becayse of server syncing :-( - FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8)); - FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 2); - FluidBufCompose.process(s,tmpStats,6,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 3); - FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 4); - FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 6); - FluidBufCompose.process(s,tmpStats,6,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 7); - }; - ~dataset.addPoint(b.path,tmpFlat); - tmpFlat.free; - tmpStats.free; - tmpMFCCs.free; - }; + var tmpMFCCs = Buffer.new(s); + var tmpStats = Buffer.new(s); + var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); + s.sync; + ~audioBuffers.do{|b| + ("Analyzing" + b.path).postln; + FluidBufMFCC.process(s,b,features: tmpMFCCs); + FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); + "stats".postln; + 12.do{|i| + //This takes ages becayse of server syncing :-( + FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8)); + FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 2); + FluidBufCompose.process(s,tmpStats,6,3, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 3); + FluidBufCompose.process(s,tmpStats,11,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 6); + FluidBufCompose.process(s,tmpStats,13,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 7); + }; + ~dataset.addPoint(b.path,tmpFlat); + }; + s.sync; + "Done".postln; + tmpFlat.free; + tmpStats.free; + tmpMFCCs.free; +}.play +) + +//STEP 3B: OR populate the dataset with the flattening happening in langage side (much faster for now) +( +Routine{ + var tmpMFCCs = Buffer.new(s); + var tmpStats = Buffer.new(s); + var langStats; + var langFlat; + var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); + s.sync; + ~audioBuffers.do{|b| + ("Analyzing" + b.path).postln; + FluidBufMFCC.process(s,b,features: tmpMFCCs); + FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); + tmpStats.getn(0,182,{|y| langStats = y;}); + s.sync; + "stats".postln; + langFlat = Array.new(); + //taking the mean, std, min and max, and the mean, std, min and max of the first derivative, of each MFCCs except coeff 0 to dismiss amplitude) + [0,1,4,6,7,8,11,13].do({|i| var j,k; j =((i*13)+1); k = j + 11;langFlat = langFlat ++ langStats[j..k]}); + tmpFlat.setn(0,langFlat); + s.sync; + ~dataset.addPoint(b.path,tmpFlat); + }; + s.sync; + "Done".postln; + tmpStats.free; + tmpMFCCs.free; + tmpFlat.free; }.play ) @@ -68,7 +100,7 @@ FileDialog.new(fileMode: 0, acceptMode: 1, okFunc:{|x| var file = x[0]; }); ) -//STEP 3B: OR load in one you rolled earlier +//STEP 3C: OR load in one you rolled earlier FileDialog.new(fileMode: 0, acceptMode: 0, okFunc:{|x| ~dataset.read(x[0])}); //peek @@ -79,6 +111,7 @@ c = Buffer.new(s) //FluidKDTree ~kdtree = FluidKDTree.new(s) ~kdtree.fit(~dataset,action:{"fit".postln}) + //match ~kdtree.kNearest(c,5,{|x| ~matches = x;}) ~kdtree.kNearestDist(c,5,{|x| x.postln}) @@ -94,13 +127,13 @@ c = Buffer.new(s) ~kMeans.predict(~dataset,~labels, {|x| x.postln}) ~labels.getLabel(~audioBuffers[2].path,action:{|c| c.postln}) Routine{ - ~labels.size({|x|x.do {|i| - forkIfNeeded{ - ~audioBuffers[i].path.postln; - ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); - s.sync; - } - } - }); + ~labels.size({|x|x.do {|i| + forkIfNeeded{ + ~audioBuffers[i].path.postln; + ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); + s.sync; + } + } + }); }.play ~labels.write(~path+/+"labels.json") \ No newline at end of file From 1225b3771411cda0cc2d07688f3340ab840433ec Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 13:39:07 +0000 Subject: [PATCH 049/550] corrected KNN methods to match Max's naming --- release-packaging/Classes/FluidKNN.sc | 4 ++-- release-packaging/HelpSource/Classes/FluidKNN.schelp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index 21a598f..f2e523b 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -4,11 +4,11 @@ FluidKNN : FluidManipulationClient { this.pr_sendMsg(\fit,[dataset.asString],action); } - classify{ |buffer, labelset, k, action| + classifyPoint{ |buffer, labelset, k, action| this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,k.collect{string(FluidMessageResponse,_,_)}); } - regress { |buffer,dataset, k, action| + regressPoint { |buffer,dataset, k, action| this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset.asString,k],action,[number(FluidMessageResponse,_,_)]); } diff --git a/release-packaging/HelpSource/Classes/FluidKNN.schelp b/release-packaging/HelpSource/Classes/FluidKNN.schelp index 1366650..41abca3 100644 --- a/release-packaging/HelpSource/Classes/FluidKNN.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNN.schelp @@ -25,7 +25,7 @@ source link::Classes/FluidDataSet:: ARGUMENT:: action A function to run when fitting is complete -METHOD:: regress +METHOD:: regressPoint Map a point between a source link::Classes/FluidDataSet:: used when link::Classes/FluidKNN#index::ing this KNN, and a target data set passed as an argument. For this to work, the target data set must have labels in common with the source data set. The code::k:: nearest neighbours to the supplied data point are retrrived from the source tree, and then a mapping is obtained through the average of the equivalently labelled points in the target data set. @@ -44,7 +44,7 @@ The number of neighbours to use in the estimation ARGUMENT:: action A function to run when the server responds, taking the value of the regressed point as its argument -METHOD:: classify +METHOD:: classifyPoint Classify a point, using categories from the supplied label set, which maps labels from the source data set to category IDs. This works by getting the labels of the code::k:: nearest points to the passed data point from the source data set, and looking up their IDs in the passed label set. The most frequently ocurring ID is designated as the class for the point. ARGUMENT:: buffer From fc0f762a3d21bf81116e513859c2ff370baa8844 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 14:05:45 +0000 Subject: [PATCH 050/550] updated super simple examples with comments and new interface --- .../dataset/super-simple-1D-example.scd | 4 +- .../dataset/super-simple-1D-example2.scd | 6 +- .../super-simple-regressor-example.scd | 65 ++++++++++++------- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd index 1306e25..0e6a240 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd @@ -13,7 +13,7 @@ Routine{ /*** KDTREE ***/ ~tree = FluidKDTree.new(s) -~tree.index(~ds,action:{"Done indexing".postln}) +~tree.fit(~ds,action:{"Done indexing".postln}) k = 5; //play with this ( @@ -32,6 +32,7 @@ Routine{ ~kmeans = FluidKMeans.new(s) ~nClusters = 4; //play with this ~kmeans.fit(~ds,~nClusters,100,action:{"Done fitting".postln}) + ( Routine{ 10.do{|i| @@ -45,6 +46,7 @@ Routine{ ~labels = FluidLabelSet(s,\simple1label); ~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) + ( Routine{ var n; diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd index 30b0a31..a74a305 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd @@ -15,15 +15,15 @@ Routine{ /*** KDTREE ***/ ~tree = FluidKDTree.new(s) -~tree.index(~ds,action:{"Done indexing".postln}) +~tree.fit(~ds,action:{"Done indexing".postln}) k = 5; //play with this ( Routine{ 10.do{|i| - ~point.set(0,i*10); + ~point.set(0,i*2); s.sync; - ("Neighbours for point" + (i*10)).postln; + ("Neighbours for point" + (i*2)).postln; ~tree.kNearest(~point, k, {|x| ("Labels:" + x).postln}); ~tree.kNearestDist(~point,k,{|x| ("Distances:" + x).postln}) } diff --git a/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd index 8c3f600..e8c4e95 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd @@ -1,46 +1,63 @@ s.reboot - ~urn = { |n=31416, min=0,max=31415| (min..max).scramble.keep(n) }; +// creates 200 indices, then values of the output of a fundion with a predictable shape of a sinewave n = 200 ~idx = ~urn.value(n) ~data = n.collect{|i|sin(~idx[i]/5000)} + +// creates the dataset with these associated indices and values ( ~simpleInput = FluidDataSet(s,\simpleInput,1); ~simpleOutput = FluidDataSet(s,\simpleOutput,1); b = Buffer.alloc(s,1,1); -~mappingviz = Buffer.alloc(s,31416,1); +~mappingviz = Buffer.alloc(s,512); ) + ( Routine{ -n.do{|i| - b.set(0,~idx[i]); - s.sync; - ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); - b.set(0,~data[i]); - s.sync; - ~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln}); - ~mappingviz.set(~idx[i].asInt,~data[i]) - } + n.do{|i| + b.set(0,~idx[i]); + s.sync; + ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); + b.set(0,~data[i]); + s.sync; + ~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln}); + ~mappingviz.set((~idx[i]/61).asInt,~data[i]) + } }.play ) -( -~simpleInput.clear; -~simpleOutput.clear; -) -3%2 + +//look at the seeing material ~mappingviz.plot(minval:-1,maxval:1) -~mappingresult = Buffer.alloc(s,31416,1); +//create a buffer to query +~mappingresult = Buffer.alloc(s,512); +//make the process then fit the data ~knn = FluidKNN(s) -~knn.index(~simpleInput,action:{"index done".postln}) +~knn.fit(~simpleInput,action:{"fitting done".postln}) + +// query 512 points along the line (slow because of all that sync'ing) ( +k = 1; // change to see how many points the system uses to regress +Routine{ + 512.do{|i| + b.set(0,i*61); + s.sync; + ~knn.regressPoint(b,~simpleOutput,k,action:{|d|~mappingresult.set(i,d);}); + s.sync; + i.postln; + } +}.play +) + +// look at the interpolated values +~mappingresult.plot - 512.do{|i| - b.set(0,i); - ~knn.regress(b,~simpleOutput,1,action:{|d|~mappingresult.set(i,d)}); - // if(i%512 == 0, {i.postln; s.sync},{}); -} -) \ No newline at end of file +// clears both datasets +( +~simpleInput.clear; +~simpleOutput.clear; +) From 9a872d26f5f333bdca9dc3819faa275c352cfac1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 4 Dec 2019 15:37:04 +0100 Subject: [PATCH 051/550] Explicitly override FluidNormalize contrsuctor, so the docs don't moan --- release-packaging/Classes/FluidNormalize.sc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 40fc0cc..9a2aa0e 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -1,9 +1,13 @@ FluidNormalize : FluidManipulationClient { - *kr{ |min, max| + *kr{ |min = 0, max = 1| ^this.multiNew('control',min, max, Done.none, super.nonBlocking); } + *new { |server,min = 0 ,max = 1| + ^super.new(server,min,max); + } + fit{|dataset, action| this.pr_sendMsg(\fit,[dataset.asUGenInput],action); } From 1d4f9a550125cd7a7069b5afbe2584c87231e4a1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 4 Dec 2019 15:37:44 +0100 Subject: [PATCH 052/550] FluidMessageResponse: Correct bundling of strings as character arrays, so server response makes sense --- release-packaging/Classes/FluidMessageResponse.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc index 2723262..51ba197 100644 --- a/release-packaging/Classes/FluidMessageResponse.sc +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -20,7 +20,7 @@ FluidMessageResponse : Object var split = a.find([0],offset); var res; if(split.isNil) {"ERROR: can't parse string from server".throw}; - ^[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInt.asAscii},String), split + 1] + ^[[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInt.asAscii},String)], split + 1] } *numbers{ |a, n, offset| From e80afc7ce57a42ee3edf412790a9f20ae0f4a72c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 4 Dec 2019 15:38:09 +0100 Subject: [PATCH 053/550] FluidKMeans predictPoint: expect number not numbers --- release-packaging/Classes/FluidKMeans.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 131b377..a45a497 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -17,7 +17,7 @@ FluidKMeans : FluidManipulationClient { } predictPoint { |buffer, action| - this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,_)]); + this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); } cols { |action| From 503aac4015fb65ebbb29073bedc2ab062d1b913c Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 15:51:04 +0000 Subject: [PATCH 054/550] classifier example: starting to have a gui that makes sense --- .../super-simple-classifier-example.scd | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd new file mode 100644 index 0000000..3009be3 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd @@ -0,0 +1,20 @@ +( +var w,v,myx,myy; +myx=0; +myy=0; +w = Window.new("Viewer", Rect(100,Window.screenBounds.height - 400, 310, 310)).front; +v = View.new(w,Rect(0,0, 310, 310)); +// v.mouseMoveAction = {|view, x, y, modifiers|"% % % %\n".postf(view, x, y, modifiers);}; +v.mouseMoveAction = {|view, x, y, modifiers|myx=x;myy=y;myx.postln;myy.postln;w.refresh;}; + +w.drawFunc = { + 100.do { |i| + if (i < 50, {Pen.color = Color.white;} ,{Pen.color = Color.red;}); + Pen.addRect(Rect(i.div(10)*30+10,i.mod(10)*30+10,20,20)); + Pen.perform(\fill); + }; + Pen.color = Color.black; + Pen.addOval(Rect(myx-5, myy-5,10,10)); + Pen.perform(\stroke); +}; +) From 2129a9a6e5f2111a22011a0f1cb6c18322228214 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 16:39:56 +0000 Subject: [PATCH 055/550] examples now finished but one is still buggy --- .../Examples/dataset/myfirstdataset.scd | 20 ++++++-- .../super-simple-classifier-example.scd | 48 ++++++++++++++++++- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd index ebf742d..508fd85 100644 --- a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd +++ b/release-packaging/ignore/Examples/dataset/myfirstdataset.scd @@ -122,18 +122,28 @@ c = Buffer.new(s) //FluidKMeans ~kMeans= FluidKMeans.new(s) ~kMeans.fit(~dataset,k:5,action:{"fit".postln}) + + +// predicts in which cluster a point would be ~kMeans.predictPoint(c,{|x|x.postln}) + +// predicts which cluster each points of a dataset would be in, as a label ~labels = FluidLabelSet.new(s,"clusters") ~kMeans.predict(~dataset,~labels, {|x| x.postln}) + ~labels.getLabel(~audioBuffers[2].path,action:{|c| c.postln}) + +//query each item +( Routine{ ~labels.size({|x|x.do {|i| - forkIfNeeded{ - ~audioBuffers[i].path.postln; - ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); - s.sync; + ~audioBuffers[i].path.postln; + ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); + s.sync; } - } }); }.play +) + +//labelset can be written as json ~labels.write(~path+/+"labels.json") \ No newline at end of file diff --git a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd index 3009be3..977c6ff 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd @@ -1,12 +1,33 @@ +( +~simpleInput = FluidDataSet(s,\simpleInput,2); +~simpleOutput = FluidLabelSet(s,\simpleOutput,2); +b = Buffer.alloc(s,2); +~knn = FluidKNN(s); +k = 3 +) + ( var w,v,myx,myy; + +//initialise the mouse position holder myx=0; myy=0; + +//make a window and a full size view w = Window.new("Viewer", Rect(100,Window.screenBounds.height - 400, 310, 310)).front; v = View.new(w,Rect(0,0, 310, 310)); -// v.mouseMoveAction = {|view, x, y, modifiers|"% % % %\n".postf(view, x, y, modifiers);}; -v.mouseMoveAction = {|view, x, y, modifiers|myx=x;myy=y;myx.postln;myy.postln;w.refresh;}; +//creates a function that reacts to mousedown +// v.mouseMoveAction = {|view, x, y|myx=x;myy=y;w.refresh;}; +v.mouseDownAction = {|view, x, y|myx=x;myy=y;w.refresh; + myx.postln;myy.postln; + Routine{ + b.setn(0,[myx,myy]); + s.sync; + ~knn.classifyPoint(b, ~simpleOutput, k, {|x|x.postln;}); +}.play;}; + +//custom redraw function w.drawFunc = { 100.do { |i| if (i < 50, {Pen.color = Color.white;} ,{Pen.color = Color.red;}); @@ -18,3 +39,26 @@ w.drawFunc = { Pen.perform(\stroke); }; ) + +( +//populates a dataset with the same squares as the gui (their centres) +Routine{ + 50.do{|i| + var x = i.div(10)*30+20; + var y = i.mod(10)*30+20; + b.setn(0,[x,y]); + s.sync; + ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); + ~simpleOutput.addLabel(i.asString,"White",{("Added Output" + i).postln}); + b.setn(0,[x+150,y]); + s.sync; + ~simpleInput.addPoint((i+50).asString,b,{("Added Input" + (i+50)).postln}); + ~simpleOutput.addLabel((i+50).asString,"Red",{("Added Output" + (i+50)).postln}); + } + }.play; +) + +// fit the dataset +~knn.fit(~simpleInput,action:{"fitting done".postln}) + +// now click on the grid and read the estimated class according to the nearest K neighbours. From d1cbcf5427a0c8a866a922f3c4cca748dd54c738 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 4 Dec 2019 17:19:53 +0000 Subject: [PATCH 056/550] basic sanity example for FluidAudioTransport --- .../HelpSource/Classes/FluidAudioTransport.schelp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp index c9b7c6b..d973cd8 100644 --- a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp @@ -43,5 +43,6 @@ The maximum FFT size for processing EXAMPLES:: code:: -(some example code) +//the mouse X axis interpolates between the two sinewaves +{FluidAudioTransport.ar(SinOsc.ar(220),SinOsc.ar(440),MouseX.kr())}.play; :: From 750c41be0770ba5859fdffcee2243a7f23ccc918 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 5 Dec 2019 10:47:15 +0100 Subject: [PATCH 057/550] NMFCross help: typo in filename gives empty buffer, which currently causes a server crash --- release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp index 6507705..e359dad 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp @@ -64,7 +64,7 @@ EXAMPLES:: code:: ~path = File.realpath(FluidBufNMF.class.filenameSymbol).dirname.withTrailingSlash +/+ "../AudioFiles/" -b = Buffer.read(s,~path+/+"~Nicol-LoopE-M.wav") +b = Buffer.read(s,~path+/+"Nicol-LoopE-M.wav") t = Buffer.read(s,~path+/+"Tremblay-SA-UprightPianoPedalWide.wav") o = Buffer.new FluidBufNMFCross.process(s,t,b,o,action:{"Ding".postln}) From 3bd79fffb11ffdaee8e1f94409436fd2b8c31e1f Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 5 Dec 2019 10:48:44 +0100 Subject: [PATCH 058/550] AudioTransport: Use specialIndex to deal with multiple audio ins, make sure wrapper notes this in the right places --- include/FluidSCWrapper.hpp | 2 +- release-packaging/Classes/FluidAudioTransport.sc | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index b338fbd..f5ce737 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -138,7 +138,7 @@ public: void next(int) { - mControlsIterator.reset(mInBuf + 1); //mClient.audioChannelsIn()); + mControlsIterator.reset(mInBuf + mSpecialIndex + 1); //mClient.audioChannelsIn()); Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params mParams.constrainParameterValues(); const Unit *unit = this; diff --git a/release-packaging/Classes/FluidAudioTransport.sc b/release-packaging/Classes/FluidAudioTransport.sc index 674c0bb..e55384f 100644 --- a/release-packaging/Classes/FluidAudioTransport.sc +++ b/release-packaging/Classes/FluidAudioTransport.sc @@ -1,5 +1,13 @@ FluidAudioTransport : UGen { + + init { |...theInputs| + theInputs.postln; + inputs = theInputs; + this.specialIndex = 1; //two audio inputs + // ^this.initOutputs(1,rate); + } + *ar { arg in = 0, in2 = 0 , interpolation=0.0, bandwidth=255,windowSize= 1024, hopSize= -1, fftSize= -1, maxFFTSize = 16384; - ^this.multiNew('audio', in.asAudioRateInput(this), in2.asAudioRateInput(this), interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize) + ^this.multiNew('audio', in.asAudioRateInput, in2, interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize) } -} +} \ No newline at end of file From c32b58ca74ef80863dfd5df12d776ae917e57fb7 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 5 Dec 2019 10:49:16 +0100 Subject: [PATCH 059/550] AudioTransport: pass the right things --- release-packaging/Classes/FluidBufAudioTransport.sc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 1268adc..0d00f67 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -5,6 +5,7 @@ FluidBufAudioTransport : UGen{ *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var blocking = 0; source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; @@ -16,7 +17,7 @@ FluidBufAudioTransport : UGen{ //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^this.multiNew(\control, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction); + ^this.multiNew(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize, doneAction,blocking); } @@ -39,7 +40,7 @@ FluidBufAudioTransport : UGen{ "WARNING: Server not running".postln; ^nil; }); - synth = { instance = FluidBufSines.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); + synth = { instance = FluidBufAudioTransport.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); forkIfNeeded{ synth.waitForFree; server.sync; From 6c44543c7500f13ef19857e290386354cb073286 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 5 Dec 2019 11:36:35 +0100 Subject: [PATCH 060/550] FluidKNN: Don't try and parse an array of strings that doesn't exist resolves 35 --- release-packaging/Classes/FluidKNN.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index f2e523b..92591bc 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -5,7 +5,7 @@ FluidKNN : FluidManipulationClient { } classifyPoint{ |buffer, labelset, k, action| - this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,k.collect{string(FluidMessageResponse,_,_)}); + this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,[string(FluidMessageResponse,_,_)]); } regressPoint { |buffer,dataset, k, action| From 71f0bb2edaa48e372c95cf52058712061f5b7d61 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 5 Dec 2019 14:41:38 +0000 Subject: [PATCH 061/550] final examples for TB2-Alpha01 --- .../Classes/FluidBufAudioTransport.schelp | 15 ++++++++++++++- .../dataset/super-simple-classifier-example.scd | 3 +-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index f5040ac..eea567a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -78,5 +78,18 @@ cancel processing on the server EXAMPLES:: code:: -(some example code) +//Make 2 sources to be interpolated +( +b = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi).sin * 0.1})); +c = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi / 2).sin * 0.1})); +d = Buffer.new +) + +//make an sound interpolating their spectrum +FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,action:{"Ding".postln}) + +// listen to the source and the result +b.play +c.play +d.play :: diff --git a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd index 977c6ff..c365365 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd @@ -18,9 +18,8 @@ w = Window.new("Viewer", Rect(100,Window.screenBounds.height - 400, 310, 310)). v = View.new(w,Rect(0,0, 310, 310)); //creates a function that reacts to mousedown -// v.mouseMoveAction = {|view, x, y|myx=x;myy=y;w.refresh;}; v.mouseDownAction = {|view, x, y|myx=x;myy=y;w.refresh; - myx.postln;myy.postln; + // myx.postln;myy.postln; Routine{ b.setn(0,[myx,myy]); s.sync; From 83bd6b70c136cc0cd816b662df174be2f0a3226d Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 23 Jan 2020 11:33:36 +0000 Subject: [PATCH 062/550] norm+std: corrected the class casting of the dataset --- release-packaging/Classes/FluidNormalize.sc | 4 ++-- release-packaging/Classes/FluidStandardize.sc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 9a2aa0e..3d87fc7 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -9,11 +9,11 @@ FluidNormalize : FluidManipulationClient { } fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset.asUGenInput],action); + this.pr_sendMsg(\fit,[dataset.asString],action); } normalize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\normalize,[sourceDataset.asUGenInput, destDataset.asUGenInput],action); + this.pr_sendMsg(\normalize,[sourceDataset.asString, destDataset.asString],action); } normalizePoint{|sourceBuffer, destBuffer, action| diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index efa6bd9..19409eb 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -1,11 +1,11 @@ FluidStandardize : FluidManipulationClient { fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset.asUGenInput],action); + this.pr_sendMsg(\fit,[dataset.asString],action); } standardize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\standardize,[sourceDataset.asUGenInput, destDataset.asUGenInput],action); + this.pr_sendMsg(\standardize,[sourceDataset.asString, destDataset.asString],action); } standardizePoint{|sourceBuffer, destBuffer, action| From b5589c13c6f4a6e50d66c7928225532bcf98f836 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 23 Jan 2020 12:10:25 +0000 Subject: [PATCH 063/550] now a new super simple example of scaling (norm and std) --- ...-normalization-standardization-example.scd | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd new file mode 100644 index 0000000..2bebf50 --- /dev/null +++ b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -0,0 +1,116 @@ +( +// set some variables +~nb_of_dim = 10; +~dataset.clear; +~dataset = FluidDataSet(s,\test,~nb_of_dim); +) + +( +// fill up the dataset with 20 entries of 10 column/dimension/descriptor value each. The naming of the item's label is arbitrary as usual +20.do({ + arg i; + Buffer.loadCollection(s,Array.fill(~nb_of_dim,{rrand(0.0,100.0)}),action:{ + arg buf; + ~dataset.addPoint("point-"++i.asInteger.asString,buf); + buf.free; + }); +}); +) + +// make a buf for getting points back +~query_buf = Buffer.alloc(s,~nb_of_dim); + +// look at a point to see that it has points in it +~dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); + +// look at another point to make sure it's different... +~dataset.getPoint("point-7",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); + +/////////////////////////////////////////////////////// +// exploring full dataset normalization and standardization + +// make a FluidNormalize +~normalize = FluidNormalize(s,0,1); + +// fits the dataset to find the coefficients +~normalize.fit(~dataset,{"done".postln;}); // throws error + +// making an empty 'normed_dataset' which is required for the normalize function +~normed_dataset = FluidDataSet(s,\normed,~nb_of_dim); + +// normalize the full dataset +~normalize.normalize(~dataset,~normed_dataset,{"done".postln;}); // throws error + +// look at a point to see that it has points in it +~normed_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); +// 10 numbers between 0.0 and 1.0 where each column/dimension/descriptor is certain to have at least one item on which it is 0 and one on which it is 1 +// query a few more for fun + +// try FluidStandardize +~standardize = FluidStandardize(s); + +// fits the dataset to find the coefficients +~standardize.fit(~dataset,{"done".postln;}); + +// standardize the full dataset +~standardized_dataset = FluidDataSet(s,\standardized,~nb_of_dim); +~standardize.standardize(~dataset,~standardized_dataset,{"done".postln;}); + +// look at a point to see that it has points in it +~standardized_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); +// 10 numbers that are standardize, which mean that, for each column/dimension/descriptor, the average of all the points will be 0. and the standard deviation 1. + +///////////////////////////////////////////////////// +// exploring point querying conceepts via norm and std + +// Once a dataset is normalized / standardized, query points have to be scaled accordingly to be used in distance measurement. In our instance, values were originally between 0 and 100, and now they will be between 0 and 1 (norm), or their average will be 0. (std). If we have data that we want to match from a similar ranging input, which is usually the case, we will need to normalize the searching point in each dimension using the same coefficients. + +// first, make sure you have run all the code above, since we will query these datasets + +// get a know point as a query point +~dataset.getPoint("point-7",~query_buf); + +// find the 2 points with the shortest distances in the dataset +~tree = FluidKDTree.new(s); +~tree.fit(~dataset) +~tree.kNearest(~query_buf,2, {|x| ("Labels:" + x).postln}); +~tree.kNearestDist(~query_buf,2, {|x| ("Distances:" + x).postln}); +// its nearest neighbourg is itself: it should be itself and the distance should be 0. The second point is depending on your input dataset. + +// normalise that point (~query_buf) to be at the right scale +~normbuf = Buffer.alloc(s,~nb_of_dim); +~normalize.normalizePoint(~query_buf,~normbuf); +~normbuf.getn(0,~nb_of_dim,{arg vec;vec.postln;}); + +// make a tree of the normalized database and query with the normalize buffer +~normtree = FluidKDTree.new(s); +~normtree.fit(~normed_dataset) +~normtree.kNearest(~normbuf,2, {|x| ("Labels:" + x).postln}); +~normtree.kNearestDist(~normbuf,2, {|x| ("Distances:" + x).postln}); +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will have changed. The distance is now different too + +// standardize that same point (~query_buf) to be at the right scale +~stdbuf = Buffer.alloc(s,~nb_of_dim); +~standardize.standardizePoint(~query_buf,~stdbuf); +~stdbuf.getn(0,~nb_of_dim,{arg vec;vec.postln;}); + +// make a tree of the standardized database and query with the normalize buffer +~stdtree = FluidKDTree.new(s); +~stdtree.fit(~standardized_dataset) +~stdtree.kNearest(~stdbuf,2, {|x| ("Labels:" + x).postln}); +~stdtree.kNearestDist(~stdbuf,2, {|x| ("Distances:" + x).postln}); +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will have changed yet again. The distance is also different too + +// where it starts to be interesting is when we query points that are not in our original dataset + +// fill with known values (50.0 for each of the 10 column/dimension/descriptor, aka the theoretical middle point of the multidimension space) This could be anything but it is fun to aim in the middle. +~query_buf.fill(0,~nb_of_dim,50); + +// normalize and standardize the query buffer. Note that we do not need to fit since we have not added a point to our reference dataset +~normalize.normalizePoint(~query_buf,~normbuf); +~standardize.standardizePoint(~query_buf,~stdbuf); + +//query the single nearest neighbourg via 3 different data scaling. Depending on the random source at the begining, you will get small to large differences between the 3 answers! +~tree.kNearest(~query_buf,1, {|x| ("Original:" + x).post;~tree.kNearestDist(~query_buf,1, {|x| (" with a distance of " + x).postln});}); +~normtree.kNearest(~normbuf,1, {|x| ("Normalized:" + x).post;~normtree.kNearestDist(~normbuf,1, {|x| (" with a distance of " + x).postln});}); +~stdtree.kNearest(~stdbuf,1, {|x| ("Standardized:" + x).post; ~stdtree.kNearestDist(~stdbuf,1, {|x| (" with a distance of " + x).postln});}); \ No newline at end of file From a4d68ade37b8899468de58dec0582aac055563f0 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 23 Jan 2020 18:33:14 +0000 Subject: [PATCH 064/550] removed the errors and added a nuance --- ...super-simple-normalization-standardization-example.scd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd index 2bebf50..47c5539 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -33,13 +33,13 @@ ~normalize = FluidNormalize(s,0,1); // fits the dataset to find the coefficients -~normalize.fit(~dataset,{"done".postln;}); // throws error +~normalize.fit(~dataset,{"done".postln;}); // making an empty 'normed_dataset' which is required for the normalize function ~normed_dataset = FluidDataSet(s,\normed,~nb_of_dim); // normalize the full dataset -~normalize.normalize(~dataset,~normed_dataset,{"done".postln;}); // throws error +~normalize.normalize(~dataset,~normed_dataset,{"done".postln;}); // look at a point to see that it has points in it ~normed_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); @@ -87,7 +87,7 @@ ~normtree.fit(~normed_dataset) ~normtree.kNearest(~normbuf,2, {|x| ("Labels:" + x).postln}); ~normtree.kNearestDist(~normbuf,2, {|x| ("Distances:" + x).postln}); -// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will have changed. The distance is now different too +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will probably have changed. The distance is now different too // standardize that same point (~query_buf) to be at the right scale ~stdbuf = Buffer.alloc(s,~nb_of_dim); @@ -99,7 +99,7 @@ ~stdtree.fit(~standardized_dataset) ~stdtree.kNearest(~stdbuf,2, {|x| ("Labels:" + x).postln}); ~stdtree.kNearestDist(~stdbuf,2, {|x| ("Distances:" + x).postln}); -// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will have changed yet again. The distance is also different too +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will probably have changed yet again. The distance is also different too // where it starts to be interesting is when we query points that are not in our original dataset From a78fa1f856ec74777346356d5795009e148f5d55 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sat, 25 Jan 2020 12:10:25 +0000 Subject: [PATCH 065/550] typoesses --- .../super-simple-normalization-standardization-example.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd index 47c5539..62193a4 100644 --- a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd +++ b/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -60,8 +60,8 @@ ~standardized_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); // 10 numbers that are standardize, which mean that, for each column/dimension/descriptor, the average of all the points will be 0. and the standard deviation 1. -///////////////////////////////////////////////////// -// exploring point querying conceepts via norm and std +//////////////////////////////////////////////////// +// exploring point querying concepts via norm and std // Once a dataset is normalized / standardized, query points have to be scaled accordingly to be used in distance measurement. In our instance, values were originally between 0 and 100, and now they will be between 0 and 1 (norm), or their average will be 0. (std). If we have data that we want to match from a similar ranging input, which is usually the case, we will need to normalize the searching point in each dimension using the same coefficients. From e13202653e0311c0bfc5ca9398a81b08b7568efa Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 27 Mar 2020 14:45:02 +0000 Subject: [PATCH 066/550] Ensure all examples are .scd and move up to top of release packaging --- .../buffer_compositing/fileiterator-2passes.scd} | 0 .../buffer_compositing/fileiterator.scd} | 0 .../{ignore => }/Examples/dataset/myfirstdataset.scd | 0 .../{ignore => }/Examples/dataset/super-simple-1D-example.scd | 0 .../{ignore => }/Examples/dataset/super-simple-1D-example2.scd | 0 .../Examples/dataset/super-simple-classifier-example.scd | 0 .../super-simple-normalization-standardization-example.scd | 0 .../Examples/dataset/super-simple-regressor-example.scd | 0 release-packaging/{ignore => }/Examples/nb_of_slices.scd | 0 release-packaging/{ignore => }/Examples/nmf/JiT-NMF.scd | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename release-packaging/{ignore/Examples/buffer_compositing/fileiterator-2passes.sc => Examples/buffer_compositing/fileiterator-2passes.scd} (100%) rename release-packaging/{ignore/Examples/buffer_compositing/fileiterator.sc => Examples/buffer_compositing/fileiterator.scd} (100%) rename release-packaging/{ignore => }/Examples/dataset/myfirstdataset.scd (100%) rename release-packaging/{ignore => }/Examples/dataset/super-simple-1D-example.scd (100%) rename release-packaging/{ignore => }/Examples/dataset/super-simple-1D-example2.scd (100%) rename release-packaging/{ignore => }/Examples/dataset/super-simple-classifier-example.scd (100%) rename release-packaging/{ignore => }/Examples/dataset/super-simple-normalization-standardization-example.scd (100%) rename release-packaging/{ignore => }/Examples/dataset/super-simple-regressor-example.scd (100%) rename release-packaging/{ignore => }/Examples/nb_of_slices.scd (100%) rename release-packaging/{ignore => }/Examples/nmf/JiT-NMF.scd (100%) diff --git a/release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc b/release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd similarity index 100% rename from release-packaging/ignore/Examples/buffer_compositing/fileiterator-2passes.sc rename to release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd diff --git a/release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc b/release-packaging/Examples/buffer_compositing/fileiterator.scd similarity index 100% rename from release-packaging/ignore/Examples/buffer_compositing/fileiterator.sc rename to release-packaging/Examples/buffer_compositing/fileiterator.scd diff --git a/release-packaging/ignore/Examples/dataset/myfirstdataset.scd b/release-packaging/Examples/dataset/myfirstdataset.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/myfirstdataset.scd rename to release-packaging/Examples/dataset/myfirstdataset.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd b/release-packaging/Examples/dataset/super-simple-1D-example.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/super-simple-1D-example.scd rename to release-packaging/Examples/dataset/super-simple-1D-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/Examples/dataset/super-simple-1D-example2.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/super-simple-1D-example2.scd rename to release-packaging/Examples/dataset/super-simple-1D-example2.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/Examples/dataset/super-simple-classifier-example.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/super-simple-classifier-example.scd rename to release-packaging/Examples/dataset/super-simple-classifier-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/super-simple-normalization-standardization-example.scd rename to release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd diff --git a/release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd b/release-packaging/Examples/dataset/super-simple-regressor-example.scd similarity index 100% rename from release-packaging/ignore/Examples/dataset/super-simple-regressor-example.scd rename to release-packaging/Examples/dataset/super-simple-regressor-example.scd diff --git a/release-packaging/ignore/Examples/nb_of_slices.scd b/release-packaging/Examples/nb_of_slices.scd similarity index 100% rename from release-packaging/ignore/Examples/nb_of_slices.scd rename to release-packaging/Examples/nb_of_slices.scd diff --git a/release-packaging/ignore/Examples/nmf/JiT-NMF.scd b/release-packaging/Examples/nmf/JiT-NMF.scd similarity index 100% rename from release-packaging/ignore/Examples/nmf/JiT-NMF.scd rename to release-packaging/Examples/nmf/JiT-NMF.scd From c72e04a8c639197bd16f409bdc8b69d2cd89b422 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 2 Apr 2020 01:07:32 +0100 Subject: [PATCH 067/550] Make it compile post-merge --- include/FluidSCWrapper.hpp | 26 +++++++++++++------------- scripts/target_post.cmake | 1 + src/FluidAmpGate/FluidAmpGate.cpp | 2 +- src/FluidAmpSlice/FluidAmpSlice.cpp | 2 +- src/FluidBufStats/FluidBufStats.cpp | 2 +- src/FluidHPSS/FluidHPSS.cpp | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index b9c6ad7..2d990c6 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -66,8 +66,8 @@ struct FloatControlsIter mCount = 0; } - size_t size() const noexcept { return mSize; } - size_t remain() + index size() const noexcept { return mSize; } + index remain() { return mSize - mCount; } @@ -493,8 +493,8 @@ class FluidSCWrapperImpl // Make base class(es), full of CRTP mixin goodness template using FluidSCWrapperBase = - FluidSCWrapperImpl, isNonRealTime, - isRealTime>; + FluidSCWrapperImpl, typename Client::isNonRealTime, + typename Client::isRealTime>; } // namespace impl @@ -521,7 +521,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static auto fromArgs(World *w, FloatControlsIter& args, std::string, int) { //first is string size, then chars - index size = args.next(); + index size = static_cast(args.next()); char* chunk = static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1)); if (!chunk) { @@ -540,7 +540,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template static std::enable_if_t::value,T> - fromArgs(World *, FloatControlsIter& args, T, int) { return args.next(); } + fromArgs(World *, FloatControlsIter& args, T, int) { return static_cast(args.next()); } template static std::enable_if_t::value,T> @@ -626,27 +626,27 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static std::enable_if_t::value||std::is_floating_point::value,size_t> allocSize(T){ return 1; } - static size_t allocSize(std::string s){ return s.size() + 1; } //put null char at end when we send + static index allocSize(std::string s){ return asSigned(s.size()) + 1; } //put null char at end when we send - static size_t allocSize(FluidTensor s) + static index allocSize(FluidTensor s) { - size_t count = 0; + index count = 0; for(auto& str: s) count += (str.size() + 1); return count; } template - static size_t allocSize(FluidTensor s) { return s.size() ; } + static index allocSize(FluidTensor s) { return asSigned(s.size()); } template - static std::tuple,size_t> allocSize(std::tuple&& t) + static std::tuple,index> allocSize(std::tuple&& t) { return allocSizeImpl(std::forward(t), std::index_sequence_for()); }; template - static std::tuple,size_t> allocSizeImpl(std::tuple&& t,std::index_sequence) + static std::tuple,index> allocSizeImpl(std::tuple&& t,std::index_sequence) { - size_t size{0}; + index size{0}; std::array res; (void)std::initializer_list{(res[Is] = size,size += ToFloatArray::allocSize(std::get(t)),0)...}; return std::make_tuple(res,size); //array of offsets into allocated buffer & total number of floats to alloc diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index 0a6a727..16df631 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -52,6 +52,7 @@ target_include_directories( ${PLUGIN} PRIVATE "${LOCAL_INCLUDES}" + "${FLUID_VERSION_PATH}" "${FLUID_M_PATH}/include/" "${FLUID_M_PATH}/thirdparty" ) diff --git a/src/FluidAmpGate/FluidAmpGate.cpp b/src/FluidAmpGate/FluidAmpGate.cpp index 3b696e2..d8a0c9b 100644 --- a/src/FluidAmpGate/FluidAmpGate.cpp +++ b/src/FluidAmpGate/FluidAmpGate.cpp @@ -18,5 +18,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidAmpGate", ft); + makeSCWrapper("FluidAmpGate", ft); } diff --git a/src/FluidAmpSlice/FluidAmpSlice.cpp b/src/FluidAmpSlice/FluidAmpSlice.cpp index 3ac71fd..c5ceaa0 100644 --- a/src/FluidAmpSlice/FluidAmpSlice.cpp +++ b/src/FluidAmpSlice/FluidAmpSlice.cpp @@ -18,5 +18,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidAmpSlice", ft); + makeSCWrapper("FluidAmpSlice", ft); } diff --git a/src/FluidBufStats/FluidBufStats.cpp b/src/FluidBufStats/FluidBufStats.cpp index c81c117..b5f902c 100644 --- a/src/FluidBufStats/FluidBufStats.cpp +++ b/src/FluidBufStats/FluidBufStats.cpp @@ -18,5 +18,5 @@ PluginLoad(OfflineFluidDecompositionUGens) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidBufStats", ft); + makeSCWrapper("FluidBufStats", ft); } diff --git a/src/FluidHPSS/FluidHPSS.cpp b/src/FluidHPSS/FluidHPSS.cpp index d8db092..d6c8288 100644 --- a/src/FluidHPSS/FluidHPSS.cpp +++ b/src/FluidHPSS/FluidHPSS.cpp @@ -18,5 +18,5 @@ PluginLoad(FluidSTFTUGen) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidHPSS", ft); + makeSCWrapper("FluidHPSS", ft); } From bd7b2bfdb62c1981569477a599500e5d3f8d9c8b Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 2 Apr 2020 13:56:46 +0100 Subject: [PATCH 068/550] update macOS target to 10.8 for now --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 340c0a8..a8d7d63 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ set(FLUID_M_PATH "" CACHE PATH "Optional path to the Fluid fluid_manipulation re if (APPLE) set(CMAKE_OSX_ARCHITECTURES x86_64) set(CMAKE_XCODE_GENERATE_SCHEME ON) - set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) endif() ################################################################################ From 683bb6679177a37a669b725be98cd2bf5550db47 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 2 Apr 2020 15:39:34 +0100 Subject: [PATCH 069/550] Delete implicitly deleted operator --- include/SCBufferAdaptor.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index 5b31b3b..f424890 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -68,9 +68,7 @@ public: SCBufferAdaptor& operator=(const SCBufferAdaptor&) = delete; SCBufferAdaptor(SCBufferAdaptor&&) = default; - SCBufferAdaptor& operator=(SCBufferAdaptor&&) = default; - - + SCBufferAdaptor(index bufnum, World* world, bool rt = false) : NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world) {} From 7159392a9b9e6510d9af3c4045f0c665f6868455 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 2 Apr 2020 17:01:39 +0100 Subject: [PATCH 070/550] Integer conversion for new stuff, formatting --- include/FluidSCWrapper.hpp | 573 +++++++++++++++++++++---------------- 1 file changed, 321 insertions(+), 252 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 2d990c6..c2df32d 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -33,24 +33,23 @@ class FluidSCWrapper; namespace impl { - template - struct AssignBuffer +template +struct AssignBuffer +{ + void operator()(const typename BufferT::type& p, World* w) { - void operator()(const typename BufferT::type &p, World *w) - { - if (auto b = static_cast(p.get())) - b->assignToRT(w); - } - }; + if (auto b = static_cast(p.get())) b->assignToRT(w); + } +}; - template - struct CleanUpBuffer +template +struct CleanUpBuffer +{ + void operator()(const typename BufferT::type& p) { - void operator()(const typename BufferT::type &p) - { - if (auto b = static_cast(p.get())) b->cleanUp(); - } - }; + if (auto b = static_cast(p.get())) b->cleanUp(); + } +}; // Iterate over kr/ir inputs via callbacks from params object @@ -65,12 +64,10 @@ struct FloatControlsIter mValues = vals; mCount = 0; } - + index size() const noexcept { return mSize; } - index remain() - { - return mSize - mCount; - } + index remain() { return mSize - mCount; } + private: float** mValues; index mSize; @@ -163,7 +160,8 @@ public: void next(int) { - mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn()); + mControlsIterator.reset(mInBuf + mSpecialIndex + + 1); // mClient.audioChannelsIn()); Wrapper::setParams( mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params @@ -339,7 +337,8 @@ public: // cancels using u_cmd, this is what will fire if (r.status() == Result::Status::kCancelled) { - std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; + std::cout << Wrapper::getName() << ": Processing cancelled" + << std::endl; w->mCancelled = true; return false; } @@ -492,9 +491,9 @@ class FluidSCWrapperImpl // Make base class(es), full of CRTP mixin goodness template -using FluidSCWrapperBase = - FluidSCWrapperImpl, typename Client::isNonRealTime, - typename Client::isRealTime>; +using FluidSCWrapperBase = FluidSCWrapperImpl, + typename Client::isNonRealTime, + typename Client::isRealTime>; } // namespace impl @@ -506,303 +505,376 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { using FloatControlsIter = impl::FloatControlsIter; - + template struct ParamReader { - static auto fromArgs(World *, sc_msg_iter* args, std::string, int) - { + static auto fromArgs(World*, sc_msg_iter* args, std::string, int) + { const char* recv = args->gets(""); - - return std::string(recv?recv:""); - } - - static auto fromArgs(World *w, FloatControlsIter& args, std::string, int) - { - //first is string size, then chars - index size = static_cast(args.next()); - char* chunk = static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1)); - - if (!chunk) { - std::cout << "ERROR: " << FluidSCWrapper::getName() << ": RT memory allocation failed\n"; - return std::string{""}; - } - - for(index i = 0; i < size; ++i) - chunk[i] = static_cast(args.next()); - - chunk[size] = 0; //terminate string - - return std::string{chunk}; + + return std::string(recv ? recv : ""); } - - - template - static std::enable_if_t::value,T> - fromArgs(World *, FloatControlsIter& args, T, int) { return static_cast(args.next()); } - - template - static std::enable_if_t::value,T> - fromArgs(World *, FloatControlsIter& args, T, int) { return args.next(); } - - template - static std::enable_if_t::value,T> - fromArgs(World *, sc_msg_iter* args, T, int defVal) { return args->geti(defVal); } - template - static std::enable_if_t::value,T> - fromArgs(World *, sc_msg_iter* args, T, int) { return args->getf(); } + static auto fromArgs(World* w, FloatControlsIter& args, std::string, int) + { + // first is string size, then chars + index size = static_cast(args.next()); + char* chunk = + static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc( + w, asUnsigned(size + 1))); + + if (!chunk) + { + std::cout << "ERROR: " << FluidSCWrapper::getName() + << ": RT memory allocation failed\n"; + return std::string{""}; + } + + for (index i = 0; i < size; ++i) + chunk[i] = static_cast(args.next()); + + chunk[size] = 0; // terminate string - static auto fromArgs(World *w, ArgType args, BufferT::type&, int) + return std::string{chunk}; + } + + + template + static std::enable_if_t::value, T> + fromArgs(World*, FloatControlsIter& args, T, int) { - typename LongT::type bufnum = static_cast(ParamReader::fromArgs(w, args, typename LongT::type(), -1)); - return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); + return static_cast(args.next()); } - - static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int) + + template + static std::enable_if_t::value, T> + fromArgs(World*, FloatControlsIter& args, T, int) + { + return args.next(); + } + + template + static std::enable_if_t::value, T> + fromArgs(World*, sc_msg_iter* args, T, int defVal) + { + return args->geti(defVal); + } + + template + static std::enable_if_t::value, T> + fromArgs(World*, sc_msg_iter* args, T, int) + { + return args->getf(); + } + + static auto fromArgs(World* w, ArgType args, BufferT::type&, int) + { + typename LongT::type bufnum = static_cast( + ParamReader::fromArgs(w, args, typename LongT::type(), -1)); + return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) + : nullptr); + } + + static auto fromArgs(World* w, ArgType args, InputBufferT::type&, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } - - template - static std::enable_if_t::value,P> - fromArgs(World *w, ArgType args, P&, int) + + template + static std::enable_if_t::value, P> + fromArgs(World* w, ArgType args, P&, int) { - return {fromArgs(w, args, std::string{}, 0).c_str()}; + return {fromArgs(w, args, std::string{}, 0).c_str()}; } - }; - - - + + // Iterate over arguments via callbacks from params object template struct Setter { - static constexpr index argSize = C::getParameterDescriptors().template get().fixedSize; - - typename T::type operator()(World *w, ArgType args) + static constexpr index argSize = + C::getParameterDescriptors().template get().fixedSize; + + typename T::type operator()(World* w, ArgType args) { - //Just return default if there's nothing left to grab - if(args.remain() == 0) + // Just return default if there's nothing left to grab + if (args.remain() == 0) { - std::cout << "WARNING: " << getName() << " received fewer parameters than expected\n"; + std::cout << "WARNING: " << getName() + << " received fewer parameters than expected\n"; return C::getParameterDescriptors().template makeValue(); } - + ParamLiteralConvertor a; using LiteralType = typename ParamLiteralConvertor::LiteralType; for (index i = 0; i < argSize; i++) - a[i] = static_cast(ParamReader::fromArgs(w, args, a[0], 0)); + a[i] = static_cast( + ParamReader::fromArgs(w, args, a[0], 0)); return a.value(); } }; - + template using ArgumentSetter = Setter; template using ControlSetter = Setter; - - //CryingEmoji.png: SC API hides all the useful functions for sending - //replies back to the language with things like, uh, strings and stuff. - //We have Node_SendReply, which assumes you are sending an array of float, - //and must be called only from the RT thread. Thanks. - //So, we do in reverse what the SendReply Ugen does, and parse - //an array of floats as characters in the language. VomitEmoji.png - + + // CryingEmoji.png: SC API hides all the useful functions for sending + // replies back to the language with things like, uh, strings and stuff. + // We have Node_SendReply, which assumes you are sending an array of float, + // and must be called only from the RT thread. Thanks. + // So, we do in reverse what the SendReply Ugen does, and parse + // an array of floats as characters in the language. VomitEmoji.png + struct ToFloatArray { - static size_t allocSize(typename BufferT::type){ return 1; } - - template - static std::enable_if_t::value||std::is_floating_point::value,size_t> - allocSize(T){ return 1; } - - static index allocSize(std::string s){ return asSigned(s.size()) + 1; } //put null char at end when we send - - static index allocSize(FluidTensor s) + static index allocSize(typename BufferT::type) { return 1; } + + template + static std::enable_if_t< + std::is_integral::value || std::is_floating_point::value, index> + allocSize(T) + { + return 1; + } + + static index allocSize(std::string s) + { + return asSigned(s.size()) + 1; + } // put null char at end when we send + + static index allocSize(FluidTensor s) { index count = 0; - for(auto& str: s) count += (str.size() + 1); + for (auto& str : s) count += (str.size() + 1); return count; } - template - static index allocSize(FluidTensor s) { return asSigned(s.size()); } - - template - static std::tuple,index> allocSize(std::tuple&& t) + + template + static index allocSize(FluidTensor s) { - return allocSizeImpl(std::forward(t), std::index_sequence_for()); - }; - - template - static std::tuple,index> allocSizeImpl(std::tuple&& t,std::index_sequence) + return s.size(); + } + + template + static std::tuple, index> + allocSize(std::tuple&& t) { - index size{0}; - std::array res; - (void)std::initializer_list{(res[Is] = size,size += ToFloatArray::allocSize(std::get(t)),0)...}; - return std::make_tuple(res,size); //array of offsets into allocated buffer & total number of floats to alloc + return allocSizeImpl(std::forward(t), + std::index_sequence_for()); }; - - static void convert(float* f, typename BufferT::type buf) { f[0] = static_cast(buf.get())->bufnum(); } - - template - static std::enable_if_t::value||std::is_floating_point::value> - convert(float* f, T x) { f[0] = static_cast(x); } - + + template + static std::tuple, index> + allocSizeImpl(std::tuple&& t, std::index_sequence) + { + index size{0}; + std::array res; + (void) std::initializer_list{ + (res[Is] = size, size += ToFloatArray::allocSize(std::get(t)), + 0)...}; + return std::make_tuple(res, + size); // array of offsets into allocated buffer & + // total number of floats to alloc + }; + + static void convert(float* f, typename BufferT::type buf) + { + f[0] = static_cast(buf.get())->bufnum(); + } + + template + static std::enable_if_t::value || + std::is_floating_point::value> + convert(float* f, T x) + { + f[0] = static_cast(x); + } + static void convert(float* f, std::string s) { std::copy(s.begin(), s.end(), f); - f[s.size()] = 0; //terminate + f[s.size()] = 0; // terminate } - static void convert(float* f, FluidTensor s) + static void convert(float* f, FluidTensor s) { - for(auto& str: s) + for (auto& str : s) { std::copy(str.begin(), str.end(), f); f += str.size(); *f++ = 0; } } - template - static void convert(float* f, FluidTensor s) + template + static void convert(float* f, FluidTensor s) { - static_assert(std::is_convertible::value,"Can't convert this to float output"); - std::copy(s.begin(), s.end(), f); + static_assert(std::is_convertible::value, + "Can't convert this to float output"); + std::copy(s.begin(), s.end(), f); } - - template - static void convert(float* f,std::tuple&& t, std::array offsets, std::index_sequence) + + template + static void convert(float* f, std::tuple&& t, + std::array offsets, + std::index_sequence) { - (void)std::initializer_list{(convert(f + offsets[Is],std::get(t)),0)...}; + (void) std::initializer_list{ + (convert(f + offsets[Is], std::get(t)), 0)...}; } }; - - - //So, to handle a message to a plugin we will need to - // (1) Launch the invovation of the message on the SC NRT Queue using FIFO Message - // (2) Run the actual function (maybe asynchronously, in our own thread) - // (3) Launch an asynchronous command to send the reply back (in Stage 3) - - template + + + // So, to handle a message to a plugin we will need to + // (1) Launch the invovation of the message on the SC NRT Queue using FIFO + // Message (2) Run the actual function (maybe asynchronously, in our own + // thread) (3) Launch an asynchronous command to send the reply back (in Stage + // 3) + + template struct MessageDispatch { static constexpr size_t Message = N; - FluidSCWrapper* wrapper; - ArgTuple args; - Ret result; - std::string name; + FluidSCWrapper* wrapper; + ArgTuple args; + Ret result; + std::string name; }; - - //Sets up a single /u_cmd - template + + // Sets up a single /u_cmd + template struct SetupMessage { void operator()(const T& message) { - auto ft = getInterfaceTable(); - ft->fDefineUnitCmd(getName(), message.name, launchMessage); + auto ft = getInterfaceTable(); + ft->fDefineUnitCmd(getName(), message.name, launchMessage); } }; - template - static void launchMessage(Unit* u,sc_msg_iter* args) + template + static void launchMessage(Unit* u, sc_msg_iter* args) { FluidSCWrapper* x = static_cast(u); - using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; - launchMessageImpl(x,args,IndexList()); + using IndexList = + typename Client::MessageSetType::template MessageDescriptorAt< + N>::IndexList; + launchMessageImpl(x, args, IndexList()); } - template - static void launchMessageImpl(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) + template + static void launchMessageImpl(FluidSCWrapper* x, sc_msg_iter* inArgs, + std::index_sequence) { - using MessageDescriptor = typename Client::MessageSetType::template MessageDescriptorAt; + using MessageDescriptor = + typename Client::MessageSetType::template MessageDescriptorAt; using ArgTuple = typename MessageDescriptor::ArgumentTypes; using ReturnType = typename MessageDescriptor::ReturnType; using IndexList = typename MessageDescriptor::IndexList; - using MessageData = MessageDispatch; - - auto ft = getInterfaceTable(); - void* msgptr = ft->fRTAlloc(x->mWorld,sizeof(MessageData)); - MessageData* msg = new(msgptr) MessageData; - ArgTuple& args = msg->args; - (void)std::initializer_list{(std::get(args) = ParamReader::fromArgs(x->mWorld,inArgs,std::get(args),0),0)...}; - + using MessageData = MessageDispatch; + + auto ft = getInterfaceTable(); + void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); + MessageData* msg = new (msgptr) MessageData; + ArgTuple& args = msg->args; + (void) std::initializer_list{ + (std::get(args) = ParamReader::fromArgs( + x->mWorld, inArgs, std::get(args), 0), + 0)...}; + msg->name = '/' + Client::getMessageDescriptors().template name(); msg->wrapper = x; x->mDone = false; - ft->fDoAsynchronousCommand(x->mWorld, nullptr, getName(), msg, - [](World*, void* data) //NRT thread: invocation - { - MessageData* m = static_cast(data); - m->result = ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; - - if(!m->result.ok()) - { - printResult(m->wrapper, m->result); - return false; - } - return true; - }, - [](World* world, void* data) //RT thread: response - { - MessageData* m = static_cast(data); - MessageDescriptor::template forEachArg(m->args, world); - messageOutput(m->wrapper,m->name,m->result); - return true; - } - , nullptr, //NRT Thread: No-op - [](World* w, void* data) //RT thread: clean up - { - getInterfaceTable()->fRTFree(w,data); - }, - 0, nullptr); - } - - template //Call from NRT - static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, std::index_sequence) - { - return x->mClient.template invoke(x->mClient,std::get(args)...); - } - - template //call from RT - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult& result) - { - auto ft = getInterfaceTable(); - //allocate return values - size_t numArgs = ToFloatArray::allocSize(static_cast(result)); - float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); - //copy return data - ToFloatArray::convert(values,static_cast(result)); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); - } - - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult&) + ft->fDoAsynchronousCommand( + x->mWorld, nullptr, getName(), msg, + [](World*, void* data) // NRT thread: invocation + { + MessageData* m = static_cast(data); + m->result = + ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; + + if (!m->result.ok()) + { + printResult(m->wrapper, m->result); + return false; + } + return true; + }, + [](World* world, void* data) // RT thread: response + { + MessageData* m = static_cast(data); + MessageDescriptor::template forEachArg(m->args, + world); + messageOutput(m->wrapper, m->name, m->result); + return true; + }, + nullptr, // NRT Thread: No-op + [](World* w, void* data) // RT thread: clean up + { getInterfaceTable()->fRTFree(w, data); }, + 0, nullptr); + } + + template // Call from NRT + static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, + std::index_sequence) { - auto ft = getInterfaceTable(); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + return x->mClient.template invoke(x->mClient, std::get(args)...); } - - template - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult>& result) + + template // call from RT + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult& result) { auto ft = getInterfaceTable(); - std::array offsets; - size_t numArgs; - std::tie(offsets,numArgs) = ToFloatArray::allocSize(static_cast>(result)); - float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); - ToFloatArray::convert(values,std::tuple(result),offsets,std::index_sequence_for()); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); + // allocate return values + index numArgs = ToFloatArray::allocSize(static_cast(result)); + + float* values = static_cast( + ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + + // copy return data + ToFloatArray::convert(values, static_cast(result)); + + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + static_cast(numArgs), values); + } + + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult&) + { + auto ft = getInterfaceTable(); + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + } + + template + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult>& result) + { + auto ft = getInterfaceTable(); + std::array offsets; + index numArgs; + std::tie(offsets, numArgs) = + ToFloatArray::allocSize(static_cast>(result)); + + float* values = static_cast( + ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + + ToFloatArray::convert(values, std::tuple(result), offsets, + std::index_sequence_for()); + + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + static_cast(numArgs), values); } - static void doVersion(Unit*, sc_msg_iter*) @@ -835,7 +907,7 @@ public: getName(name); getInterfaceTable(ft); impl::FluidSCWrapperBase::setup(ft, name); - Client::getMessageDescriptors().template iterate(); + Client::getMessageDescriptors().template iterate(); ft->fDefineUnitCmd(name, "version", doVersion); } @@ -855,40 +927,37 @@ public: "and SC sources are different versions" << std::endl; } - + return p; } - + static void printResult(FluidSCWrapper* x, Result& r) { if (!x) return; switch (r.status()) { - case Result::Status::kWarning: - { - if(x->mWorld->mVerbosity > 0) - std::cout << "WARNING: " << r.message().c_str() << '\n'; - break; - } - case Result::Status::kError: - { - std::cout << "ERROR: " << r.message().c_str() << '\n'; - break; - } - case Result::Status::kCancelled: - { - std::cout << "Task cancelled\n" << '\n'; - break; - } - default: { - } + case Result::Status::kWarning: { + if (x->mWorld->mVerbosity > 0) + std::cout << "WARNING: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kError: { + std::cout << "ERROR: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kCancelled: { + std::cout << "Task cancelled\n" << '\n'; + break; + } + default: { + } } } }; -template -void makeSCWrapper(const char *name, InterfaceTable *ft) +template +void makeSCWrapper(const char* name, InterfaceTable* ft) { FluidSCWrapper::setup(ft, name); } From 41c4fda0f51a335b5942ba69e307e8b132b84976 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Apr 2020 09:07:17 +0100 Subject: [PATCH 071/550] (hopefully) Temporary fix for incompatibility between master / tb2 parameter counting --- include/FluidSCWrapper.hpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index c2df32d..695cfc3 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -914,19 +914,21 @@ public: static auto& setParams(ParameterSetType& p, bool verbose, World* world, FloatControlsIter& inputs, bool constrain = false) { + //TODO: Regain this robustness if possible? + // We won't even try and set params if the arguments don't match - if (inputs.size() == C::getParameterDescriptors().count()) - { + // if (inputs.size() == C::getParameterDescriptors().count()) + // { p.template setParameterValues(verbose, world, inputs); if (constrain) p.constrainParameterValues(); - } - else - { - std::cout << "ERROR: " << getName() - << ": parameter count mismatch. Perhaps your binary plugins " - "and SC sources are different versions" - << std::endl; - } + // } + // else + // { + // std::cout << "ERROR: " << getName() + // << ": parameter count mismatch. Perhaps your binary plugins " + // "and SC sources are different versions" + // << std::endl; + // } return p; } From e79b846be6899ebaf8761f60b75b56543d25c87e Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 Apr 2020 11:00:35 +0100 Subject: [PATCH 072/550] Remove dims from FluidDataSet --- release-packaging/Classes/FluidDataSet.sc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index fb4b696..44afb17 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -2,12 +2,12 @@ FluidDataSet : FluidManipulationClient { var <>synth, <>server, <>id; - *kr{ |name, dims| - ^this.new1('control',name, dims); + *kr{ |name| + ^this.new1('control',name); } - *new { |server,name,dims| - ^super.new(server,name,dims); + *new { |server,name| + ^super.new(server,name); } init { |name, dims| From fd13fff2de6479de5fcf6d73a37518f35547d811 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 Apr 2020 15:54:06 +0100 Subject: [PATCH 073/550] Add strict argument count and type checking to messages, to avoid server crashes. --- include/FluidSCWrapper.hpp | 151 ++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 695cfc3..dec2057 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -510,6 +510,86 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase struct ParamReader { + static const char* oscTagToString(char tag) + { + switch (tag) + { + case 'i': return "integer"; break; + case 'f': return "float"; break; + case 'd': return "double"; break; + case 's': return "string"; break; + case 'b': return "blob"; break; + case 't': return "time tag"; break; + default: return "unknown type"; + } + } + + static const char* argTypeToString(std::string&) + { + return "string"; + } + + template + static std::enable_if_t::value, const char*> + argTypeToString(T&) + { + return "integer"; + } + + template + static std::enable_if_t::value, const char*> + argTypeToString(T&) + { + return "float"; + } + + static const char* argTypeToString(BufferT::type&) + { + return "buffer"; + } + + static const char* argTypeToString(InputBufferT::type&) + { + return "buffer"; + } + + template + static std::enable_if_t::value,const char*> + argTypeToString(P&) + { + return "shared_object"; //not ideal + } + + static bool argTypeOK(std::string&, char tag) + { + return tag == 's'; + } + + template + static std::enable_if_t::value + || std::is_floating_point::value, bool> + argTypeOK(T&, char tag) + { + return tag == 'i' || tag == 'f' || tag == 'd'; + } + + static bool argTypeOK(BufferT::type&, char tag) + { + return tag == 'i'; + } + + static bool argTypeOK(InputBufferT::type&, char tag) + { + return tag == 'i'; + } + + template + static std::enable_if_t::value,bool> + argTypeOK(P&, char tag) + { + return tag == 's'; + } + static auto fromArgs(World*, sc_msg_iter* args, std::string, int) { const char* recv = args->gets(""); @@ -785,14 +865,81 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto ft = getInterfaceTable(); void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); MessageData* msg = new (msgptr) MessageData; + msg->name = '/' + Client::getMessageDescriptors().template name(); + msg->wrapper = x; ArgTuple& args = msg->args; + + // type check OSC message + + std::string tags(inArgs->tags + inArgs->count); + bool willContinue = true; + bool typesMatch = true; + + constexpr size_t expectedArgCount = std::tuple_size::value; + + if(tags.size() > expectedArgCount) + { + std::cout << "WARNING: " << msg->name << " received more arguments than expected (got " + << tags.size() << ", expect " << expectedArgCount << ")\n"; + } + + if(tags.size() < expectedArgCount) + { + std::cout << "ERROR: " << msg->name << " received fewer arguments than expected (got " + << tags.size() << ", expect " << expectedArgCount << ")\n"; + willContinue = false; + } + + auto tagsIter = tags.begin(); + auto tagsEnd = tags.end(); + ForEach(args,[&typesMatch,&tagsIter,&tagsEnd](auto& x){ + if(tagsIter == tagsEnd) + { + typesMatch = false; + return; + } + char t = *(tagsIter++); + typesMatch = typesMatch && ParamReader::argTypeOK(x,t); + }); + + willContinue = willContinue && typesMatch; + + if(!typesMatch) + { + auto& report = std::cout; + report << "ERROR: " << msg->name << " type signature incorrect.\nExpect: ("; + size_t i{0}; + ForEach(args, [&i](auto& x){ + report << ParamReader::argTypeToString(x); + if(i < ( expectedArgCount - 1 ) ) + { + report << " ,"; + } + i++; + }); + report << ")\nReceived: ("; + i = 0; + for(auto t: tags) + { + report << ParamReader::oscTagToString(t); + if( i < ( tags.size() - 1 ) ) + { + report << ", "; + } + i++; + } + report << ")\n"; + } + + if(!willContinue) return; + + /// (void) std::initializer_list{ (std::get(args) = ParamReader::fromArgs( x->mWorld, inArgs, std::get(args), 0), 0)...}; - msg->name = '/' + Client::getMessageDescriptors().template name(); - msg->wrapper = x; + x->mDone = false; ft->fDoAsynchronousCommand( x->mWorld, nullptr, getName(), msg, From 717457d8bb70ae6820bd13631b0d0d8d48cbd1c8 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 Apr 2020 15:55:17 +0100 Subject: [PATCH 074/550] Report unallocated buffers as non-existent and invalid --- include/SCBufferAdaptor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index f424890..8cb2a4f 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -99,10 +99,10 @@ public: // knows about bool valid() const override { - return (mBuffer && mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs)); + return (exists() && mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs)); } - bool exists() const override { return true; } + bool exists() const override { return mBuffer && mBuffer->data; } FluidTensorView samps(index channel) override { From 2ad8b508bda57514f60e1f50e792f8440ae0b960 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 Apr 2020 16:02:37 +0100 Subject: [PATCH 075/550] Toughen FluidManipulation SC classes where strings are expected --- release-packaging/Classes/FluidDataSet.sc | 12 ++++++------ release-packaging/Classes/FluidKDTree.sc | 4 ++-- release-packaging/Classes/FluidKMeans.sc | 4 ++-- release-packaging/Classes/FluidLabelSet.sc | 10 +++++----- release-packaging/Classes/FluidNormalize.sc | 4 ++-- release-packaging/Classes/FluidStandardize.sc | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 44afb17..dc1d074 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -22,19 +22,19 @@ FluidDataSet : FluidManipulationClient { } addPoint{|label, buffer, action| - this.pr_sendMsg(\addPoint,[label,buffer.asUGenInput],action); + this.pr_sendMsg(\addPoint,[label.asString,buffer.asUGenInput],action); } getPoint{|label, buffer, action| - this.pr_sendMsg(\getPoint,[label,buffer.asUGenInput],action); + this.pr_sendMsg(\getPoint,[label.asString,buffer.asUGenInput],action); } updatePoint{|label, buffer, action| - this.pr_sendMsg(\updatePoint,[label,buffer.asUGenInput],action); + this.pr_sendMsg(\updatePoint,[label.asString,buffer.asUGenInput],action); } deletePoint{|label, action| - this.pr_sendMsg(\deletePoint,[label],action); + this.pr_sendMsg(\deletePoint,[label.asString],action); } cols {|action| @@ -42,11 +42,11 @@ FluidDataSet : FluidManipulationClient { } read{|filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } size { |action| diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 08d1e72..5360b78 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -17,11 +17,11 @@ FluidKDTree : FluidManipulationClient { } read{ |filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{ |filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index a45a497..ebe497c 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -25,10 +25,10 @@ FluidKMeans : FluidManipulationClient { } read{ |filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{ |filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 9abf723..147c3a5 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -22,15 +22,15 @@ FluidLabelSet : FluidManipulationClient { } addLabel{|id, label, action| - this.pr_sendMsg(\addLabel,[id, label],action); + this.pr_sendMsg(\addLabel,[id.asString, label.asString],action); } getLabel{|id, action| - this.pr_sendMsg(\getLabel,[id],action,[string(FluidMessageResponse,_,_)]); + this.pr_sendMsg(\getLabel,[id.asString],action,[string(FluidMessageResponse,_,_)]); } deleteLabel{|id, action| - this.pr_sendMsg(\deleteLabel,[id],action); + this.pr_sendMsg(\deleteLabel,[id.asString],action); } cols {|action| @@ -38,11 +38,11 @@ FluidLabelSet : FluidManipulationClient { } read{|filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } size { |action| diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 3d87fc7..ed67f10 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -25,11 +25,11 @@ FluidNormalize : FluidManipulationClient { } read{|filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } } \ No newline at end of file diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index 19409eb..ab192e7 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -17,11 +17,11 @@ FluidStandardize : FluidManipulationClient { } read{|filename,action| - this.pr_sendMsg(\read,[filename],action); + this.pr_sendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename],action); + this.pr_sendMsg(\write,[filename.asString],action); } } \ No newline at end of file From 8844be07b194eea1a5fa94d1758ff10ebc0586da Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 9 Apr 2020 12:40:15 +0100 Subject: [PATCH 076/550] Send server response even when message fails, resolves #50 --- include/FluidSCWrapper.hpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index dec2057..a9ba21e 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -950,10 +950,8 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; if (!m->result.ok()) - { printResult(m->wrapper, m->result); - return false; - } + return true; }, [](World* world, void* data) // RT thread: response @@ -962,7 +960,15 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase MessageDescriptor::template forEachArg(m->args, world); - messageOutput(m->wrapper, m->name, m->result); + + if(m->result.status() != Result::Status::kError) + messageOutput(m->wrapper, m->name, m->result); + else + { + auto ft = getInterfaceTable(); + ft->fSendNodeReply(&m->wrapper->mParent->mNode, + -1, m->name.c_str(),0, nullptr); + } return true; }, nullptr, // NRT Thread: No-op @@ -977,6 +983,8 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { return x->mClient.template invoke(x->mClient, std::get(args)...); } + + template // call from RT static void messageOutput(FluidSCWrapper* x, const std::string& s, From 6f88ec98fe5b4833fe82737728776ef94301d303 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 22 Apr 2020 11:49:47 +0100 Subject: [PATCH 077/550] sines - refined example on residual --- release-packaging/HelpSource/Classes/FluidSines.schelp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/release-packaging/HelpSource/Classes/FluidSines.schelp b/release-packaging/HelpSource/Classes/FluidSines.schelp index f419a7c..c34d207 100644 --- a/release-packaging/HelpSource/Classes/FluidSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidSines.schelp @@ -82,6 +82,12 @@ b = Buffer.read(s,File.realpath(FluidSines.class.filenameSymbol).dirname.withTra // as the algorithm resynthesize the sinusoidal peaks, we would expect to get it to work almost perfectly on a sine wave, with these settings that tell the process to tolerate everything as a sinusoid, even short and quiet peaks {FluidSines.ar(SinOsc.ar(mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 200,trackFreqRange: 1000,trackProb: 0)}.play; +// we can listen to the artefact in solo, amplifying it by 30dB, to hear the 'lobes' - not bad at all! +{FluidSines.ar(SinOsc.ar(mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 200,trackFreqRange: 1000,trackProb: 0)[1].dup * Line.ar(0,30,1).dbamp}.play; + // as this is a windowed process, the frequency of the peak is good for that full window, and therefore interesting artefacts appear when the pitch is changing. {FluidSines.ar(SinOsc.ar(LFTri.kr(0.1).exprange(220,880),mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 300,trackFreqRange: 1000,trackProb: 0)}.play; + +// if we solo and amplify the artefacts, they are much more apparent (and interesting) +{FluidSines.ar(SinOsc.ar(LFTri.kr(0.1).exprange(220,880),mul: 0.1),detectionThreshold: -144,birthLowThreshold: -144,birthHighThreshold: -144,minTrackLen: 1,trackMagRange: 300,trackFreqRange: 1000,trackProb: 0)[1].dup * Line.ar(0,30,1).dbamp}.play; :: From e804c90baa9f4a28259abb9943c4ff1d1bd22175 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 22 Apr 2020 12:02:15 +0100 Subject: [PATCH 078/550] changed asInt to asInteger, resolving #44 --- release-packaging/HelpSource/Classes/FluidBufPitch.schelp | 2 +- release-packaging/HelpSource/Classes/FluidBufStats.schelp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 3b8f530..f202e83 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -143,7 +143,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInt, 2)})}); + FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index ce83e04..6b9cf4c 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -134,10 +134,10 @@ g= Array.new; Routine({ e.doAdjacentPairs({ arg start,end; - FluidBufStats.processBlocking(s,c,(start/512).asInt,((end-start)/512).max(2).asInt,0,1,d,1, action: {d.loadToFloatArray(action: { + FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1, action: {d.loadToFloatArray(action: { arg array; g = g.add(array[12]); - "% % %\n".postf((start/512).asInt,((end-start)/512).max(2).asInt, array[12]); + "% % %\n".postf((start/512).asInteger,((end-start)/512).max(2).asInteger, array[12]); })}); }); "Done".postln; From 060db23bdc6d15690db3494eb5277cfcb4ab14af Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 22 Apr 2020 12:05:23 +0100 Subject: [PATCH 079/550] corrects the description of the output in loudness --- release-packaging/HelpSource/Classes/FluidLoudness.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidLoudness.schelp b/release-packaging/HelpSource/Classes/FluidLoudness.schelp index 9a6ffdc..769b236 100644 --- a/release-packaging/HelpSource/Classes/FluidLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidLoudness.schelp @@ -32,7 +32,7 @@ ARGUMENT:: maxwindowSize How large can the windowSize be, by allocating memory at instantiation time. This cannot be modulated. RETURNS:: - A 2-channel KR signal with the [pitch, confidence] descriptors. The latency is windowSize. + A 2-channel KR signal with the [loudness, peak] descriptors. The latency is windowSize. EXAMPLES:: From 6d292602576c2f0819a71045d82e4594aec93af0 Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 27 Apr 2020 19:01:32 +0100 Subject: [PATCH 080/550] Add FluidPCA --- release-packaging/Classes/FluidPCA.sc | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 release-packaging/Classes/FluidPCA.sc diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc new file mode 100644 index 0000000..51bb06d --- /dev/null +++ b/release-packaging/Classes/FluidPCA.sc @@ -0,0 +1,36 @@ +FluidPCA : FluidManipulationClient { + + fit{|dataset, k, action| + this.pr_sendMsg(\fit,[dataset.asString, k],action); + } + + transform{|sourceDataset, destDataset, action| + this.pr_sendMsg(\transform,[sourceDataset.asString, destDataset.asString],action); + } + + fitTransform{|sourceDataset, k, destDataset, action| + this.pr_sendMsg(\fitTransform,[sourceDataset.asString,k, destDataset.asString],action); + } + + + transformPoint{|sourceBuffer, destBuffer, action| + this.pr_sendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + } + + cols {|action| + this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + rows {|action| + this.pr_sendMsg(\rows,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename,action| + this.pr_sendMsg(\read,[filename],action); + } + + write{|filename,action| + this.pr_sendMsg(\write,[filename],action); + } + +} \ No newline at end of file From 1b2655b9cc2405554d2b59249fc60f150ce9fc7d Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 27 Apr 2020 19:01:45 +0100 Subject: [PATCH 081/550] Add FluidPCA (2) --- src/FluidManipulation/FluidManipulation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index cc767b6..596afe6 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,7 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidKNN",ft); makeSCWrapper("FluidNormalize",ft); makeSCWrapper("FluidStandardize",ft); + makeSCWrapper("FluidPCA",ft); makeSCWrapper("FluidAudioTransport",ft); makeSCWrapper("FluidBufAudioTransport",ft); } From f5e2878dc8a1a65be886d62153dedb151228827e Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 29 Apr 2020 09:31:01 +0100 Subject: [PATCH 082/550] sorted help (filter and settings) of (buf)amp(gate|slice) --- .../HelpSource/Classes/FluidAmpGate.schelp | 16 ++++++++-------- .../HelpSource/Classes/FluidAmpSlice.schelp | 2 +- .../HelpSource/Classes/FluidBufAmpGate.schelp | 14 +++++++------- .../HelpSource/Classes/FluidBufAmpSlice.schelp | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidAmpGate.schelp index 880a960..8fdee5f 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpGate.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpGate.schelp @@ -19,16 +19,16 @@ ARGUMENT:: in The audio to be processed. ARGUMENT:: rampUp - The number of samples the absolute envelope follower will take to reach the next value when raising. + The number of samples the envelope follower will take to reach the next value when raising. ARGUMENT:: rampDown - The number of samples the absolute envelope follower will take to reach the next value when falling. + The number of samples the envelope follower will take to reach the next value when falling. ARGUMENT:: onThreshold - The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state. + The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. ARGUMENT:: offThreshold - The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state. + The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. ARGUMENT:: minSliceLength The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. @@ -37,10 +37,10 @@ ARGUMENT:: minSilenceLength The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. ARGUMENT:: minLengthAbove - The length in samples that the absolute envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. + The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: minLengthBelow - The length in samples that the absolute envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. + The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: lookBack The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. @@ -49,7 +49,7 @@ ARGUMENT:: lookAhead The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. + The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. ARGUMENT:: maxSize How large can the buffer be for time-critical conditions, by allocating memory at instantiation time. This cannot be modulated. @@ -129,7 +129,7 @@ b = Buffer.read(s,File.realpath(FluidAmpGate.class.filenameSymbol).dirname.withT //have fun with a gate (explore lookahead and lookback, but correct for latency, which will be the greatest of the lookahead and lookback) ( {var env, source = PlayBuf.ar(1,b); - env = FluidAmpGate.ar(source, rampUp:441, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:20); + env = FluidAmpGate.ar(source, rampUp:441, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:4410, lookBack:441, highPassFreq:20); [DelayN.ar(source,delaytime:441/44100), env] }.plot(2, separately:true); ) diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 72d87e8..347ddf2 100644 --- a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp @@ -43,7 +43,7 @@ ARGUMENT:: minSliceLength The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. + The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. RETURNS:: An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers. diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp index 438208a..70a137f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp @@ -41,16 +41,16 @@ ARGUMENT:: indices The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis. ARGUMENT:: rampUp - The number of samples the absolute envelope follower will take to reach the next value when raising. + The number of samples the envelope follower will take to reach the next value when raising. ARGUMENT:: rampDown - The number of samples the absolute envelope follower will take to reach the next value when falling. + The number of samples the envelope follower will take to reach the next value when falling. ARGUMENT:: onThreshold - The threshold in dB of the absolute envelope follower to trigger an onset, aka to go ON when in OFF state. + The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state. ARGUMENT:: offThreshold - The threshold in dB of the absolute envelope follower to trigger an offset, , aka to go ON when in OFF state. + The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state. ARGUMENT:: minSliceLength The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. @@ -59,10 +59,10 @@ ARGUMENT:: minSilenceLength The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. ARGUMENT:: minLengthAbove - The length in samples that the absolute envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. + The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: minLengthBelow - The length in samples that the absolute envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. + The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency. ARGUMENT:: lookBack The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm. @@ -71,7 +71,7 @@ ARGUMENT:: lookAhead The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. + The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. ARGUMENT:: action A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp index 1cdf44b..59e2c8a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp @@ -65,7 +65,7 @@ ARGUMENT:: minSliceLength The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. ARGUMENT:: highPassFreq - The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. + The frequency of the fourth-order Linkwitz–Riley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter. ARGUMENT:: action A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument. From 1f2ad13c0daee652608d2e44002c115d88e51856 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 29 Apr 2020 12:44:47 +0100 Subject: [PATCH 083/550] fixing sound quality of strange resonator example in nmfmatch --- release-packaging/HelpSource/Classes/FluidNMFMatch.schelp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp index d4202e2..9c44521 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFMatch.schelp @@ -216,7 +216,7 @@ FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ }); ) -//7 shapes (track) x 8 components (tracks) x 7 stats(frames) +//the interleave format is 7 stats(frames) x 8 components in the source as tracks x 7 shapes (tracks) ~stats.query ~centroids.size() @@ -230,8 +230,8 @@ x = { var sound = PlayBuf.ar(1,b,loop:1); var harm, perc; # harm, perc = FluidHPSS.ar(sound, maskingMode:1, harmThreshFreq1: 0.005869, harmThreshAmp1: -9.6875, harmThreshFreq2: 0.006609, harmThreshAmp2: -4.375, hopSize:256); - Out.ar(~splitaudio, harm); - Out.kr(~nmfenvs, FluidNMFMatch.kr(sound, ~bases, maxComponents:8, hopSize:256, fftSize:2048)); + Out.ar(~splitaudio, (harm / 4) + perc); + Out.kr(~nmfenvs, FluidNMFMatch.kr(DelayN.ar(sound,delaytime: ((17-1)*256)/44100), ~bases, maxComponents:8, hopSize:256, fftSize:2048)); Out.ar(0,perc.dup) }.play; ) @@ -241,7 +241,7 @@ x = { 8.do({ arg i; { - var audio = BPF.ar(In.ar(~splitaudio,1), ~centroids[i],0.01,LagUD.kr(In.kr(~nmfenvs,8)[i],0.001,0.01,0.1)); + var audio = BPF.ar(In.ar(~splitaudio,1), ~centroids[i],0.0015,Lag.kr(In.kr(~nmfenvs,8)[i] * 2,0.022)); Out.ar(0,Pan2.ar(audio, (i / 14) - 0.25)); }.play(x,addAction: \addAfter); }); From 6f4a2abb70dd7aaa702b38f97d399b58798f40bd Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 29 Apr 2020 14:40:56 +0100 Subject: [PATCH 084/550] missing explanation in bufampslice demo --- release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp index 59e2c8a..f5739cc 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp @@ -86,7 +86,7 @@ c = Buffer.new(s); b.play b.plot -// +//process with symmetrical thresholds FluidBufAmpSlice.process(s, b,indices: c,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60); c.query c.getn(0,c.numFrames,{|item|item.postln;}) From 5c3e2b78ff0ecc4f79e007705858393b7fd4c970 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 29 Apr 2020 14:56:23 +0100 Subject: [PATCH 085/550] bufstats: less ugly presentation of the stats at the end --- release-packaging/HelpSource/Classes/FluidBufStats.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 6b9cf4c..02ab8c8 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -175,5 +175,5 @@ FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * //looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R //this will make it tidier - the first value of each line is Left, the second is Right -d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln}) +d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln; :: From 3109a51842d497593d6eda4848ab44729d049699 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 1 May 2020 13:14:21 +0100 Subject: [PATCH 086/550] Add LocalBuf awareness --- include/FluidSCWrapper.hpp | 55 +++++++++++++++++++++++-------------- include/SCBufferAdaptor.hpp | 20 ++++++++++++-- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index a9ba21e..8162674 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -107,7 +107,8 @@ public: : mControlsIterator{mInBuf + mSpecialIndex + 1, static_cast(mNumInputs) - mSpecialIndex - 1}, mParams{Wrapper::Client::getParameterDescriptors()}, - mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld, + mClient{Wrapper::setParams(static_cast(this), + mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator, true)} {} @@ -249,7 +250,7 @@ public: NonRealTime() : mControlsIterator{mInBuf, index(mNumInputs) - mSpecialIndex - 2}, mParams{Wrapper::Client::getParameterDescriptors()}, - mClient{Wrapper::setParams(mParams, mWorld->mVerbosity > 0, mWorld, + mClient{Wrapper::setParams(static_cast(this), mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator, true)}, mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false} {} @@ -590,14 +591,14 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase return tag == 's'; } - static auto fromArgs(World*, sc_msg_iter* args, std::string, int) + static auto fromArgs(FluidSCWrapper*, World*, sc_msg_iter* args, std::string, int) { const char* recv = args->gets(""); return std::string(recv ? recv : ""); } - static auto fromArgs(World* w, FloatControlsIter& args, std::string, int) + static auto fromArgs(FluidSCWrapper*,World* w, FloatControlsIter& args, std::string, int) { // first is string size, then chars index size = static_cast(args.next()); @@ -623,21 +624,21 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template static std::enable_if_t::value, T> - fromArgs(World*, FloatControlsIter& args, T, int) + fromArgs(FluidSCWrapper*,World*, FloatControlsIter& args, T, int) { return static_cast(args.next()); } template static std::enable_if_t::value, T> - fromArgs(World*, FloatControlsIter& args, T, int) + fromArgs(FluidSCWrapper*,World*, FloatControlsIter& args, T, int) { return args.next(); } template static std::enable_if_t::value, T> - fromArgs(World*, sc_msg_iter* args, T, int defVal) + fromArgs(FluidSCWrapper*,World*, sc_msg_iter* args, T, int defVal) { return args->geti(defVal); } @@ -649,25 +650,39 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase return args->getf(); } - static auto fromArgs(World* w, ArgType args, BufferT::type&, int) + static SCBufferAdaptor* fetchBuffer(FluidSCWrapper* x, World* w, index bufnum) + { + if(bufnum >= w->mNumSndBufs) + { + index localBufNum = bufnum - w->mNumSndBufs; + + Graph* parent = static_cast(x)->mParent; + + return localBufNum <= parent -> localMaxBufNum ? + new SCBufferAdaptor(parent->mLocalSndBufs + localBufNum,w,true) + : nullptr; + } + else + return bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr; + } + + static auto fromArgs(FluidSCWrapper* x,World* w, ArgType args, BufferT::type&, int) { typename LongT::type bufnum = static_cast( - ParamReader::fromArgs(w, args, typename LongT::type(), -1)); - return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) - : nullptr); + ParamReader::fromArgs(x, w, args, typename LongT::type(), -1)); + return BufferT::type(fetchBuffer(x,w,bufnum)); } - static auto fromArgs(World* w, ArgType args, InputBufferT::type&, int) + static auto fromArgs(FluidSCWrapper* x,World* w, ArgType args, InputBufferT::type&, int) { typename LongT::type bufnum = - static_cast(fromArgs(w, args, LongT::type(), -1)); - return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) - : nullptr); + static_cast(fromArgs(x, w, args, LongT::type(), -1)); + return InputBufferT::type(fetchBuffer(x,w,bufnum)); } template static std::enable_if_t::value, P> - fromArgs(World* w, ArgType args, P&, int) + fromArgs(FluidSCWrapper*,World* w, ArgType args, P&, int) { return {fromArgs(w, args, std::string{}, 0).c_str()}; } @@ -681,7 +696,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static constexpr index argSize = C::getParameterDescriptors().template get().fixedSize; - typename T::type operator()(World* w, ArgType args) + typename T::type operator()(FluidSCWrapper* x, World* w, ArgType args) { // Just return default if there's nothing left to grab if (args.remain() == 0) @@ -697,7 +712,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase for (index i = 0; i < argSize; i++) a[i] = static_cast( - ParamReader::fromArgs(w, args, a[0], 0)); + ParamReader::fromArgs(x, w, args, a[0], 0)); return a.value(); } @@ -1066,7 +1081,7 @@ public: ft->fDefineUnitCmd(name, "version", doVersion); } - static auto& setParams(ParameterSetType& p, bool verbose, World* world, + static auto& setParams(FluidSCWrapper* x, ParameterSetType& p, bool verbose, World* world, FloatControlsIter& inputs, bool constrain = false) { //TODO: Regain this robustness if possible? @@ -1074,7 +1089,7 @@ public: // We won't even try and set params if the arguments don't match // if (inputs.size() == C::getParameterDescriptors().count()) // { - p.template setParameterValues(verbose, world, inputs); + p.template setParameterValues(verbose, x, world, inputs); if (constrain) p.constrainParameterValues(); // } // else diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index df85474..b66fe76 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -69,7 +69,9 @@ public: SCBufferAdaptor(SCBufferAdaptor&&) = default; - + SCBufferAdaptor(SndBuf* buf, World* world, bool local) + : NRTBuf{buf}, mWorld{world}, mLocal{local} + {} SCBufferAdaptor(index bufnum, World* world, bool rt = false) : NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world) @@ -79,6 +81,8 @@ public: void assignToRT(World* rtWorld) { + if(mLocal) return; + SndBuf* rtBuf = World_GetBuf(rtWorld, static_cast(mBufnum)); *rtBuf = *mBuffer; rtWorld->mSndBufUpdates[mBufnum].writes++; @@ -101,7 +105,7 @@ public: // knows about bool valid() const override { - return (exists() && mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs)); + return (exists() && (mLocal ? true : mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs))); } bool exists() const override { return mBuffer && mBuffer->data; } @@ -161,6 +165,17 @@ public: const Result resize(index frames, index channels, double sampleRate) override { SndBuf* thisThing = mBuffer; + + if(mLocal) // don't try and resize + { + if(frames > thisThing->frames || channels > thisThing->channels) + { + return {Result::Status::kError, "Local buffer must be presized adequetly, need", + frames, "frames", channels, "channels." }; + } + else return {}; + } + mOldData = thisThing->data; int allocResult = mWorld->ft->fBufAlloc(mBuffer, static_cast(channels), @@ -184,6 +199,7 @@ protected: float* mOldData{0}; index mBufnum; World* mWorld; + bool mLocal{false}; }; std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b) From a46643d46a08aca1259f92167f33d2f2069667f9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 1 May 2020 16:03:47 +0100 Subject: [PATCH 087/550] Update remaining fromArgs for LocalBuf stuff --- include/FluidSCWrapper.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 8162674..b7766ae 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -163,7 +163,7 @@ public: { mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn()); - Wrapper::setParams( + Wrapper::setParams(static_cast(this), mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params mParams.constrainParameterValues(); @@ -682,9 +682,9 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase template static std::enable_if_t::value, P> - fromArgs(FluidSCWrapper*,World* w, ArgType args, P&, int) + fromArgs(FluidSCWrapper* x,World* w, ArgType args, P&, int) { - return {fromArgs(w, args, std::string{}, 0).c_str()}; + return {fromArgs(x, w, args, std::string{}, 0).c_str()}; } }; @@ -951,7 +951,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase /// (void) std::initializer_list{ (std::get(args) = ParamReader::fromArgs( - x->mWorld, inArgs, std::get(args), 0), + x, x->mWorld, inArgs, std::get(args), 0), 0)...}; From 561cf0faf019f98da7c1986852111943f575fe4d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 1 May 2020 16:04:20 +0100 Subject: [PATCH 088/550] Add persistence mechanism for shared clients --- include/FluidSCWrapper.hpp | 109 +++++++++++++++++++++- release-packaging/Classes/FluidDataSet.sc | 4 + 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index b7766ae..8d446b7 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -12,6 +12,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "SCBufferAdaptor.hpp" #include +#include #include #include #include @@ -21,6 +22,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include #include #include +#include #include #include @@ -188,6 +190,11 @@ public: mClient.process(mAudioInputs, mOutputs, mContext); } + ParamSetType& params() + { + return mParams; + } + private: std::vector mInputConnections; std::vector mOutputConnections; @@ -392,6 +399,11 @@ public: static_cast(unit)->mClient.cancel(); } + ParamSetType& params() + { + return mParams; + } + private: static Result validateParameters(NonRealTime* w) { @@ -473,6 +485,75 @@ class NonRealTimeAndRealTime : public RealTime, //////////////////////////////////////////////////////////////////////////////// +//Discovery for clients that need persistent storage (Dataset and friends) + +template +struct IsPersistent : std::false_type +{}; + +//TODO: make less tied to current implementation +template +struct IsPersistent>> + : std::true_type +{}; + + +template +struct ObjectPersistance; + +template +struct ObjectPersistance{ + void init(){} + static void setup(InterfaceTable*, const char*){} +}; + +template +struct ObjectPersistance +{ + + template struct GetSharedType; + + template + struct GetSharedType>> + { + using type = NRTSharedInstanceAdaptor; + }; + + using SharedType = typename GetSharedType::type; + using ClientPointer = typename SharedType::ClientPointer; + + void init() + { + auto clientRef = getClientPointer(static_cast(this)); + auto pos = mRegistry.find(clientRef); + if(pos == mRegistry.end()) mRegistry.emplace(clientRef); + } + + static void setup(InterfaceTable* ft, const char* name) + { + ft->fDefineUnitCmd(name, "free", [](Unit* unit, sc_msg_iter*) + { + auto clientRef = getClientPointer(static_cast(unit)); + auto pos = mRegistry.find(clientRef); + if(pos != mRegistry.end()) mRegistry.erase(clientRef); + }); + } + +private: + + static ClientPointer getClientPointer(Wrapper* wrapper) + { + auto& params = wrapper->params(); + auto name = params.template get<0>(); + return SharedType::lookup(name); + } + + static std::unordered_set mRegistry; +}; + +template +std::unordered_set::ClientPointer> + ObjectPersistance::mRegistry; // Template Specialisations for NRT/RT template @@ -480,13 +561,33 @@ class FluidSCWrapperImpl; template class FluidSCWrapperImpl - : public NonRealTime -{}; + : public NonRealTime, public ObjectPersistance::value> +{ +public: + void init(){ + NonRealTime::init(); + ObjectPersistance::value>::init(); + } + static void setup(InterfaceTable* ft, const char* name){ + NonRealTime::setup(ft,name); + ObjectPersistance::value>::setup(ft,name); + } +}; template class FluidSCWrapperImpl - : public RealTime -{}; + : public RealTime, public ObjectPersistance::value> +{ +public: + void init(){ + RealTime::init(); + ObjectPersistance::value>::init(); + } + static void setup(InterfaceTable* ft, const char* name){ + RealTime::setup(ft,name); + ObjectPersistance::value>::setup(ft,name); + } +}; //////////////////////////////////////////////////////////////////////////////// diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index dc1d074..d519509 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -57,4 +57,8 @@ FluidDataSet : FluidManipulationClient { this.pr_sendMsg(\clear,[],action); } + free { |action| + this.pr_sendMsg(\free,[],action); + } + } \ No newline at end of file From 4984e06772171c21de8d4ec8e9df5c6510494f6b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 3 May 2020 10:14:57 +0100 Subject: [PATCH 089/550] Fix a potential memory leak --- include/FluidSCWrapper.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 8d446b7..0927d96 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1047,7 +1047,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase report << ")\n"; } - if(!willContinue) return; + if(!willContinue) + { + msg->~MessageData(); + ft->fRTFree(x->mWorld, msgptr); + return; + } /// (void) std::initializer_list{ From 058605a78af6fdbf8c02ba7ebc8408518132ad4a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 3 May 2020 10:15:19 +0100 Subject: [PATCH 090/550] Readability --- include/FluidSCWrapper.hpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 0927d96..85b16b2 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1055,11 +1055,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase } /// - (void) std::initializer_list{ - (std::get(args) = ParamReader::fromArgs( - x, x->mWorld, inArgs, std::get(args), 0), - 0)...}; - + ForEach(args,[x,&inArgs](auto& arg){ + arg = ParamReader::fromArgs(x,x->mWorld,inArgs,arg,0); + }); + x->mDone = false; ft->fDoAsynchronousCommand( From 40691d421728df7d92720bfb2e143d1f743b333a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sun, 3 May 2020 10:15:41 +0100 Subject: [PATCH 091/550] Tidy up properly --- include/FluidSCWrapper.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 85b16b2..ce3f07b 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1093,7 +1093,11 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase }, nullptr, // NRT Thread: No-op [](World* w, void* data) // RT thread: clean up - { getInterfaceTable()->fRTFree(w, data); }, + { + MessageData* m = static_cast(data); + m->~MessageData(); + getInterfaceTable()->fRTFree(w, data); + }, 0, nullptr); } From 1eabc42b242e2e0337d43c19d50252f9fabb9e64 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 6 May 2020 15:10:52 +0100 Subject: [PATCH 092/550] Amend wrapper creation to fetch client and params from different places This is to enable persistent storage for certain client typee --- include/FluidSCWrapper.hpp | 385 +++++++++++++++++++++++++------------ 1 file changed, 260 insertions(+), 125 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index ce3f07b..7bf1790 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -13,6 +13,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include "SCBufferAdaptor.hpp" #include #include +#include #include #include #include @@ -87,16 +88,20 @@ class RealTime : public SCUnit using ParamSetType = typename Client::ParamSetType; public: + + static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; } + static index ControlSize(Unit* unit) { return static_cast(unit->mNumInputs) - unit->mSpecialIndex - 1; } + static void setup(InterfaceTable* ft, const char* name) { - registerUnit(ft, name); ft->fDefineUnitCmd(name, "latency", doLatency); } static void doLatency(Unit* unit, sc_msg_iter*) { float l[]{ - static_cast(static_cast(unit)->mClient.latency())}; + static_cast(static_cast(unit)->mClient.latency()) + }; auto ft = Wrapper::getInterfaceTable(); std::stringstream ss; @@ -106,55 +111,51 @@ public: } RealTime() - : mControlsIterator{mInBuf + mSpecialIndex + 1, - static_cast(mNumInputs) - mSpecialIndex - 1}, - mParams{Wrapper::Client::getParameterDescriptors()}, - mClient{Wrapper::setParams(static_cast(this), - mParams, mWorld->mVerbosity > 0, mWorld, - mControlsIterator, true)} {} void init() { + + auto& client =static_cast(this)->mClient; assert( - !(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) && + !(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) && "Client can't have both audio and control outputs"); // If we don't the number of arguments we expect, the language side code is // probably the wrong version set plugin to no-op, squawk, and bail; - if (mControlsIterator.size() != Client::getParameterDescriptors().count()) + if (static_cast(this)->mControlsIterator.size() != Client::getParameterDescriptors().count()) { mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; std::cout << "ERROR: " << Wrapper::getName() << " wrong number of arguments. Expected " << Client::getParameterDescriptors().count() << ", got " - << mControlsIterator.size() + << static_cast(this)->mControlsIterator.size() << ". Your .sc file and binary plugin might be different versions." << std::endl; return; } - mClient.sampleRate(fullSampleRate()); - mInputConnections.reserve(asUnsigned(mClient.audioChannelsIn())); - mOutputConnections.reserve(asUnsigned(mClient.audioChannelsOut())); - mAudioInputs.reserve(asUnsigned(mClient.audioChannelsIn())); + client.sampleRate(fullSampleRate()); + mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); + mOutputConnections.reserve(asUnsigned(client.audioChannelsOut())); + mAudioInputs.reserve(asUnsigned(client.audioChannelsIn())); mOutputs.reserve(asUnsigned( - std::max(mClient.audioChannelsOut(), mClient.controlChannelsOut()))); + std::max(client.audioChannelsOut(), client.controlChannelsOut()))); - for (index i = 0; i < mClient.audioChannelsIn(); ++i) + for (index i = 0; i < client.audioChannelsIn(); ++i) { mInputConnections.emplace_back(isAudioRateIn(static_cast(i))); mAudioInputs.emplace_back(nullptr, 0, 0); } - for (index i = 0; i < mClient.audioChannelsOut(); ++i) + for (index i = 0; i < client.audioChannelsOut(); ++i) { mOutputConnections.emplace_back(true); mOutputs.emplace_back(nullptr, 0, 0); } - for (index i = 0; i < mClient.controlChannelsOut(); ++i) + for (index i = 0; i < client.controlChannelsOut(); ++i) { mOutputs.emplace_back(nullptr, 0, 0); } mCalcFunc = make_calc_function(); @@ -163,49 +164,41 @@ public: void next(int) { - mControlsIterator.reset(mInBuf + mSpecialIndex + + + auto& client = static_cast(this)->mClient; + auto& params = static_cast(this)->mParams; + + static_cast(this)->mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn()); Wrapper::setParams(static_cast(this), - mParams, mWorld->mVerbosity > 0, mWorld, - mControlsIterator); // forward on inputs N + audio inputs as params - mParams.constrainParameterValues(); + params, static_cast(this)->mControlsIterator); // forward on inputs N + audio inputs as params + params.constrainParameterValues(); const Unit* unit = this; - for (index i = 0; i < mClient.audioChannelsIn(); ++i) + for (index i = 0; i < client.audioChannelsIn(); ++i) { if (mInputConnections[asUnsigned(i)]) { mAudioInputs[asUnsigned(i)].reset(IN(i), 0, fullBufferSize()); } } - for (index i = 0; i < mClient.audioChannelsOut(); ++i) + for (index i = 0; i < client.audioChannelsOut(); ++i) { assert(i <= std::numeric_limits::max()); if (mOutputConnections[asUnsigned(i)]) mOutputs[asUnsigned(i)].reset(out(static_cast(i)), 0, fullBufferSize()); } - for (index i = 0; i < mClient.controlChannelsOut(); ++i) + for (index i = 0; i < client.controlChannelsOut(); ++i) { assert(i <= std::numeric_limits::max()); mOutputs[asUnsigned(i)].reset(out(static_cast(i)), 0, 1); } - mClient.process(mAudioInputs, mOutputs, mContext); + client.process(mAudioInputs, mOutputs, mContext); } - - ParamSetType& params() - { - return mParams; - } - private: std::vector mInputConnections; std::vector mOutputConnections; std::vector mAudioInputs; std::vector mOutputs; - FloatControlsIter mControlsIterator; FluidContext mContext; - -protected: - ParamSetType mParams; - Client mClient; }; //////////////////////////////////////////////////////////////////////////////// @@ -220,9 +213,12 @@ class NonRealTime : public SCUnit using ParamSetType = typename Client::ParamSetType; public: + + static index ControlOffset(Unit*) { return 0; } + static index ControlSize(Unit* unit) { return index(unit->mNumInputs) - unit->mSpecialIndex - 2; } + static void setup(InterfaceTable* ft, const char* name) { - registerUnit(ft, name); ft->fDefineUnitCmd(name, "cancel", doCancel); ft->fDefineUnitCmd( name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args) { @@ -253,18 +249,14 @@ public: } /// Penultimate input is the doneAction, final is blocking mode. Neither are - /// params, so we skip them in the controlsIterator + /// params, so we skip them in the controlsIterator. We may also have an ID for Model objects NonRealTime() - : mControlsIterator{mInBuf, index(mNumInputs) - mSpecialIndex - 2}, - mParams{Wrapper::Client::getParameterDescriptors()}, - mClient{Wrapper::setParams(static_cast(this), mParams, mWorld->mVerbosity > 0, mWorld, - mControlsIterator, true)}, - mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false} + : mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false} {} ~NonRealTime() { - if (mClient.state() == ProcessState::kProcessing) + if (client().state() == ProcessState::kProcessing) { std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done", @@ -290,7 +282,7 @@ public: /// launches tidy up when complete void poll(int) { - out0(0) = mDone ? 1.0f : static_cast(mClient.progress()); + out0(0) = mDone ? 1.0f : static_cast(client().progress()); if (0 == pollCounter++ && !mCheckingForDone) { @@ -399,15 +391,13 @@ public: static_cast(unit)->mClient.cancel(); } - ParamSetType& params() - { - return mParams; - } + ParamSetType& params() { return mWrapper->mParams; } + Client& client() { return mWrapper->mClient; } private: - static Result validateParameters(NonRealTime* w) + static Result validateParameters(NonRealTime* nrt) { - auto results = w->mParams.constrainParameterValues(); + auto results = nrt->params().constrainParameterValues(); for (auto& r : results) { if (!r.ok()) return r; @@ -417,7 +407,7 @@ private: bool exchangeBuffers(World* world) // RT thread { - mParams.template forEachParamType(world); + params().template forEachParamType(world); // At this point, we can see if we're finished and let the language know (or // it can wait for the doneAction, but that takes extra time) use replyID to // convey status (0 = normal completion, 1 = cancelled) @@ -430,7 +420,7 @@ private: bool tidyUp(World*) // NRT thread { - mParams.template forEachParamType(); + params().template forEachParamType(); return true; } @@ -452,7 +442,6 @@ private: } }; - FloatControlsIter mControlsIterator; FifoMsg mFifoMsg; char* mCompletionMessage = nullptr; void* mReplyAddr = nullptr; @@ -461,12 +450,12 @@ private: index pollCounter{0}; protected: - ParamSetType mParams; - Client mClient; - bool mSynchronous{true}; - bool mQueueEnabled{false}; + bool mSynchronous{true}; + bool mQueueEnabled{false}; bool mCheckingForDone{false}; // only write to this from RT thread kthx bool mCancelled{false}; +private: + Wrapper* mWrapper{static_cast(this)}; }; //////////////////////////////////////////////////////////////////////////////// @@ -487,48 +476,169 @@ class NonRealTimeAndRealTime : public RealTime, //Discovery for clients that need persistent storage (Dataset and friends) +/// Named, shared clients already have a lookup table in their adaptor class template -struct IsPersistent : std::false_type -{}; +struct IsPersistent +{ + using type = std::false_type; +}; //TODO: make less tied to current implementation template struct IsPersistent>> - : std::true_type -{}; +{ + using type = std::true_type; +}; + +template +using IsPersistent_t = typename IsPersistent::type; + +/// Models don't, but still need to survive CMD-. +template +struct IsModel +{ + using type = std::false_type; +}; +template +struct IsModel>> +{ + using type = typename ClientWrapper::isModelObject; +}; + +template +using IsModel_t = typename IsModel::type; + +template +struct LifetimePolicy; -template -struct ObjectPersistance; +//template +//struct LifetimePolicy +//{ +//// static_assert(false,"Shared Objecthood and Model Objecthood are not compatible"); +//}; +/// Default policy template -struct ObjectPersistance{ - void init(){} +struct LifetimePolicy +{ + static void constructClass(Unit* unit) + { + FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; + auto params = typename Wrapper::ParamSetType{Client::getParameterDescriptors()}; + Wrapper::setParams(unit, params, controlsReader); + Client client{params}; + new (static_cast(unit)) Wrapper(std::move(controlsReader), std::move(client), std::move(params)); + } + static void destroyClass(Unit* unit) { static_cast(unit)->~Wrapper(); } static void setup(InterfaceTable*, const char*){} }; +/// Model objects +template +struct LifetimePolicy +{ + + index uid; + + struct CacheRecord + { + typename Client::ParamSetType params{Client::getParameterDescriptors()}; + Client client{params}; + bool leased{false}; + }; + + using Cache = std::unordered_map; + + static void constructClass(Unit* unit) + { + index uid = static_cast(unit->mInBuf[Wrapper::ControlOffset(unit)][0]); + FloatControlsIter controlsReader{unit->mInBuf + 1 + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; + auto& entry = mRegistry[uid]; + auto& client = entry.client; + auto& params = entry.params; + if(entry.leased) //if this happens, then the client has probably messed up + { + std::cout << "ERROR: ID " << uid << "is already being used by the cache" << std::endl; + return; + } + Wrapper::setParams(unit, entry.params,controlsReader); + new (static_cast(unit)) Wrapper{std::move(controlsReader),std::move(client),std::move(params)}; + static_cast(unit)->uid = uid; + entry.leased = true; + } + + static void destroyClass(Unit* unit) + { + auto wrapper = static_cast(unit); + index uid = wrapper->uid; + auto pos = mRegistry.find(uid); + if( pos != mRegistry.end() ) + { + //on cmd-. live to fight another day + auto& entry = *pos; + entry.second.client = std::move(wrapper->client()); + entry.second.params = std::move(wrapper->params()); + entry.second.leased = false; + } + wrapper->~Wrapper(); + } + + static void setup(InterfaceTable* ft, const char* name) + { + ft->fDefineUnitCmd(name, "free", [](Unit* unit, sc_msg_iter*) + { + //This ABSOLUTELY ASSUMES the client is going to also delete + //the Unit by calling Synth.free. + auto wrapper = static_cast(unit); + mRegistry.erase(wrapper->uid); + }); + } + +private: + static std::unordered_map mRegistry; +}; + template -struct ObjectPersistance +typename LifetimePolicy::Cache + LifetimePolicy::mRegistry{}; + + +/// Shared objects +template +struct LifetimePolicy { template struct GetSharedType; - + template struct GetSharedType>> { using type = NRTSharedInstanceAdaptor; }; - + using SharedType = typename GetSharedType::type; using ClientPointer = typename SharedType::ClientPointer; - void init() + static void constructClass(Unit* unit) { - auto clientRef = getClientPointer(static_cast(this)); + + FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; + + auto params = typename Client::ParamSetType{Client::getParameterDescriptors()}; + Wrapper::setParams(unit, params,controlsReader); + auto& name = params.template get<0>(); + auto client = Client{params}; + auto clientRef = SharedType::lookup(name); + auto pos = mRegistry.find(clientRef); if(pos == mRegistry.end()) mRegistry.emplace(clientRef); + + new (static_cast(unit)) Wrapper(std::move(controlsReader),std::move(client),std::move(params)); + } - + static void destroyClass(Unit* unit) { static_cast(unit)->~Wrapper(); } + static void setup(InterfaceTable* ft, const char* name) { ft->fDefineUnitCmd(name, "free", [](Unit* unit, sc_msg_iter*) @@ -538,9 +648,7 @@ struct ObjectPersistance if(pos != mRegistry.end()) mRegistry.erase(clientRef); }); } - private: - static ClientPointer getClientPointer(Wrapper* wrapper) { auto& params = wrapper->params(); @@ -552,40 +660,44 @@ private: }; template -std::unordered_set::ClientPointer> - ObjectPersistance::mRegistry; -// Template Specialisations for NRT/RT +std::unordered_set::ClientPointer> + LifetimePolicy::mRegistry{}; + + +//// Template Specialisations for NRT/RT template class FluidSCWrapperImpl; template class FluidSCWrapperImpl - : public NonRealTime, public ObjectPersistance::value> + : public NonRealTime, + public LifetimePolicy, IsPersistent_t> { public: void init(){ NonRealTime::init(); - ObjectPersistance::value>::init(); } - static void setup(InterfaceTable* ft, const char* name){ + static void setup(InterfaceTable* ft, const char* name) + { NonRealTime::setup(ft,name); - ObjectPersistance::value>::setup(ft,name); + LifetimePolicy, IsPersistent_t>::setup(ft,name); } }; template class FluidSCWrapperImpl - : public RealTime, public ObjectPersistance::value> + : public RealTime, + public LifetimePolicy, IsPersistent_t> { public: void init(){ RealTime::init(); - ObjectPersistance::value>::init(); } - static void setup(InterfaceTable* ft, const char* name){ + static void setup(InterfaceTable* ft, const char* name) + { RealTime::setup(ft,name); - ObjectPersistance::value>::setup(ft,name); + LifetimePolicy, IsPersistent_t>::setup(ft,name); } }; @@ -608,6 +720,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase using FloatControlsIter = impl::FloatControlsIter; + //I would like to template these to something more scaleable, but baby steps + friend class impl::RealTime; + friend class impl::NonRealTime; + template struct ParamReader { @@ -692,20 +808,20 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase return tag == 's'; } - static auto fromArgs(FluidSCWrapper*, World*, sc_msg_iter* args, std::string, int) + static auto fromArgs(Unit*, sc_msg_iter* args, std::string, int) { const char* recv = args->gets(""); return std::string(recv ? recv : ""); } - static auto fromArgs(FluidSCWrapper*,World* w, FloatControlsIter& args, std::string, int) + static auto fromArgs(Unit* x, FloatControlsIter& args, std::string, int) { // first is string size, then chars index size = static_cast(args.next()); char* chunk = static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc( - w, asUnsigned(size + 1))); + x->mWorld, asUnsigned(size + 1))); if (!chunk) { @@ -718,74 +834,74 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase chunk[i] = static_cast(args.next()); chunk[size] = 0; // terminate string - + //todo: Did I check that this is getting cleaned up somewhere? It doesn't + //look like it is return std::string{chunk}; } - template static std::enable_if_t::value, T> - fromArgs(FluidSCWrapper*,World*, FloatControlsIter& args, T, int) + fromArgs(Unit*, FloatControlsIter& args, T, int) { return static_cast(args.next()); } template static std::enable_if_t::value, T> - fromArgs(FluidSCWrapper*,World*, FloatControlsIter& args, T, int) + fromArgs(Unit*, FloatControlsIter& args, T, int) { return args.next(); } template static std::enable_if_t::value, T> - fromArgs(FluidSCWrapper*,World*, sc_msg_iter* args, T, int defVal) + fromArgs(Unit*, sc_msg_iter* args, T, int defVal) { return args->geti(defVal); } template static std::enable_if_t::value, T> - fromArgs(World*, sc_msg_iter* args, T, int) + fromArgs(Unit*, sc_msg_iter* args, T, int) { return args->getf(); } - static SCBufferAdaptor* fetchBuffer(FluidSCWrapper* x, World* w, index bufnum) + static SCBufferAdaptor* fetchBuffer(Unit* x, index bufnum) { - if(bufnum >= w->mNumSndBufs) + if(bufnum >= x->mWorld->mNumSndBufs) { - index localBufNum = bufnum - w->mNumSndBufs; + index localBufNum = bufnum - x->mWorld->mNumSndBufs; - Graph* parent = static_cast(x)->mParent; + Graph* parent = x->mParent; - return localBufNum <= parent -> localMaxBufNum ? - new SCBufferAdaptor(parent->mLocalSndBufs + localBufNum,w,true) + return localBufNum <= parent->localMaxBufNum ? + new SCBufferAdaptor(parent->mLocalSndBufs + localBufNum,x->mWorld,true) : nullptr; } else - return bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr; + return bufnum >= 0 ? new SCBufferAdaptor(bufnum, x->mWorld) : nullptr; } - static auto fromArgs(FluidSCWrapper* x,World* w, ArgType args, BufferT::type&, int) + static auto fromArgs(Unit* x, ArgType args, BufferT::type&, int) { typename LongT::type bufnum = static_cast( - ParamReader::fromArgs(x, w, args, typename LongT::type(), -1)); - return BufferT::type(fetchBuffer(x,w,bufnum)); + ParamReader::fromArgs(x, args, typename LongT::type(), -1)); + return BufferT::type(fetchBuffer(x, bufnum)); } - static auto fromArgs(FluidSCWrapper* x,World* w, ArgType args, InputBufferT::type&, int) + static auto fromArgs(Unit* x, ArgType args, InputBufferT::type&, int) { typename LongT::type bufnum = - static_cast(fromArgs(x, w, args, LongT::type(), -1)); - return InputBufferT::type(fetchBuffer(x,w,bufnum)); + static_cast(fromArgs(x, args, LongT::type(), -1)); + return InputBufferT::type(fetchBuffer(x, bufnum)); } template static std::enable_if_t::value, P> - fromArgs(FluidSCWrapper* x,World* w, ArgType args, P&, int) + fromArgs(Unit* x, ArgType args, P&, int) { - return {fromArgs(x, w, args, std::string{}, 0).c_str()}; + return {fromArgs(x, args, std::string{}, 0).c_str()}; } }; @@ -797,7 +913,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static constexpr index argSize = C::getParameterDescriptors().template get().fixedSize; - typename T::type operator()(FluidSCWrapper* x, World* w, ArgType args) + typename T::type operator()(Unit* x, ArgType args) { // Just return default if there's nothing left to grab if (args.remain() == 0) @@ -813,7 +929,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase for (index i = 0; i < argSize; i++) a[i] = static_cast( - ParamReader::fromArgs(x, w, args, a[0], 0)); + ParamReader::fromArgs(x, args, a[0], 0)); return a.value(); } @@ -977,14 +1093,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase using ReturnType = typename MessageDescriptor::ReturnType; using IndexList = typename MessageDescriptor::IndexList; using MessageData = MessageDispatch; - auto ft = getInterfaceTable(); void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); MessageData* msg = new (msgptr) MessageData; msg->name = '/' + Client::getMessageDescriptors().template name(); msg->wrapper = x; ArgTuple& args = msg->args; - // type check OSC message std::string tags(inArgs->tags + inArgs->count); @@ -1008,14 +1122,14 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto tagsIter = tags.begin(); auto tagsEnd = tags.end(); - ForEach(args,[&typesMatch,&tagsIter,&tagsEnd](auto& x){ + ForEach(args,[&typesMatch,&tagsIter,&tagsEnd](auto& arg){ if(tagsIter == tagsEnd) { typesMatch = false; return; } char t = *(tagsIter++); - typesMatch = typesMatch && ParamReader::argTypeOK(x,t); + typesMatch = typesMatch && ParamReader::argTypeOK(arg,t); }); willContinue = willContinue && typesMatch; @@ -1056,7 +1170,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase /// ForEach(args,[x,&inArgs](auto& arg){ - arg = ParamReader::fromArgs(x,x->mWorld,inArgs,arg,0); + arg = ParamReader::fromArgs(x, inArgs,arg,0); }); @@ -1108,8 +1222,6 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase return x->mClient.template invoke(x->mClient, std::get(args)...); } - - template // call from RT static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult& result) @@ -1165,9 +1277,17 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase public: using Client = C; - using ParameterSetType = typename C::ParamSetType; + using ParamSetType = typename C::ParamSetType; - FluidSCWrapper() { impl::FluidSCWrapperBase::init(); } + + + FluidSCWrapper(FloatControlsIter&& i, Client&& c, ParamSetType&& p): + mControlsIterator{std::move(i)}, + mParams{std::move(p)}, mClient{std::move(c)} + { + mClient.setParams(mParams); //<-IMPORTANT: client's ref to params is by address, and this has just changed + impl::FluidSCWrapperBase::init(); + } static const char* getName(const char* setName = nullptr) { @@ -1185,20 +1305,20 @@ public: { getName(name); getInterfaceTable(ft); + registerUnit(ft, name); impl::FluidSCWrapperBase::setup(ft, name); Client::getMessageDescriptors().template iterate(); ft->fDefineUnitCmd(name, "version", doVersion); } - static auto& setParams(FluidSCWrapper* x, ParameterSetType& p, bool verbose, World* world, + static auto& setParams(Unit* x, ParamSetType& p, FloatControlsIter& inputs, bool constrain = false) { //TODO: Regain this robustness if possible? - // We won't even try and set params if the arguments don't match // if (inputs.size() == C::getParameterDescriptors().count()) // { - p.template setParameterValues(verbose, x, world, inputs); + p.template setParameterValues(x->mWorld->mVerbosity > 0, x, inputs); if (constrain) p.constrainParameterValues(); // } // else @@ -1235,6 +1355,21 @@ public: } } } + + auto& client() { return mClient; } + auto& params() { return mParams; } + + private: + + static void registerUnit(InterfaceTable* ft, const char* name) { + UnitCtorFunc ctor =impl::FluidSCWrapperBase::constructClass; + UnitDtorFunc dtor = impl::FluidSCWrapperBase::destroyClass; + (*ft->fDefineUnit)(name, sizeof(FluidSCWrapper), ctor, dtor, 0); + } + + FloatControlsIter mControlsIterator; + ParamSetType mParams; + Client mClient; }; template From 4b88b405d782d30e939b9a99fa4b69264ad755fe Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 6 May 2020 15:13:04 +0100 Subject: [PATCH 093/550] Enable persistence for Dataset, Labelset and 'model' objects viz. KDTree, KMeans,KNN, Normalize, PCA and Standardize. Most changes in FluidManipulationClient --- release-packaging/Classes/FluidDataSet.sc | 112 ++++++---- release-packaging/Classes/FluidKDTree.sc | 51 +++-- release-packaging/Classes/FluidKMeans.sc | 25 ++- release-packaging/Classes/FluidKNN.sc | 26 ++- release-packaging/Classes/FluidLabelSet.sc | 106 ++++++---- .../Classes/FluidManipulationClient.sc | 191 +++++++++++++++--- release-packaging/Classes/FluidNormalize.sc | 25 +-- release-packaging/Classes/FluidPCA.sc | 28 ++- .../Classes/FluidProviderTest.sc | 8 +- release-packaging/Classes/FluidStandardize.sc | 23 ++- .../Classes/FluidSubscriberTest.sc | 4 +- test/TestFluidManipulationLifecyle.sc | 105 ++++++++++ 12 files changed, 524 insertions(+), 180 deletions(-) create mode 100644 test/TestFluidManipulationLifecyle.sc diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index d519509..6d270cd 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -1,64 +1,88 @@ + +FluidDataSetExistsError : Exception{ +} + FluidDataSet : FluidManipulationClient { - var <>synth, <>server, <>id; + var k; - + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); + } + + init {|uid| + id = uid; + } + fit{|dataset,k, maxIter = 100, buffer, action| buffer = buffer ? -1; this.k = k; - this.pr_sendMsg(\fit,[dataset.asString, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); + this.prSendMsg(\fit,[dataset.asString, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); } predict{ |dataset, labelset,action| - this.pr_sendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); + this.prSendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); } getClusters{ |dataset, labelset,action| - this.pr_sendMsg(\getClusters,[dataset.asString, labelset.asString],action); + this.prSendMsg(\getClusters,[dataset.asString, labelset.asString],action); } predictPoint { |buffer, action| - this.pr_sendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); + this.prSendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); } cols { |action| - this.pr_sendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); + this.prSendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); } read{ |filename,action| - this.pr_sendMsg(\read,[filename.asString],action); + this.prSendMsg(\read,[filename.asString],action); } write{ |filename,action| - this.pr_sendMsg(\write,[filename.asString],action); + this.prSendMsg(\write,[filename.asString],action); } } diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index 92591bc..f1e8260 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -1,15 +1,23 @@ FluidKNN : FluidManipulationClient { - fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset.asString],action); - } + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); + } - classifyPoint{ |buffer, labelset, k, action| - this.pr_sendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,[string(FluidMessageResponse,_,_)]); - } + init {|uid| + id = uid; + } - regressPoint { |buffer,dataset, k, action| - this.pr_sendMsg(\regress,[buffer.asUGenInput, dataset.asString,k],action,[number(FluidMessageResponse,_,_)]); - } + fit{|dataset, action| + this.prSendMsg(\fit,[dataset.asString],action); + } + classifyPoint{ |buffer, labelset, k, action| + this.prSendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,[string(FluidMessageResponse,_,_)]); + } + + regressPoint { |buffer,dataset, k, action| + this.prSendMsg(\regress,[buffer.asUGenInput, dataset.asString,k],action,[number(FluidMessageResponse,_,_)]); + } } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 147c3a5..edc0220 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -1,55 +1,83 @@ +FluidLabelSetExistsError : Exception{ +} + FluidLabelSet : FluidManipulationClient { - var <> synth, <> server, <>id; + var synth, <> server; + var <>pluginname; - *kr { - ^this.multiNew('control', Done.none, nonBlocking); - } + *kr { |pluginname...args| + ^this.new1('control', pluginname,*args) + } + + init { |pluginname...args| + this.pluginname = pluginname; + inputs = args++Done.none++0; + } + + name{ + ^pluginname.asString; + } +} + +FluidManipulationClient { + + var ugen, updateFunc; + var nodeResponder; + + var initTreeCondition; + var synthBeenSet = false; + var serverListener; + + *prServerString{ |s| + var ascii = s.ascii; + ^[ascii.size].addAll(ascii) + } + + sendSynthDef { |...args| + var plugin = this.class.name.asSymbol; + if(server.hasBooted) + { + fork{ + SynthDef(defName.asSymbol,{ + var ugen = FluidProxyUgen.kr(plugin, *args); + this.ugen = ugen; + ugen + }).send(server); + + server.sync; + + synthDefLoaded = true; + updateFunc = { + //Sometimes Server.initTree seems to get called a bunch of + //times during boot: we can't be having extra instances + //However, once boot has finished, ending up here means cmd-. or server.freeAll + //has happened, and we just need to run + + var shouldRun = (synthBeenSet.not.and(server.serverBooting)) + .or(server.serverRunning.and(server.serverBooting.not)); + + if(shouldRun) { + synthBeenSet = true; + synth = nil; + this.updateSynth; + } + }; + updateFunc.value; + ServerTree.add(updateFunc, server); + }; + }; + } + + updateSynth { + if(server.hasBooted){ + if(synthDefLoaded){ + if(synth.isNil){ + synth = Synth.after(server.defaultGroup,defName.asSymbol); + synth.register; + } + } + }{ + synth !? {synth.free}; + } + } *new{ |server...args| - var synth, instance; server = server ? Server.default; - if(server.serverRunning.not,{("ERROR:" + this.asString + "– server not running").postln; ^nil}); - synth = {instance = this.kr(*args)}.play(server); - instance.server = server; - instance.synth = synth; - ^instance + if(server.serverRunning.not,{ + (this.asString + "– server not running").warn; + + }); + ^super.newCopyArgs(server ?? {Server.default}).baseinit(*args) } - pr_sendMsg { |msg, args, action,parser| - var c = Condition.new(false); + baseinit { |...args| + + id = UniqueID.next; + synthDefLoaded = false; + defName = (this.class.name.asString ++ id); - OSCFunc( - { |msg| - forkIfNeeded{ - var result; - // msg.postln; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - this.server.sync; - c.test = true; - c.signal; - if(action.notNil){action.value(result)}{action.value}; - } - },'/'++msg).oneShot; + if(server.serverRunning){ this.sendSynthDef(*args);}; - this.server.listSendMsg(['/u_cmd',this.synth.nodeID,this.synthIndex,msg].addAll(args)); + bootFunc = { + ServerBoot.remove(bootFunc,server); + synth = nil; + this.sendSynthDef(*args); + }; - forkIfNeeded { c.wait }; + ServerBoot.add(bootFunc,server); + ServerQuit.add({this.free;},server); + } + free{ + ServerTree.remove(updateFunc,server); + ServerBoot.remove(bootFunc, server); + updateFunc = nil; + // synth !? {synth.tryPerform(\free)};// + synth = nil; + } + + prSendMsg { |msg, args, action,parser| + if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); + synth !? { + OSCFunc( + { |msg| + forkIfNeeded{ + var result; + result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); + if(action.notNil){action.value(result)}{action.value}; + } + },'/'++msg, server.addr, nil,[synth.nodeID]).oneShot; + server.listSendMsg(['/u_cmd',synth.nodeID,ugen.synthIndex,msg].addAll(args)); + } } } + +FluidServerCache { + + var cache; + + *new{ ^super.new.init } + + init{ + cache = IdentityDictionary.new; + } + + at { |server,id| + ^cache[server].tryPerform(\at,id) + } + + includesKey{|server,key| + ^cache[server].tryPerform(\includesKey,key) + } + + put {|server,id,x| + cache[server][id] = x; + } + + remove { |server,id| + cache[server]!? {cache[server].removeAt(id)}; + } + + initCache {|server| + cache[server] ?? { + cache[server] = IdentityDictionary.new; + ServerQuit.add({this.clearCache(server)},server); + NotificationCenter.register(server,\newAllocators,this, { + this.clearCache(server); + }); + } + } + + clearCache { |server| + cache[server] !? { cache.removeAt(server) !? {|x| x.tryPerform(\free) } }; + } +} + diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index ed67f10..1f3cffb 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -1,35 +1,36 @@ FluidNormalize : FluidManipulationClient { - *kr{ |min = 0, max = 1| - ^this.multiNew('control',min, max, Done.none, super.nonBlocking); + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); } - *new { |server,min = 0 ,max = 1| - ^super.new(server,min,max); - } + init {|uid| + id = uid; + } fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset.asString],action); + this.prSendMsg(\fit,[dataset.asString],action); } normalize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\normalize,[sourceDataset.asString, destDataset.asString],action); + this.prSendMsg(\normalize,[sourceDataset.asString, destDataset.asString],action); } normalizePoint{|sourceBuffer, destBuffer, action| - this.pr_sendMsg(\normalizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + this.prSendMsg(\normalizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| - this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } read{|filename,action| - this.pr_sendMsg(\read,[filename.asString],action); + this.prSendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename.asString],action); + this.prSendMsg(\write,[filename.asString],action); } -} \ No newline at end of file +} diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index 51bb06d..519acb9 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -1,36 +1,46 @@ FluidPCA : FluidManipulationClient { + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); + } + + init {|uid| + id = uid; + } + fit{|dataset, k, action| - this.pr_sendMsg(\fit,[dataset.asString, k],action); + this.prSendMsg(\fit,[dataset.asString, k],action); } transform{|sourceDataset, destDataset, action| - this.pr_sendMsg(\transform,[sourceDataset.asString, destDataset.asString],action); + this.prSendMsg(\transform,[sourceDataset.asString, destDataset.asString],action); } fitTransform{|sourceDataset, k, destDataset, action| - this.pr_sendMsg(\fitTransform,[sourceDataset.asString,k, destDataset.asString],action); + this.prSendMsg(\fitTransform,[sourceDataset.asString,k, destDataset.asString],action); } transformPoint{|sourceBuffer, destBuffer, action| - this.pr_sendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| - this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } rows {|action| - this.pr_sendMsg(\rows,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.prSendMsg(\rows,[],action,[numbers(FluidMessageResponse,_,1,_)]); } read{|filename,action| - this.pr_sendMsg(\read,[filename],action); + this.prSendMsg(\read,[filename],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename],action); + this.prSendMsg(\write,[filename],action); } -} \ No newline at end of file +} diff --git a/release-packaging/Classes/FluidProviderTest.sc b/release-packaging/Classes/FluidProviderTest.sc index d9589b2..bcc434e 100644 --- a/release-packaging/Classes/FluidProviderTest.sc +++ b/release-packaging/Classes/FluidProviderTest.sc @@ -26,18 +26,18 @@ FluidProviderTest : UGen { } addPoint{|server, nodeID, args, action| - this.pr_sendMsg(server, nodeID, 'addPoint',args,action); + this.prSendMsg(server, nodeID, 'addPoint',args,action); } updatePoint{|server, nodeID, args, action| - this.pr_sendMsg(server, nodeID, 'updatePoint',args,action); + this.prSendMsg(server, nodeID, 'updatePoint',args,action); } deletePoint{|server, nodeID, args, action| - this.pr_sendMsg(server,nodeID, 'deletePoint',args,action); + this.prSendMsg(server,nodeID, 'deletePoint',args,action); } - pr_sendMsg { |server, nodeID, msg, args, action,parser| + prSendMsg { |server, nodeID, msg, args, action,parser| server = server ? Server.default; diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index ab192e7..deee70e 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -1,27 +1,36 @@ FluidStandardize : FluidManipulationClient { + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); + } + + init {|uid| + id = uid; + } + fit{|dataset, action| - this.pr_sendMsg(\fit,[dataset.asString],action); + this.prSendMsg(\fit,[dataset.asString],action); } standardize{|sourceDataset, destDataset, action| - this.pr_sendMsg(\standardize,[sourceDataset.asString, destDataset.asString],action); + this.prSendMsg(\standardize,[sourceDataset.asString, destDataset.asString],action); } standardizePoint{|sourceBuffer, destBuffer, action| - this.pr_sendMsg(\standardizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + this.prSendMsg(\standardizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| - this.pr_sendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } read{|filename,action| - this.pr_sendMsg(\read,[filename.asString],action); + this.prSendMsg(\read,[filename.asString],action); } write{|filename,action| - this.pr_sendMsg(\write,[filename.asString],action); + this.prSendMsg(\write,[filename.asString],action); } -} \ No newline at end of file +} diff --git a/release-packaging/Classes/FluidSubscriberTest.sc b/release-packaging/Classes/FluidSubscriberTest.sc index 8ea6070..04a799d 100644 --- a/release-packaging/Classes/FluidSubscriberTest.sc +++ b/release-packaging/Classes/FluidSubscriberTest.sc @@ -21,11 +21,11 @@ FluidSubscriberTest : UGen { } providerLookup { |server, nodeID, label, action| - this.pr_sendMsg(server, nodeID, 'providerLookup', label, action, + this.prSendMsg(server, nodeID, 'providerLookup', label, action, [string(FluidMessageResponse,_,_),numbers(FluidMessageResponse,_,2,_)] ); } - pr_sendMsg { |server, nodeID, msg, args, action,parser| + prSendMsg { |server, nodeID, msg, args, action,parser| server = server ? Server.default; diff --git a/test/TestFluidManipulationLifecyle.sc b/test/TestFluidManipulationLifecyle.sc new file mode 100644 index 0000000..8209341 --- /dev/null +++ b/test/TestFluidManipulationLifecyle.sc @@ -0,0 +1,105 @@ + +TestFluidCorpusManipulationServer : UnitTest +{ + var waitForCounts, countsListener; + + setUp{ + waitForCounts = Condition.new(false); + countsListener = { |s,changed| + if(changed == \counts) { + waitForCounts.test = true; + waitForCounts.signal; + } + }; + Server.default.addDependant(countsListener); + } + + tearDown{ + Server.default.removeDependant(countsListener); + Server.default.quit; + } + + test_DataSetPersistence{ + var foo, bar, tree, testPoint; + + foo = FluidDataSet(Server.default,\foo); + + this.bootServer(Server.default); + while {Server.default.serverRunning.not}{0.2.wait}; + waitForCounts.test = false; + waitForCounts.wait; + + this.assertEquals(Server.default.numSynths,1,"Dataset: One Synth present after deferred boot"); + + waitForCounts.test = false; + foo.free; + Server.default.freeAll; + waitForCounts.wait; + + this.assertEquals(Server.default.numSynths,0,"Dataset: One Synth present via cretation after boot"); + + //Uniqueness test (difficult to run with previous instance of foo, because + //UnitTest.bootServer messes with Server alloctors and screws up the ID cache + foo = FluidDataSet(Server.default,\foo); + this.assertException({ + bar = FluidDataSet(Server.default,\foo); + },FluidDataSetExistsError,"DataSetDuplicateError on reused name", onFailure:{ + "Exception fail".postln; + }); + + waitForCounts.test = false; + bar = FluidDataSet(Server.default,\bar); + waitForCounts.wait; + + this.assertEquals(Server.default.numSynths,2,"Dataset: Two Synths present after new Dataset added"); + + testPoint = Buffer.alloc(Server.default,8); + Server.default.sync; + testPoint.setn(0,[1,2,3,4,5,6,7,8]); + Server.default.sync; + foo.addPoint(\one,testPoint); + Server.default.sync; + foo.size({|size| + this.assertEquals(size,1,"Dataset size is 1"); + }); + Server.default.sync; + foo.cols({|cols| + this.assertEquals(cols,8,"Dataset cols is 8"); + }); + + Server.default.sync; + waitForCounts.test = false; + + tree = FluidKDTree(Server.default); + waitForCounts.wait; + + this.assert(tree.synth.notNil,"Tree should have a valid synth"); + this.assertEquals(Server.default.numSynths,3,"Dataset: Three Synths remain after cmd-."); + + tree.fit(foo); + Server.default.sync; + tree.cols({|cols| + this.assertEquals(cols,8,"KDTree correct dims after fit") + }); + Server.default.sync; + + //Test cmd-period resistance + waitForCounts.test = false; + Server.default.freeAll; + Server.default.sync; + Server.default.sync; + waitForCounts.wait; + + this.assertEquals(Server.default.numSynths,3,"Dataset: Three Synths remain after cmd-."); + foo.size({|size| + this.assertEquals(size,1,"Dataset size is still 1 after Cmd-."); + }); + Server.default.sync; + foo.cols({|cols| + this.assertEquals(cols,8,"Dataset cols is still 8 after Cmd-."); + }); + Server.default.sync; + tree.cols({|cols| this.assertEquals(cols,8,"KDTree correct dims after Cmd-.")}); + Server.default.sync; + } +} \ No newline at end of file From 99496a66337a98683f8dc90d4860cd47410733c1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 7 May 2020 01:50:23 +0100 Subject: [PATCH 094/550] KISS: Greatly simplify persistence dynamics --- .../Classes/FluidManipulationClient.sc | 113 ++++++------------ 1 file changed, 36 insertions(+), 77 deletions(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 7c99fbf..2522bf8 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -19,105 +19,64 @@ FluidProxyUgen : UGen { FluidManipulationClient { var ugen, updateFunc; - var nodeResponder; - - var initTreeCondition; - var synthBeenSet = false; - var serverListener; + var ugen; + var id; + var defName, def; + var onSynthFree, persist; *prServerString{ |s| var ascii = s.ascii; ^[ascii.size].addAll(ascii) } - sendSynthDef { |...args| - var plugin = this.class.name.asSymbol; - if(server.hasBooted) - { - fork{ - SynthDef(defName.asSymbol,{ - var ugen = FluidProxyUgen.kr(plugin, *args); - this.ugen = ugen; - ugen - }).send(server); - - server.sync; - - synthDefLoaded = true; - updateFunc = { - //Sometimes Server.initTree seems to get called a bunch of - //times during boot: we can't be having extra instances - //However, once boot has finished, ending up here means cmd-. or server.freeAll - //has happened, and we just need to run - - var shouldRun = (synthBeenSet.not.and(server.serverBooting)) - .or(server.serverRunning.and(server.serverBooting.not)); - - if(shouldRun) { - synthBeenSet = true; - synth = nil; - this.updateSynth; - } - }; - updateFunc.value; - ServerTree.add(updateFunc, server); - }; - }; - } - - updateSynth { - if(server.hasBooted){ - if(synthDefLoaded){ - if(synth.isNil){ - synth = Synth.after(server.defaultGroup,defName.asSymbol); - synth.register; - } - } - }{ - synth !? {synth.free}; - } - } - - *new{ |server...args| - server = server ? Server.default; + *new{ |server...args| + server = server ? Server.default; if(server.serverRunning.not,{ (this.asString + "– server not running").warn; - }); ^super.newCopyArgs(server ?? {Server.default}).baseinit(*args) - } + } baseinit { |...args| - + var makeFirstSynth; id = UniqueID.next; - synthDefLoaded = false; - defName = (this.class.name.asString ++ id); + defName = (this.class.name.asString ++ id).asSymbol; - if(server.serverRunning){ this.sendSynthDef(*args);}; + def = SynthDef(defName,{ + var ugen = FluidProxyUgen.kr(this.class.name, *args); + this.ugen = ugen; + ugen + }); - bootFunc = { - ServerBoot.remove(bootFunc,server); - synth = nil; - this.sendSynthDef(*args); + synth = Synth.basicNew(defName,server); + persist = true; + onSynthFree = { + if(persist){ + synth = Synth.after(server.defaultGroup,defName); + synth.onFree(onSynthFree); + } }; - ServerBoot.add(bootFunc,server); - ServerQuit.add({this.free;},server); + synth.onFree(onSynthFree); + CmdPeriod.add(onSynthFree); + + makeFirstSynth ={ + var synthMsg= synth.newMsg(server.defaultGroup,\addAfter); + def.send(server,synthMsg); + }; + + if(server.serverRunning) + { makeFirstSynth.value} + {server.doWhenBooted(makeFirstSynth)}; } free{ - ServerTree.remove(updateFunc,server); - ServerBoot.remove(bootFunc, server); - updateFunc = nil; - // synth !? {synth.tryPerform(\free)};// + persist = false; + CmdPeriod.remove(onSynthFree); synth = nil; } - prSendMsg { |msg, args, action,parser| + prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); synth !? { OSCFunc( @@ -130,7 +89,7 @@ FluidManipulationClient { },'/'++msg, server.addr, nil,[synth.nodeID]).oneShot; server.listSendMsg(['/u_cmd',synth.nodeID,ugen.synthIndex,msg].addAll(args)); } - } + } } FluidServerCache { From 188f0f913376076487e8a66aaac3c94ee0d5fdc8 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 7 May 2020 13:12:01 +0100 Subject: [PATCH 095/550] KISS-erer: No trickery with Synth.basicNew because we can't communicate with the Node afterwards --- release-packaging/Classes/FluidDataSet.sc | 9 +++-- .../Classes/FluidManipulationClient.sc | 34 +++++++++---------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 6d270cd..db80afe 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -78,11 +78,14 @@ FluidDataSet : FluidManipulationClient { free { |action| serverCaches.remove(server,id); - if(server.serverRunning){this.prSendMsg(\free,[],action)}; - super.free; + fork{ + if(server.serverRunning){this.prSendMsg(\free,[],action)}; + server.sync; + super.free; + } } *freeAll { |server| serverCaches.tryPerform(\clearCache,server); } -} +} diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 2522bf8..cbbcf1e 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -29,6 +29,11 @@ FluidManipulationClient { ^[ascii.size].addAll(ascii) } + *newFromDesc { arg rate, numOutputs, inputs, specialIndex; + ^FluidProxyUgen.newFromDesc(rate, numOutputs, inputs, specialIndex) + } + + *new{ |server...args| server = server ? Server.default; if(server.serverRunning.not,{ @@ -46,34 +51,29 @@ FluidManipulationClient { var ugen = FluidProxyUgen.kr(this.class.name, *args); this.ugen = ugen; ugen - }); + }).add; + - synth = Synth.basicNew(defName,server); persist = true; onSynthFree = { + synth = nil; if(persist){ - synth = Synth.after(server.defaultGroup,defName); - synth.onFree(onSynthFree); + //If we don't sync here, cmd-. doesn't reset properly (but server.freeAll does) + fork { + server.sync; + synth = Synth(defName,target: RootNode(server)); + synth.onFree{onSynthFree.value}; + } } }; - synth.onFree(onSynthFree); - CmdPeriod.add(onSynthFree); - - makeFirstSynth ={ - var synthMsg= synth.newMsg(server.defaultGroup,\addAfter); - def.send(server,synthMsg); - }; - - if(server.serverRunning) - { makeFirstSynth.value} - {server.doWhenBooted(makeFirstSynth)}; + server.doWhenBooted{onSynthFree.value}; } free{ persist = false; - CmdPeriod.remove(onSynthFree); - synth = nil; + synth.tryPerform(\free); + ^nil } prSendMsg { |msg, args, action,parser| From 377705799581ae5ac32d553e85022d033a1c381c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 7 May 2020 15:28:35 +0100 Subject: [PATCH 096/550] decouple freeing shared and model objects from the synth with PlugInCmd This should give much stronger guarantees that the server and client agree about what exists. It would be neat if the server could delete its own node as well, but I haven't worked out how to do this yet --- include/FluidSCWrapper.hpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 7bf1790..cd3f420 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -586,13 +586,16 @@ struct LifetimePolicy static void setup(InterfaceTable* ft, const char* name) { - ft->fDefineUnitCmd(name, "free", [](Unit* unit, sc_msg_iter*) - { - //This ABSOLUTELY ASSUMES the client is going to also delete - //the Unit by calling Synth.free. - auto wrapper = static_cast(unit); - mRegistry.erase(wrapper->uid); - }); + auto freeName = std::stringstream(); + freeName << "free" << name; + + ft->fDefinePlugInCmd(freeName.str().c_str(), + [](World*,void*,sc_msg_iter* args, void*/*replyAddr*/) + { + auto objectID = args->geti(); + auto pos = mRegistry.find(objectID); + if(pos != mRegistry.end()) mRegistry.erase(objectID); + }, &mRegistry); } private: @@ -641,12 +644,19 @@ struct LifetimePolicy static void setup(InterfaceTable* ft, const char* name) { - ft->fDefineUnitCmd(name, "free", [](Unit* unit, sc_msg_iter*) - { - auto clientRef = getClientPointer(static_cast(unit)); + + auto freeName = std::stringstream(); + freeName << "free" << name; + + ft->fDefinePlugInCmd(freeName.str().c_str(), + [](World*,void*,sc_msg_iter* args, void* /*replyAddr*/) + { + auto objectName = std::string(args->gets()); + auto clientRef = SharedType::lookup(objectName); auto pos = mRegistry.find(clientRef); if(pos != mRegistry.end()) mRegistry.erase(clientRef); - }); + }, &mRegistry); + } private: static ClientPointer getClientPointer(Wrapper* wrapper) From d6905db3a3a4143fd90d3f2096f52f5f9a8d8996 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 7 May 2020 16:07:15 +0100 Subject: [PATCH 097/550] New free message; no dice if server not running; working freeAll --- release-packaging/Classes/FluidDataSet.sc | 13 ++--- release-packaging/Classes/FluidKDTree.sc | 57 +++++++++---------- release-packaging/Classes/FluidKMeans.sc | 2 +- release-packaging/Classes/FluidKNN.sc | 2 +- release-packaging/Classes/FluidLabelSet.sc | 5 +- .../Classes/FluidManipulationClient.sc | 16 ++++-- release-packaging/Classes/FluidNormalize.sc | 2 +- release-packaging/Classes/FluidPCA.sc | 2 +- release-packaging/Classes/FluidStandardize.sc | 2 +- test/TestFluidManipulationLifecyle.sc | 20 +++---- 10 files changed, 57 insertions(+), 64 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index db80afe..56cc515 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -18,8 +18,9 @@ FluidDataSet : FluidManipulationClient { *new { |server,name| if(this.at(server,name).notNil){ FluidDataSetExistsError("A FluidDataset called % already exists.".format(name)).throw; + ^nil } - {^super.new(server,*FluidManipulationClient.prServerString(name)).init(name)} + ^super.new(server,*FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst} } init {|name| @@ -76,16 +77,12 @@ FluidDataSet : FluidManipulationClient { this.prSendMsg(\clear,[],action); } - free { |action| + free { serverCaches.remove(server,id); - fork{ - if(server.serverRunning){this.prSendMsg(\free,[],action)}; - server.sync; - super.free; - } + super.free; } *freeAll { |server| - serverCaches.tryPerform(\clearCache,server); + serverCaches.do(server,{|x|x.free;}); } } diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 77c6153..0f97a09 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -1,42 +1,37 @@ FluidKDTree : FluidManipulationClient { - var id; + var id; - *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid).init(uid); - } + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } - init {|uid| - id = uid; - } + init {|uid| + id = uid; + } - fit{|dataset,action| - this.prSendMsg(\fit,[dataset.asUGenInput],action); - } + fit{|dataset,action| + this.prSendMsg(\fit,[dataset.asUGenInput],action); + } - kNearest{ |buffer, k,action| - this.prSendMsg(\kNearest,[buffer.asUGenInput,k],action,k.collect{string(FluidMessageResponse,_,_)}); - } + kNearest{ |buffer, k,action| + this.prSendMsg(\kNearest,[buffer.asUGenInput,k],action,k.collect{string(FluidMessageResponse,_,_)}); + } - kNearestDist { |buffer, k,action| - this.prSendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,k,_)]); - } + kNearestDist { |buffer, k,action| + this.prSendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,k,_)]); + } - cols { |action| - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } + cols { |action| + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } - read{ |filename,action| - this.prSendMsg(\read,[filename.asString],action); - } + read{ |filename,action| + this.prSendMsg(\read,[filename.asString],action); + } - write{ |filename,action| - this.prSendMsg(\write,[filename.asString],action); - } - - free { |action| - if(server.serverRunning){this.prSendMsg(\free,[],action)}; - super.free; - } + write{ |filename,action| + this.prSendMsg(\write,[filename.asString],action); + } } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index e09ee0c..808f378 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -4,7 +4,7 @@ FluidKMeans : FluidManipulationClient { *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc index f1e8260..1be993a 100644 --- a/release-packaging/Classes/FluidKNN.sc +++ b/release-packaging/Classes/FluidKNN.sc @@ -2,7 +2,7 @@ FluidKNN : FluidManipulationClient { *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index edc0220..3d92852 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -18,7 +18,7 @@ FluidLabelSet : FluidManipulationClient { serverCaches.at(server,name) !? { FluidLabelSetExistsError("A FluidLabelSet called % already exists.".format(name)).throw; }; - ^super.new(server,*FluidManipulationClient.prServerString(name)).init(name) + ^super.new(server,*FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst} } init { |name| @@ -73,11 +73,10 @@ FluidLabelSet : FluidManipulationClient { free { |action| serverCaches.remove(server,id); - if(server.serverRunning){this.prSendMsg(\free,[],action)}; super.free; } *freeAll { |server| - serverCaches.tryPerform(\clearCache,server); + serverCaches.do(server,{|x|x.free;}); } } diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index cbbcf1e..e57bd7d 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -37,7 +37,7 @@ FluidManipulationClient { *new{ |server...args| server = server ? Server.default; if(server.serverRunning.not,{ - (this.asString + "– server not running").warn; + (this.asString + "– server not running").error; ^nil }); ^super.newCopyArgs(server ?? {Server.default}).baseinit(*args) } @@ -72,6 +72,7 @@ FluidManipulationClient { free{ persist = false; + if(server.serverRunning){server.sendMsg("/cmd","free"++this.class.name,id)}; synth.tryPerform(\free); ^nil } @@ -102,6 +103,10 @@ FluidServerCache { cache = IdentityDictionary.new; } + do { |server, func| + cache[server]!?{cache[server].do{|x|func.value(x)}} + } + at { |server,id| ^cache[server].tryPerform(\at,id) } @@ -121,15 +126,16 @@ FluidServerCache { initCache {|server| cache[server] ?? { cache[server] = IdentityDictionary.new; - ServerQuit.add({this.clearCache(server)},server); - NotificationCenter.register(server,\newAllocators,this, { - this.clearCache(server); + NotificationCenter.register(server,\newAllocators,this, + { + this.clearCache(server); }); } } clearCache { |server| - cache[server] !? { cache.removeAt(server) !? {|x| x.tryPerform(\free) } }; + cache.postln; + cache[server] !? { cache.removeAt(server) }; } } diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 1f3cffb..ee25f1b 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -2,7 +2,7 @@ FluidNormalize : FluidManipulationClient { *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index 519acb9..ef7cf7a 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -3,7 +3,7 @@ FluidPCA : FluidManipulationClient { *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index deee70e..d3e5a6d 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -2,7 +2,7 @@ FluidStandardize : FluidManipulationClient { *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| diff --git a/test/TestFluidManipulationLifecyle.sc b/test/TestFluidManipulationLifecyle.sc index 8209341..796673a 100644 --- a/test/TestFluidManipulationLifecyle.sc +++ b/test/TestFluidManipulationLifecyle.sc @@ -21,26 +21,23 @@ TestFluidCorpusManipulationServer : UnitTest test_DataSetPersistence{ var foo, bar, tree, testPoint; - - foo = FluidDataSet(Server.default,\foo); + //No server on? You get nothing + this.assertEquals(nil,FluidDataSet(Server.default,\foo)); this.bootServer(Server.default); - while {Server.default.serverRunning.not}{0.2.wait}; + // while {Server.default.serverRunning.not}{0.2.wait}; + waitForCounts.test = false; + Server.default.doWhenBooted{foo=FluidDataSet(Server.default,\foo)}; waitForCounts.wait; - this.assertEquals(Server.default.numSynths,1,"Dataset: One Synth present after deferred boot"); - waitForCounts.test = false; foo.free; - Server.default.freeAll; waitForCounts.wait; - - this.assertEquals(Server.default.numSynths,0,"Dataset: One Synth present via cretation after boot"); - + this.assertEquals(Server.default.numSynths,0,"Dataset: No synth present via cretation after free"); + foo=FluidDataSet(Server.default,\foo); //Uniqueness test (difficult to run with previous instance of foo, because //UnitTest.bootServer messes with Server alloctors and screws up the ID cache - foo = FluidDataSet(Server.default,\foo); this.assertException({ bar = FluidDataSet(Server.default,\foo); },FluidDataSetExistsError,"DataSetDuplicateError on reused name", onFailure:{ @@ -50,8 +47,7 @@ TestFluidCorpusManipulationServer : UnitTest waitForCounts.test = false; bar = FluidDataSet(Server.default,\bar); waitForCounts.wait; - - this.assertEquals(Server.default.numSynths,2,"Dataset: Two Synths present after new Dataset added"); + this.assertEquals(Server.default.numSynths,2,"Dataset: Two Synths present after new valid Dataset added"); testPoint = Buffer.alloc(Server.default,8); Server.default.sync; From b283494caeeb489453adc26f4bbf816cce646cf1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 8 May 2020 09:43:27 +0100 Subject: [PATCH 098/550] Ensure that all results from synchronous jobs get passed on correctly --- include/FluidSCWrapper.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index a9ba21e..6b530dc 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -313,7 +313,7 @@ public: } w->mClient.setSynchronous(w->mSynchronous); w->mClient.enqueue(w->mParams); - w->mClient.process(); + w->mResult = w->mClient.process(); } /// Check result and report if bad @@ -323,6 +323,8 @@ public: Result r; ProcessState s = w->mClient.checkProgress(r); + if(w->mSynchronous) r = w->mResult; + if ((s == ProcessState::kDone || s == ProcessState::kDoneStillProcessing) || (w->mSynchronous && s == ProcessState::kNoProcess)) // I think this hinges on the fact that @@ -454,6 +456,7 @@ protected: bool mQueueEnabled{false}; bool mCheckingForDone{false}; // only write to this from RT thread kthx bool mCancelled{false}; + Result mResult; }; //////////////////////////////////////////////////////////////////////////////// From d4b11d7200a367801394b3457d74a77cf57f5312 Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 11 May 2020 17:47:21 +0100 Subject: [PATCH 099/550] add FLuidMDS --- release-packaging/Classes/FluidMDS.sc | 17 +++++++++++++++++ src/FluidManipulation/FluidManipulation.cpp | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 release-packaging/Classes/FluidMDS.sc diff --git a/release-packaging/Classes/FluidMDS.sc b/release-packaging/Classes/FluidMDS.sc new file mode 100644 index 0000000..9912b61 --- /dev/null +++ b/release-packaging/Classes/FluidMDS.sc @@ -0,0 +1,17 @@ +FluidMDS : FluidManipulationClient { + var id; + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid).init(uid); + } + + init {|uid| + id = uid; + } + + fitTransform{|sourceDataset, k, dist, destDataset, action| + this.pr_sendMsg(\fitTransform,[sourceDataset.asString, k, dist, destDataset.asString],action); + } + +} diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 596afe6..8a5061f 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidNormalize",ft); makeSCWrapper("FluidStandardize",ft); makeSCWrapper("FluidPCA",ft); + makeSCWrapper("FluidMDS",ft); makeSCWrapper("FluidAudioTransport",ft); makeSCWrapper("FluidBufAudioTransport",ft); } From d73a82d0699b143e98a25e636303ec28ee8e6753 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 13 May 2020 22:48:50 +0100 Subject: [PATCH 100/550] Fix memory leak, and NRT housekeeping --- include/FluidSCWrapper.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6b530dc..2c4c1e0 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -276,6 +276,7 @@ public: // we want to poll thread roughly every 20ms checkThreadInterval = static_cast(0.02 / controlDur()); set_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); }; /// The calc function. Checks to see if we've cancelled, spits out progress, @@ -452,7 +453,7 @@ private: protected: ParamSetType mParams; Client mClient; - bool mSynchronous{true}; + bool mSynchronous{false}; bool mQueueEnabled{false}; bool mCheckingForDone{false}; // only write to this from RT thread kthx bool mCancelled{false}; @@ -604,9 +605,11 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { // first is string size, then chars index size = static_cast(args.next()); + + auto ft = FluidSCWrapper::getInterfaceTable(); + char* chunk = - static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc( - w, asUnsigned(size + 1))); + static_cast(ft->fRTAlloc(w, asUnsigned(size + 1))); if (!chunk) { @@ -619,8 +622,9 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase chunk[i] = static_cast(args.next()); chunk[size] = 0; // terminate string - - return std::string{chunk}; + auto res = std::string{chunk}; + ft->fRTFree(w,chunk); + return res; } From ee666fe7cfb2b8e1b3a3f7b8b00f615a003148a5 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 13 May 2020 22:49:11 +0100 Subject: [PATCH 101/550] All frames as 2D view from Buffer --- include/SCBufferAdaptor.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index df85474..775c330 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -106,6 +106,21 @@ public: bool exists() const override { return mBuffer && mBuffer->data; } + FluidTensorView allFrames() override + { + + FluidTensorView v{mBuffer->data, 0, mBuffer->frames, + mBuffer->channels}; + return v.transpose(); + } + + FluidTensorView allFrames() const override + { + FluidTensorView v{mBuffer->data, 0, mBuffer->frames, + mBuffer->channels}; + return v.transpose(); + } + FluidTensorView samps(index channel) override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, From a3e85b502fa821e39796fa31be755274423b7904 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 13 May 2020 22:49:48 +0100 Subject: [PATCH 102/550] BufMFCC.sc: Remove rogue parameter from kr call --- release-packaging/Classes/FluidBufMFCC.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 458e327..5c336a4 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -16,7 +16,7 @@ FluidBufMFCC : UGen{ } *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, doneAction); + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, doneAction); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action | From 368e7af7d22f498ab3cb4677f278e82847dad994 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 13 May 2020 23:05:49 +0100 Subject: [PATCH 103/550] Add asUGeninput for dataset and labelset --- release-packaging/Classes/FluidDataSet.sc | 6 ++++++ release-packaging/Classes/FluidLabelSet.sc | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index dc1d074..7f7c6d3 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -2,6 +2,12 @@ FluidDataSet : FluidManipulationClient { var <>synth, <>server, <>id; + *asUGenInput { |input| + var ascii = input.asString.ascii; + ^[ascii.size].addAll(ascii) + } + + *kr{ |name| ^this.new1('control',name); } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 147c3a5..313f21a 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -21,6 +21,12 @@ FluidLabelSet : FluidManipulationClient { ^id.asString; } + + *asUGenInput { |input| + var ascii = input.asString.ascii; + ^[ascii.size].addAll(ascii) + } + addLabel{|id, label, action| this.pr_sendMsg(\addLabel,[id.asString, label.asString],action); } @@ -52,4 +58,4 @@ FluidLabelSet : FluidManipulationClient { clear { |action| this.pr_sendMsg(\clear,[],action); } -} \ No newline at end of file +} From c6087b160e6cd2439cfdf3089bbcbaf5c129d448 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 14 May 2020 14:20:13 +0100 Subject: [PATCH 104/550] KMeans: add fitPredict --- release-packaging/Classes/FluidKMeans.sc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index ebe497c..16c3ab3 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -8,6 +8,11 @@ FluidKMeans : FluidManipulationClient { this.pr_sendMsg(\fit,[dataset.asString, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); } + fitPredict{|dataset,labelset, k, maxIter = 100, action| + this.k = k; + this.pr_sendMsg(\fitPredict,[dataset.asString,labelset.asString, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); + } + predict{ |dataset, labelset,action| this.pr_sendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); } From c3f240148d5a2549ba815469d9c8a9345dc9215b Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 14 May 2020 14:21:24 +0100 Subject: [PATCH 105/550] add FluidKNNClassifier / FluidKNNRegressor --- .../Classes/FluidKNNClassifier.sc | 19 ++++++++++++++++++ .../Classes/FluidKNNRegressor.sc | 20 +++++++++++++++++++ .../super-simple-classifier-example.scd | 6 +++--- .../super-simple-regressor-example.scd | 6 +++--- 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 release-packaging/Classes/FluidKNNClassifier.sc create mode 100644 release-packaging/Classes/FluidKNNRegressor.sc diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc new file mode 100644 index 0000000..ef6a7b6 --- /dev/null +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -0,0 +1,19 @@ +FluidKNNClassifier : FluidManipulationClient { + fit{|dataset, labelset, action| + this.pr_sendMsg(\fit,[dataset.asString, labelset.asString], action); + } + + predict{ |dataset, labelset, k, action| + this.pr_sendMsg(\predict, + [dataset.asString, labelset.asString, k], + action, [string(FluidMessageResponse,_,_)] + ); + } + + predictPoint { |buffer, k, action| + this.pr_sendMsg(\predictPoint, + [buffer.asUGenInput, k], action, + [number(FluidMessageResponse,_,_)] + ); + } +} \ No newline at end of file diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc new file mode 100644 index 0000000..d3716b0 --- /dev/null +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -0,0 +1,20 @@ +FluidKNNRegressor : FluidManipulationClient { + fit{|sourceDataset, targetDataset, action| + this.pr_sendMsg(\fit, + [sourceDataset.asString, targetDataset.asString], + action + ); + } + + predict{ |sourceDataset, targetDataset, k, action| + this.pr_sendMsg(\predict, + [sourceDataset.asString, targetDataset.asString, k], + action, + [string(FluidMessageResponse,_,_)]); + } + + predictPoint { |buffer, k, action| + this.pr_sendMsg(\predictPoint, [buffer.asUGenInput, k], action, + [number(FluidMessageResponse,_,_)]); + } +} diff --git a/release-packaging/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/Examples/dataset/super-simple-classifier-example.scd index c365365..8c58f29 100644 --- a/release-packaging/Examples/dataset/super-simple-classifier-example.scd +++ b/release-packaging/Examples/dataset/super-simple-classifier-example.scd @@ -2,7 +2,7 @@ ~simpleInput = FluidDataSet(s,\simpleInput,2); ~simpleOutput = FluidLabelSet(s,\simpleOutput,2); b = Buffer.alloc(s,2); -~knn = FluidKNN(s); +~knn = FluidKNNClassifier(s); k = 3 ) @@ -23,7 +23,7 @@ v.mouseDownAction = {|view, x, y|myx=x;myy=y;w.refresh; Routine{ b.setn(0,[myx,myy]); s.sync; - ~knn.classifyPoint(b, ~simpleOutput, k, {|x|x.postln;}); + ~knn.predictPoint(b, k, {|x|x.postln;}); }.play;}; //custom redraw function @@ -58,6 +58,6 @@ Routine{ ) // fit the dataset -~knn.fit(~simpleInput,action:{"fitting done".postln}) +~knn.fit(~simpleInput,~simpleOutput, action:{"fitting done".postln}) // now click on the grid and read the estimated class according to the nearest K neighbours. diff --git a/release-packaging/Examples/dataset/super-simple-regressor-example.scd b/release-packaging/Examples/dataset/super-simple-regressor-example.scd index e8c4e95..4c5c7bd 100644 --- a/release-packaging/Examples/dataset/super-simple-regressor-example.scd +++ b/release-packaging/Examples/dataset/super-simple-regressor-example.scd @@ -36,8 +36,8 @@ Routine{ ~mappingresult = Buffer.alloc(s,512); //make the process then fit the data -~knn = FluidKNN(s) -~knn.fit(~simpleInput,action:{"fitting done".postln}) +~knn = FluidKNNRegressor(s) +~knn.fit(~simpleInput, ~simpleOutput, action:{"fitting done".postln}) // query 512 points along the line (slow because of all that sync'ing) ( @@ -46,7 +46,7 @@ Routine{ 512.do{|i| b.set(0,i*61); s.sync; - ~knn.regressPoint(b,~simpleOutput,k,action:{|d|~mappingresult.set(i,d);}); + ~knn.predictPoint(b,k,action:{|d|~mappingresult.set(i,d);}); s.sync; i.postln; } From aaffb2de2cb2070cf3f682e5c597f66e9cd10ee3 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 14 May 2020 14:22:30 +0100 Subject: [PATCH 106/550] Replace FluidKNN with FluidKNNClassifier/Regressor --- src/FluidManipulation/FluidManipulation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 8a5061f..ebd4ee2 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -5,7 +5,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -23,7 +24,8 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidLabelSet",ft); makeSCWrapper("FluidKDTree",ft); makeSCWrapper("FluidKMeans",ft); - makeSCWrapper("FluidKNN",ft); + makeSCWrapper("FluidKNNClassifier",ft); + makeSCWrapper("FluidKNNRegressor",ft); makeSCWrapper("FluidNormalize",ft); makeSCWrapper("FluidStandardize",ft); makeSCWrapper("FluidPCA",ft); From 4c2d96f2449769fb7f304f461ebae8d224d9ec73 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 19 May 2020 11:11:36 +0100 Subject: [PATCH 107/550] Wrapper: make sure world is available if we're going to use it --- include/FluidSCWrapper.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6a5a58e..661cc4c 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -835,7 +835,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase index size = static_cast(args.next()); auto ft = FluidSCWrapper::getInterfaceTable(); - + auto w = x->mWorld; char* chunk = static_cast(ft->fRTAlloc(w, asUnsigned(size + 1))); @@ -1117,7 +1117,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ArgTuple& args = msg->args; // type check OSC message - std::string tags(inArgs->tags + inArgs->count); + std::string tags(inArgs->tags + inArgs->count);//evidently this needs commenting: construct string at pointer offset by tag count, to pick up args bool willContinue = true; bool typesMatch = true; From 00a238dae767995dd13d9dc8712af99f9b676760 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 19 May 2020 16:59:50 +0100 Subject: [PATCH 108/550] Improve order async behaviour for synth; fix symbol vs string; default behaviour for cols/size; update interfaces --- release-packaging/Classes/FluidDataSet.sc | 16 +++++++++---- release-packaging/Classes/FluidKDTree.sc | 6 ++++- release-packaging/Classes/FluidKMeans.sc | 15 +++++------- release-packaging/Classes/FluidLabelSet.sc | 20 ++++++++++++---- .../Classes/FluidManipulationClient.sc | 24 +++++++++++++------ .../Classes/FluidMessageResponse.sc | 2 +- 6 files changed, 56 insertions(+), 27 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index a49ef5d..06499ea 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -32,7 +32,7 @@ FluidDataSet : FluidManipulationClient { serverCaches.initCache(server); serverCaches.put(server,id,this); } - + *asUGenInput { |input| var ascii = input.asString.ascii; ^[ascii.size].addAll(ascii) @@ -43,23 +43,28 @@ FluidDataSet : FluidManipulationClient { ^"FluidDataSet(%)".format(id).asString; } + asSymbol { + ^id.asSymbol + } + addPoint{|label, buffer, action| - this.prSendMsg(\addPoint,[label.asString,buffer.asUGenInput],action); + this.prSendMsg(\addPoint,[label.asSymbol,buffer.asUGenInput],action); } getPoint{|label, buffer, action| - this.prSendMsg(\getPoint,[label.asString,buffer.asUGenInput],action); + this.prSendMsg(\getPoint,[label.asSymbol,buffer.asUGenInput],action); } updatePoint{|label, buffer, action| - this.prSendMsg(\updatePoint,[label.asString,buffer.asUGenInput],action); + this.prSendMsg(\updatePoint,[label.asSymbol,buffer.asUGenInput],action); } deletePoint{|label, action| - this.prSendMsg(\deletePoint,[label.asString],action); + this.prSendMsg(\deletePoint,[label.asSymbol],action); } cols {|action| + action ?? {action = postit}; this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } @@ -72,6 +77,7 @@ FluidDataSet : FluidManipulationClient { } size { |action| + action ?? {action = postit}; this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); } diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 0f97a09..71306d7 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -12,7 +12,8 @@ FluidKDTree : FluidManipulationClient { } fit{|dataset,action| - this.prSendMsg(\fit,[dataset.asUGenInput],action); + dataset.asSymbol.postln; + this.prSendMsg(\fit,[dataset.asSymbol],action); } kNearest{ |buffer, k,action| @@ -24,6 +25,9 @@ FluidKDTree : FluidManipulationClient { } cols { |action| + + action ?? {action = postit}; + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 24c6cc1..22b95f7 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -1,7 +1,7 @@ FluidKMeans : FluidManipulationClient { var <>k; - + *new {|server| var uid = UniqueID.next; ^super.new(server,uid)!?{|inst|inst.init(uid);inst} @@ -10,24 +10,20 @@ FluidKMeans : FluidManipulationClient { init {|uid| id = uid; } - + fit{|dataset,k, maxIter = 100, buffer, action| buffer = buffer ? -1; this.k = k; - this.prSendMsg(\fit,[dataset.asString, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); + this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); } fitPredict{|dataset,labelset, k, maxIter = 100, action| this.k = k; - this.pr_sendMsg(\fitPredict,[dataset.asString,labelset.asString, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); + this.prSendMsg(\fitPredict,[dataset.asSymbol,labelset.asSymbol, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); } predict{ |dataset, labelset,action| - this.prSendMsg(\predict,[dataset.asString, labelset.asString],action,[numbers(FluidMessageResponse,_,this.k,_)]); - } - - getClusters{ |dataset, labelset,action| - this.prSendMsg(\getClusters,[dataset.asString, labelset.asString],action); + this.prSendMsg(\predict,[dataset.asSymbol, labelset.asSymbol],action,[numbers(FluidMessageResponse,_,this.k,_)]); } predictPoint { |buffer, action| @@ -35,6 +31,7 @@ FluidKMeans : FluidManipulationClient { } cols { |action| + action ?? action = postit; this.prSendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 645e918..398d972 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -22,7 +22,7 @@ FluidLabelSet : FluidManipulationClient { } init { |name| - this.id = name; + id = name; this.cache; } @@ -35,7 +35,12 @@ FluidLabelSet : FluidManipulationClient { ^"FluidLabelSet(%)".format(id).asString; } - + asSymbol { + ^id + } + + + *asUGenInput { |input| var ascii = input.asString.ascii; ^[ascii.size].addAll(ascii) @@ -45,6 +50,11 @@ FluidLabelSet : FluidManipulationClient { this.prSendMsg(\addLabel,[id.asString, label.asString],action); } + updateLabel{|id, label, action| + this.prSendMsg(\updateLabel,[id.asString, label.asString],action); + } + + getLabel{|id, action| this.prSendMsg(\getLabel,[id.asString],action,[string(FluidMessageResponse,_,_)]); } @@ -54,6 +64,7 @@ FluidLabelSet : FluidManipulationClient { } cols {|action| + action ?? {action = postit}; this.prSendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); } @@ -66,6 +77,7 @@ FluidLabelSet : FluidManipulationClient { } size { |action| + action ?? {action = postit}; this.prSendMsg(\size,[],action,[number(FluidMessageResponse,_,_)]); } @@ -77,8 +89,8 @@ FluidLabelSet : FluidManipulationClient { serverCaches.remove(server,id); super.free; } - + *freeAll { |server| serverCaches.do(server,{|x|x.free;}); } -} +} diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index e57bd7d..552aa23 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -24,6 +24,9 @@ FluidManipulationClient { var defName, def; var onSynthFree, persist; + var postit; + var < ready; + *prServerString{ |s| var ascii = s.ascii; ^[ascii.size].addAll(ascii) @@ -45,29 +48,35 @@ FluidManipulationClient { baseinit { |...args| var makeFirstSynth; id = UniqueID.next; + postit = {|x| x.postln;}; defName = (this.class.name.asString ++ id).asSymbol; - + ready = Condition(false); def = SynthDef(defName,{ var ugen = FluidProxyUgen.kr(this.class.name, *args); this.ugen = ugen; ugen - }).add; - + }); persist = true; onSynthFree = { + ready.test = false; synth = nil; if(persist){ //If we don't sync here, cmd-. doesn't reset properly (but server.freeAll does) - fork { + forkIfNeeded { server.sync; synth = Synth(defName,target: RootNode(server)); synth.onFree{onSynthFree.value}; + ready.test = true; + ready.signal; } } }; - - server.doWhenBooted{onSynthFree.value}; + forkIfNeeded{ + def.add; + server.sync; + onSynthFree.value; + } } free{ @@ -79,6 +88,7 @@ FluidManipulationClient { prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); + synth ?? {"Not ready".warn}; synth !? { OSCFunc( { |msg| @@ -134,8 +144,8 @@ FluidServerCache { } clearCache { |server| - cache.postln; cache[server] !? { cache.removeAt(server) }; } + } diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc index 51ba197..60b97f7 100644 --- a/release-packaging/Classes/FluidMessageResponse.sc +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -20,7 +20,7 @@ FluidMessageResponse : Object var split = a.find([0],offset); var res; if(split.isNil) {"ERROR: can't parse string from server".throw}; - ^[[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInt.asAscii},String)], split + 1] + ^[[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInteger.asAscii},String)], split + 1] } *numbers{ |a, n, offset| From ddce5473a8b00537f4f41ef980d9cb3338df8637 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 19 May 2020 17:00:18 +0100 Subject: [PATCH 109/550] Improving help files: Dataset, Labelset, KDTree and KMeans --- .../HelpSource/Classes/FluidDataSet.schelp | 141 +++++++-------- .../HelpSource/Classes/FluidKDTree.schelp | 113 ++++++++---- .../HelpSource/Classes/FluidKMeans.schelp | 167 +++++++++++++----- .../HelpSource/Classes/FluidLabelSet.schelp | 91 +++++----- 4 files changed, 322 insertions(+), 190 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index f09b433..0212061 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -4,137 +4,138 @@ categories:: UGens>FluidManipulation related:: Classes/FluidLabelSet, Classes/FluidKDTree, Classes/FluidKNN, Classes/FluidKMeans ​ DESCRIPTION:: -A server-side container associating labels with multi-dimensional data. FluidDataSet is identified by its name, and multiple instances of the object with the same name point to the same instance on the server. +A server-side container associating labels with multi-dimensional data. FluidDataSet is identified by its name. + + ​ CLASSMETHODS:: ​ -PRIVATE::kr +PRIVATE:: asUGenInput METHOD:: new -Create a new instance of the dataset, with the given name and dimensionality. If an instance already exists on the server, then the existing dimensionality takes precedence. -​ +Create a new instance of the dataset, with the given name. If a Dataset with this name already exists, an exception will be thrown (see link::Classes/FluidDataSet#at:: to access an extant Dataset) + ARGUMENT:: server The link::Classes/Server:: on which to create the data set - ARGUMENT:: name A symbol or string with the name of the dataset. ​ -ARGUMENT:: dims -An integer number of dimensions -​ returns:: The new instance +METHOD:: at +Retreives a cached instance of a FluidDataSet with the given name, or returns nil if no such object exists. + +ARGUMENT:: server +The server associated with this dataset instance +ARGUMENT:: id +The name of the Dataset to retreive from the cache + + INSTANCEMETHODS:: ​ -PRIVATE::init,id +PRIVATE:: init,id,cache -METHOD:: synth -The internal synth the object uses to communicate with the server -​ -returns:: A link::Classes/Synth:: -​ -METHOD:: server -The server instance the object uses -​ -returns:: A link::Classes/Server:: -​​​ +METHOD:: addPoint +Add a new point to the data set. The dimensionality of the dataset is governed by the size of the first point added. +Will report an error if the label already exists, or if the size of the data does not match the dimensionality of the dataset. +ARGUMENT:: label +A symbol or string with the label for the new point +ARGUMENT:: buffer +A link::Classes/Buffer:: with the new data point +ARGUMENT:: action +A function to run when the point has been added +​​ METHOD:: updatePoint Update an existing label's data. Will report an error if the label doesn't exist, or if the size of the data does not match the given dimensionality of the dataset. -​ ARGUMENT:: label symbol or string with the label -​ ARGUMENT:: buffer A link::Classes/Buffer:: containing the updated data -​ ARGUMENT:: action A function to run when the server has updated -​​​ METHOD:: size Report the number of items currently in the data set ​ -ARGUMENT:: action -A function to run when the server responds, whose argument is the data set size -​​ -METHOD:: addPoint -Add a new point to the data set. Will report an error if the label already exists, or if the size of the data does not match the given dimensionality of the dataset. -​ +METHOD:: getPoint +Retreive a point from the data set into a link::Classes/Buffer::. Will report an error if the label or buffer doesn't exist​ ARGUMENT:: label -A symbol or string with the label for the new point -​ +symbol or string with the label to retreive ARGUMENT:: buffer -A link::Classes/Buffer:: with the new data point -​ -ARGUMENT:: action -A function to run when the point has been added -​​ -METHOD:: write -Write the data set to disk as a JSON file. Will not overwrite existing files -​ -ARGUMENT:: filename -Absolute path for the new file -​ +link::Classes/Buffer:: to fill ARGUMENT:: action -A function to run when the file has been written -​​ -METHOD:: asString -​ -returns:: The name of the data set as a string -​ +function to run when the point has been retreived + + METHOD:: deletePoint Remove a point from the data set. Will report an error if the label doesn't exist. -​ ARGUMENT:: label symbol or string with the label to remove -​ ARGUMENT:: action Function to run when the point has been deleted ​​ METHOD:: clear Empty the data set -​ ARGUMENT:: action Function to run when the data set has been emptied ​ -METHOD:: getPoint -Retreive a point from the data set into a link::Classes/Buffer::. Will report an error if the label or buffer doesn't exist -​ -ARGUMENT:: label -symbol or string with the label to retreive -​ -ARGUMENT:: buffer -link::Classes/Buffer:: to fill -​ +METHOD:: free +Destroy the object on the server + +METHOD:: cols +Report the dimensionality of the data set. If action is nil, will default to posting result. ARGUMENT:: action -function to run when the point has been retreived -​​ +A function to run when the server responds, whose argument is the data set dimensionality. By default, the method will print the response to the post window. + +METHOD:: size +Report the number of points in the data set. If action is nil, will default to posting result. +ARGUMENT:: action +A function to run when the server responds, whose argument is the data set size. By default, the method will print the response to the post window. + + METHOD:: read Read a data set from a JSON file on disk -​ ARGUMENT:: filename The absolute path of the JSON file to read -​ ARGUMENT:: action A function to run when the file has been read +​ +METHOD:: write +Write the data set to disk as a JSON file. +ARGUMENT:: filename +Absolute path for the new file +ARGUMENT:: action +A function to run when the file has been written ​​ -METHOD:: cols -Report the dimensionality of the data set +METHOD:: asString +​Responds with the name of the data set as a pretty(ish) string + +METHOD:: asSymbol +​Responds with the name of the data set as a symbol + +METHOD:: synth +The internal synth the object uses to communicate with the server +​ +returns:: A link::Classes/Synth:: +​ +METHOD:: server +The server instance the object uses ​ +returns:: A link::Classes/Server:: + EXAMPLES:: CODE:: - +( // Make a one-dimensional data set called 'simple1data' ~ds = FluidDataSet.new(s,\simple1data,1) // Make a buffer to use for adding points ~point = Buffer.alloc(s,1,1) //Add 10 points, using the index as a label. -( Routine{ 10.do{|i| ~point.set(0,i); - s.sync; - ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); + s.sync; } }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index 1476d77..21dbc79 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -4,20 +4,41 @@ categories:: FluidManipulation related:: Classes/FluidDataSet DESCRIPTION:: -A server-side K-Dimensional tree for efficient neighbourhood searches of multi-dimensional data. See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algorithms for more on KD Trees +A server-side K-Dimensional tree for efficient neighbourhood searches of multi-dimensional data. + +See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algorithms for more on KD Trees CLASSMETHODS:: +METHOD:: new +Make a new KDTree model for the given server +ARGUMENT:: server +The server on which to make the model + INSTANCEMETHODS:: -METHOD:: read -Set the object's state from a JSON file +METHOD:: fit +Build the tree by scanning the points of a LINK::Classes/FluidDataSet:: -ARGUMENT:: filename -The location of a JSON file on disk +ARGUMENT:: dataset +The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one. ARGUMENT:: action -function to run when the data is loaded +A function to run when indexing is complete + + +METHOD:: kNearest +Returns the IDs of the CODE::k:: points nearest to the one passed + +ARGUMENT:: buffer +A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. + +ARGUMENT:: k +The number of neighbours to return + + +ARGUMENT:: action +A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet:: METHOD:: kNearestDist Get the distances of the K nearest neighbours to a point @@ -31,16 +52,22 @@ The number of neighbours to search ARGUMENT:: action A function that will run when the query returns, whose argument is an array of distances -returns:: nothing, but could return an array if you like +METHOD:: cols +Get the dimensionality of the data that the tree is indexed against -METHOD:: fit -Build the tree by scanning the points of a LINK::Classes/FluidDataSet:: +ARGUMENT:: action +A function that runs when the query returns, whose argument is the dimensionality -ARGUMENT:: dataset -The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one. + +METHOD:: read +Set the object's state from a JSON file + +ARGUMENT:: filename +The location of a JSON file on disk ARGUMENT:: action -A function to run when indexing is complete +function to run when the data is loaded + METHOD:: write Write the index of the tree to disk. Currently this will not overwrite extant files. @@ -51,28 +78,50 @@ The path of a JSON file to write ARGUMENT:: action A function to run when writing is complete -METHOD:: kNearest -Returns the IDs of the CODE::k:: points nearest to the one passed - -ARGUMENT:: buffer -A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. - -ARGUMENT:: k -The number of neighbours to return - -ARGUMENT:: action -A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet:: - -returns:: Nothing, but could return an array of IDs if you like - -METHOD:: cols -Get the dimensionality of the data that the tree is indexed against - -ARGUMENT:: action -A function that runs when the query returns, whose argument is the dimensionality EXAMPLES:: code:: -(some example code) +//Make some 2D points and place into a dataset +( +~points = 100.collect{ [ 1.0.linrand,1.0.linrand] }; +~dataset= FluidDataSet(s,\kdtree_help_rand2d); +~dataset.free +~tmpbuf = Buffer.alloc(s,2) ; +fork{ + ~dataset.ready.wait; + ~points.do{|x,i| + (""++(i+1)++"/100").postln; + ~tmpbuf.setn(0,x); + ~dataset.addPoint(i,~tmpbuf); + s.sync + } +} +) + + + + + +//Make a new tree, and fit it to the dataset +( +fork{ + ~tree = FluidKDTree(s); + ~tree.ready.wait; + s.sync; + ~tree.fit(~dataset); +} +) + +//Dims of tree should match dataset +~tree.cols + +//Return labels of k nearest points to new data +( +~tmpbuf.setn(0,[ 1.0.linrand,1.0.linrand ]); +~tree.kNearest(~tmpbuf,5, { |a| a.postln }); +) + +//or the distances +~tree.kNearestDist(~tmpbuf,5, { |a| a.postln }); :: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index fa27fd3..bd2384e 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -10,87 +10,168 @@ https://scikit-learn.org/stable/tutorial/statistical_inference/unsupervised_lear CLASSMETHODS:: +METHOD:: new +Construct a new K Means model on the passed server +ARGUMENT:: server +If nil will use Server.default + INSTANCEMETHODS:: PRIVATE::k -METHOD:: predictPoint -Given a trained object, return the cluster ID for a data point in a link::Classes/Buffer:: - -ARGUMENT:: buffer -a link::Classes/Buffer:: containing a data point - -ARGUMENT:: action -A function to run when the server responds, taking the ID of the cluser as its argument - METHOD:: fit Identify code::k:: clusters in a link::Classes/FluidDataSet:: - ARGUMENT:: dataset A link::Classes/FluidDataSet:: of data points - ARGUMENT:: k The number of clusters to identify in the data set - ARGUMENT:: maxIter Maximum number of iterations to use partitioning the data - ARGUMENT:: buffer Seed centroids for clusters WARNING:: Not yet implemented :: - ARGUMENT:: action A function to run when fitting is complete, taking as its argument an array with the number of data points for each cluster -METHOD:: write -write learned clusters to disk as a JSON file. Will not overwrite existing files - -ARGUMENT:: filename -Absolute path for file - -ARGUMENT:: action -A function to run when the file is written - -METHOD:: read -Read a learned clustering of a data set from a JSON file - -ARGUMENT:: filename -Absolute path of the JSON file - +METHOD:: predict +Given a trained object, return the cluster ID for each data point in a dataset to a label set. +ARGUMENT:: dataset +a link::Classes/FluidDataSet:: containing the data to predict +ARGUMENT:: labelset +a link::Classes/FluidLabelSet:: to reveive the predicted clusters ARGUMENT:: action -Function to run when the file has been read - -METHOD:: getClusters -Fill a link::Classes/FluidLabelSet:: with the assignments for each point in the passed link::Classes/FluidDataSet:: that was used to train this instance +A function to run when the server responds +METHOD:: fitPredict +Run link::Classes/FluidKMeans#*fit:: and link::Classes/FluidKMeans#*predict:: in a single pass: i.e. train the model on the incoming link::Classes/FluidDataSet:: and then return the learned clustering to the passed link::Classes/FluidLabelSet:: ARGUMENT:: dataset -The link::Classes/FluidDataSet:: used to train this instance - +a link::Classes/FluidDataSet:: containing the data to fit and predict ARGUMENT:: labelset -A link::Classes/FluidLabelSet:: to fill with assignments +a link::Classes/FluidLabelSet:: to reveive the predicted clusters +ARGUMENT:: k +The number of clusters to identify in the data set +ARGUMENT:: maxIter +Maximum number of iterations to use partitioning the data +ARGUMENT:: action +A function to run when the server responds +METHOD:: predictPoint +Given a trained object, return the cluster ID for a data point in a link::Classes/Buffer:: +ARGUMENT:: buffer +a link::Classes/Buffer:: containing a data point ARGUMENT:: action -A function to run when the operation is complete +A function to run when the server responds, taking the ID of the cluser as its argument + + METHOD:: cols Retreive the dimentionality of the dataset this instance is trained on - ARGUMENT:: action A function to run when the server responds, taking the dimensionality as its argument METHOD:: predict Report cluster assignments for previously unseen data - ARGUMENT:: dataset A link::Classes/FluidDataSet:: of data points - ARGUMENT:: labelset A link::Classes/FluidLabelSet:: to contain assigments - ARGUMENT:: action A function to run when complete, taking an array of the counts for each catgegory as its argument -EXAMPLES:: + +METHOD:: write +write learned clusters to disk as a JSON file. Will not overwrite existing files +ARGUMENT:: filename +Absolute path for file +ARGUMENT:: action +A function to run when the file is written + +METHOD:: read +Read a learned clustering of a data set from a JSON file +ARGUMENT:: filename +Absolute path of the JSON file +ARGUMENT:: action +Function to run when the file has been read + + +EXAMPLES:: +Server.default.options.outDevice = "Built-in Output" code:: -(some example code) + +//A dataset for our points, a labelset for cluster labels +( +~dataset= FluidDataSet(s,\kdtree_help_rand2d); + +~clusters = FluidLabelSet(s,\kmeans_help_clusters); +) + +//Make some clumped 2D points and place into a dataset +( +~points = (4.collect{64.collect{(1.sum3rand) + [1,-1].choose}.clump(2)}).flatten(1) * 0.5; +~dataset.clear; +~tmpbuf = Buffer.alloc(s,2); +fork{ + s.sync; + ~points.do{|x,i| + (""++(i+1)++"/128").postln; + ~tmpbuf.setn(0,x); + ~dataset.addPoint(i,~tmpbuf); + s.sync + } +} +) + +//Make a new k means model, fit it to the dataset and return the discovered clusters to a labelset +( +fork{ + ~clusters.clear; + ~kmeans = FluidKMeans(s); + s.sync; + ~kmeans.fitPredict(~dataset,~clusters, 4,action: {|c| + "Fitted.\n # Points in each cluster:".postln; + c.do{|x,i| + ("Cluster" + i + "->" + x.asInteger + "points").postln; + } + }); +} +) + +//Dims of kmeans should match dataset +~kmeans.cols + +//Return labels of clustered points +( +~assignments = Array.new(128); +fork{ + 128.do{ |i| + ~clusters.getLabel(i,{|clusterID| + (i.asString+clusterID).postln; + ~assignments.add(clusterID) + }); + s.sync; + } +} +) + +//Visualise: we're hoping to see colours neatly mapped to quandrants... +( +d = ((~points + 1) * 0.5).flatten(1).unlace; +// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; +w = Window("scatter", Rect(128, 64, 200, 200)); +~colours = [Color.blue,Color.red,Color.green,Color.magenta]; +w.drawFunc = { + Pen.use { + d[0].size.do{|i| + var x = (d[0][i]*200); + var y = (d[1][i]*200); + var r = Rect(x,y,5,5); + Pen.fillColor = ~colours[~assignments[i].asInteger]; + Pen.fillOval(r); + } + } +}; +w.refresh; +w.front; +) + :: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp index 4bd10f6..77d812c 100644 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -12,84 +12,85 @@ CLASSMETHODS:: PRIVATE:: kr METHOD:: new -Make a new instance of a label set, uniquely identified by its name. Multiple instances to of this class with the same name refer to the same server-side entity. - +Make a new instance of a label set, uniquely identified by its name. Creating an instance with a name already in use will throw an exception. Use link::Classes/FluidLabelSet#*at:: or free the existing instance. ARGUMENT:: server The link::Classes/Server:: on which to create the label set - ARGUMENT:: name symbol or string with the label set's name +METHOD:: at +Retreive a label set from the cache +ARGUMENT:: server +The link::Classes/Server:: on which to create the label set +ARGUMENT:: id +symbol or string with the label set's name + + INSTANCEMETHODS:: -PRIVATE:: init, id, server, synth +PRIVATE:: init, id + +METHOD:: addLabel +Add a label to the label set +ARGUMENT:: id +symbol or string with the ID for this label +ARGUMENT:: label +symbol or string with the label to add +ARGUMENT:: action +function to run when the operation completes + +METHOD:: updateLabel +Change a label in the label set +ARGUMENT:: id +symbol or string with the ID for this label +ARGUMENT:: label +symbol or string with the label to add +ARGUMENT:: action +function to run when the operation completes METHOD:: getLabel Retreive the label associated with an ID. Will report an error if the ID isn't present in the set - ARGUMENT:: id symbol or string with the ID to retreive. - ARGUMENT:: action A function to run when the server responds, with the label as its argument +METHOD:: deleteLabel +Remove a id-label pair from the label set +ARGUMENT:: id +symbol or string with the ID to remove +ARGUMENT:: action +A function to run when the label has been removed + +METHOD:: clear +Empty the label set +ARGUMENT:: action +Function to run whrn the action completes + +METHOD:: size +Report the number of items in the label set +ARGUMENT:: action +A function to run when the server responds, taking the size as its argument + METHOD:: cols Returns the dimensionality of the link::Classes/FluidDataSet:: associated with this label set - ARGUMENT:: action A function to run when the server responds, with the dimensionality as its argument METHOD:: write -Write this label set to disk as a JSON file. Will not overwrite existing files. - +Write this label set to disk as a JSON file. ARGUMENT:: filename Absolute path of file to write - ARGUMENT:: action A function to run when the file is written METHOD:: read Read a label set from a JSON file on disk - ARGUMENT:: filename Absolute path of the file to read - ARGUMENT:: action A function to run when the file is read -METHOD:: deleteLabel -Remove a id-label pair from the label set - -ARGUMENT:: id -symbol or string with the ID to remove - -ARGUMENT:: action -A function to run when the label has been removed - -METHOD:: size -Report the num er of items in the label set - -ARGUMENT:: action -A function to run when the server responds, taking the size as its argument - -METHOD:: addLabel -Add a label to the label set - -ARGUMENT:: id -symbol or string with the ID for this label - -ARGUMENT:: label -symbol or string with the label to add - -ARGUMENT:: action -function to run when the operation completes - -METHOD:: clear -Empty the label set - -ARGUMENT:: action -Function to run whrn the action completes - EXAMPLES:: code:: From 6305274d522f04ddcbb41a62285f33be06854211 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 00:21:31 +0100 Subject: [PATCH 110/550] Quietly remove a thing I didn't realised I'd committed It's probably good, if doing experimental changes that break everytihng, not to commit and push them to the main branch --- include/SCBufferAdaptor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index 2be9d9d..bca7445 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -105,10 +105,10 @@ public: // knows about bool valid() const override { - return (exists() && (mLocal ? true : mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs))); + return (mLocal ? true : mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs)); } - bool exists() const override { return mBuffer && mBuffer->data; } + bool exists() const override { return true; } FluidTensorView allFrames() override { From a109652612853696ee51154ef8c60170ddd7f452 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 00:57:32 +0100 Subject: [PATCH 111/550] Update and augment help for FluidNormalize, FluidStandardize and fix classes --- release-packaging/Classes/FluidNormalize.sc | 21 ++-- release-packaging/Classes/FluidStandardize.sc | 19 +-- .../HelpSource/Classes/FluidNormalize.schelp | 109 ++++++++++++----- .../Classes/FluidStandardize.schelp | 112 ++++++++++++++---- 4 files changed, 195 insertions(+), 66 deletions(-) diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index ee25f1b..674d481 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -1,8 +1,8 @@ FluidNormalize : FluidManipulationClient { - *new {|server| + *new {|server, min = 0, max = 1| var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + ^super.new(server,uid,min,max)!?{|inst|inst.init(uid);inst} } init {|uid| @@ -10,18 +10,23 @@ FluidNormalize : FluidManipulationClient { } fit{|dataset, action| - this.prSendMsg(\fit,[dataset.asString],action); + this.prSendMsg(\fit,[dataset.asSymbol],action); } - normalize{|sourceDataset, destDataset, action| - this.prSendMsg(\normalize,[sourceDataset.asString, destDataset.asString],action); + fitTransform{|dataset, action| + this.prSendMsg(\fit,[dataset.asSymbol],action); } - normalizePoint{|sourceBuffer, destBuffer, action| - this.prSendMsg(\normalizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + transform{|sourceDataset, destDataset, action| + this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); + } + + transformPoint{|sourceBuffer, destBuffer, action| + this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| + action ?? {action = postit}; this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } @@ -33,4 +38,4 @@ FluidNormalize : FluidManipulationClient { this.prSendMsg(\write,[filename.asString],action); } -} +} diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index d3e5a6d..4be99cd 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -8,20 +8,25 @@ FluidStandardize : FluidManipulationClient { init {|uid| id = uid; } - + fit{|dataset, action| - this.prSendMsg(\fit,[dataset.asString],action); + this.prSendMsg(\fit,[dataset.asSymbol],action); + } + + transform{|sourceDataset, destDataset, action| + this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); } - standardize{|sourceDataset, destDataset, action| - this.prSendMsg(\standardize,[sourceDataset.asString, destDataset.asString],action); + fitTransform{|dataset, action| + this.prSendMsg(\fitTransform,[dataset.asSymbol],action); } - standardizePoint{|sourceBuffer, destBuffer, action| - this.prSendMsg(\standardizePoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); + transformPoint{|sourceBuffer, destBuffer, action| + this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } cols {|action| + action ?? {action = postit}; this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } @@ -33,4 +38,4 @@ FluidStandardize : FluidManipulationClient { this.prSendMsg(\write,[filename.asString],action); } -} +} diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index 1848cd3..a416063 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -14,81 +14,136 @@ private:: kr METHOD:: new Create a new instance - ARGUMENT:: server The link::Classes/Server:: on which to run - ARGUMENT:: min Minimum output value, default 0 - ARGUMENT:: max Maximum output value, default 1 -returns:: new instance - INSTANCEMETHODS:: METHOD:: fit Normalize a link::Classes/FluidDataSet:: strong::in-place:: - ARGUMENT:: dataset The link::Classes/FluidDataSet:: to normalize +ARGUMENT:: action +A function to run when processing is complete +METHOD:: fitTransform +Normalize a link::Classes/FluidDataSet:: strong::in-place:: +ARGUMENT:: dataset +The link::Classes/FluidDataSet:: to normalize ARGUMENT:: action A function to run when processing is complete -METHOD:: normalize +METHOD:: transform Normalize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: - ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to normalize - ARGUMENT:: destDataset The link::Classes/FluidDataSet:: to populate with normalized data - ARGUMENT:: action A function to run when processing is complete -METHOD:: normalizePoint +METHOD:: transformPoint Normalize a new data point, using the learned extrema from a previous link::Classes/FluidNormalize#fit::ting - ARGUMENT:: sourceBuffer A link::Classes/Buffer:: with the new data point - ARGUMENT:: destBuffer A link::Classes/Buffer:: to contain the normalized value - ARGUMENT:: action A function to run when processing is complete +METHOD:: cols +Retreive the dimensionality of the data set we have fitted on +ARGUMENT:: action +A function to run when the server responds, taking the dimensions as its argument + METHOD:: read Load internal state (dimensionality, mins, maxes) from a JSON file - ARGUMENT:: filename Absolute path to the JSON file - ARGUMENT:: action A function to run when file is loaded METHOD:: write Store the internal state of object on disk as a JSON file. Will not overwrite existing files - ARGUMENT:: filename Absolute path of file to write - ARGUMENT:: action A function to run when file is written -METHOD:: cols -Retreive the dimensionality of the data set we have fitted on - -ARGUMENT:: action -A function to run when the server responds, taking the dimensions as its argument - EXAMPLES:: - code:: -(some example code) -:: \ No newline at end of file +s.boot; +//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidNormalize +( +~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; +~raw = FluidDataSet(s,\norm_help_raw); +~norm = FluidDataSet(s,\norm_help_normd); +~audio = Buffer.read(s,~audiofile); +~pitch_feature = Buffer.new(s); +~stats = Buffer.new(s); +~datapoint = Buffer.alloc(s,2); +~normalizer = FluidNormalize(s); +) + +// Do a pitch analysis on the audio, which gives us pitch and pitch confidence (so a 2D datum) +// Divide the time series in to 10, and take the mean of each segment and add this as a point to +// the 'raw' FluidDataSet +( +~raw.clear; +~norm.clear; +FluidBufPitch.process(s,~audio,features:~pitch_feature,action:{ + "Pitch analysis.complete. Doing stats".postln; + fork{ + var chunkLen = (~pitch_feature.numFrames / 10).asInteger; + 10.do{ |i| + s.sync; FluidBufStats.process(s,~pitch_feature,startFrame:i*chunkLen,numFrames:chunkLen,stats:~stats, action:{ + ~stats.loadToFloatArray(action:{ |statsdata| + [statsdata[0],statsdata[1]].postln; + ~datapoint.setn(0,[statsdata[0],statsdata[1]]); + s.sync; + ("Adding point" ++ i).postln; + ~raw.addPoint(i,~datapoint); + }) + }); + if(i == 9) {"Analysis done, dataset ready".postln} + } + } +}); +) + +//Fit the FluidNormalizer to the raw data, and then apply the scaling out of place into +//our second FluidDataSet, so we can compare. +//Download the dataset contents into arrays for plotting +( +~normalizer.fit(~raw); +~normalizer.transform(~raw,~norm); +~rawarray = Array.new(10); +~normedarray= Array.new(10); +fork{ + 10.do{|i| + ~raw.getPoint(i,~datapoint,{ + ~datapoint.loadToFloatArray(action:{|a| ~rawarray.add(Array.newFrom(a))}) + }); + s.sync; + ~norm.getPoint(i,~datapoint,{ + + ~datapoint.loadToFloatArray(action:{|a| ~normedarray.add(Array.newFrom(a))}) + }); + s.sync; + if(i==9){"Data downloaded".postln}; + } +} +) +//Plot side by side. Before normalization the two dimensions have radically different scales +//which can be unhelpful in many cases +( +~rawarray.flatten(1).unlace.plot("Unnormalized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; +~plot2 = ~normedarray.flatten(1).unlace.plot("Normalized",Rect(410,0,400,400)).plotMode=\bars; +) +:: diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index 5c94be7..217799b 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -1,7 +1,7 @@ TITLE:: FluidStandardize summary:: Standardize a FluidDataSet categories:: FluidManipulation -related:: Classes/FluidDataSet, Classes/FluidNormalize +related:: Classes/FluidDataSet, Classes/FluidStandardize DESCRIPTION:: Standardize a link::Classes/FluidDataSet::, i.e. rescale using its mean(s) and standard deviation(s) in each dimension. @@ -10,68 +10,132 @@ See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html CLASSMETHODS:: +METHOD:: new +Create a new instance +ARGUMENT:: server +The server for this model INSTANCEMETHODS:: METHOD:: fit -Standardize a link::Classes/FluidDataSet:: strong::in-place:: - +Fit model to a dataset without applying scaling ARGUMENT:: dataset The link::Classes/FluidDataSet:: to standardize - ARGUMENT:: action A function to run when processing is complete -METHOD:: standardize +METHOD:: transform Standrdize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: - ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to standardize - ARGUMENT:: destDataset The link::Classes/FluidDataSet:: to populate with standardized data +ARGUMENT:: action +A function to run when processing is complete +METHOD:: fitTransform +Standrdize a link::Classes/FluidDataSet:: fit and scale a link::Classes/FluidDataSet:: in-place +ARGUMENT:: sourceDataset +The link::Classes/FluidDataSet:: to standardize ARGUMENT:: action A function to run when processing is complete -METHOD:: standardizePoint +METHOD:: transformPoint Standardize a new data point, using the learned statistics from a previous link::Classes/FluidStandardize#fit::ting - ARGUMENT:: sourceBuffer A link::Classes/Buffer:: with the new data point - ARGUMENT:: destBuffer A link::Classes/Buffer:: to contain the standardize value - ARGUMENT:: action A function to run when processing is complete +METHOD:: cols +Retreive the dimensionality of the data set we have fitted on +ARGUMENT:: action +A function to run when the server responds, taking the dimensions as its argument. Will print to post window by default + METHOD:: read Load internal state (dimensionality, means, deviations) from a JSON file - ARGUMENT:: filename Absolute path to the JSON file - ARGUMENT:: action A function to run when file is loaded METHOD:: write -Store the internal state of object on disk as a JSON file. Will not overwrite existing files - +Store the internal state of object on disk as a JSON file. ARGUMENT:: filename Absolute path of file to write - ARGUMENT:: action A function to run when file is written -METHOD:: cols -Retreive the dimensionality of the data set we have fitted on - -ARGUMENT:: action -A function to run when the server responds, taking the dimensions as its argument - EXAMPLES:: - code:: -(some example code) +s.boot; +//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers and a FluidStandardize +( +~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; +~raw = FluidDataSet(s,\stand_help_raw); +~stand = FluidDataSet(s,\stand_help_standd); +~audio = Buffer.read(s,~audiofile); +~pitch_feature = Buffer.new(s); +~stats = Buffer.new(s); +~datapoint = Buffer.alloc(s,2); +~standardizer = FluidStandardize(s); +) + +// Do a pitch analysis on the audio, which gives us pitch and pitch confidence (so a 2D datum) +// Divide the time series in to 10, and take the mean of each segment and add this as a point to +// the 'raw' FluidDataSet +( +~raw.clear; +~stand.clear; +FluidBufPitch.process(s,~audio,features:~pitch_feature,action:{ + "Pitch analysis.complete. Doing stats".postln; + fork{ + var chunkLen = (~pitch_feature.numFrames / 10).asInteger; + 10.do{ |i| + s.sync; FluidBufStats.process(s,~pitch_feature,startFrame:i*chunkLen,numFrames:chunkLen,stats:~stats, action:{ + ~stats.loadToFloatArray(action:{ |statsdata| + [statsdata[0],statsdata[1]].postln; + ~datapoint.setn(0,[statsdata[0],statsdata[1]]); + s.sync; + ("Adding point" ++ i).postln; + ~raw.addPoint(i,~datapoint); + }) + }); + if(i == 9) {"Analysis done, dataset ready".postln} + } + } +}); +) + +//Fit the FluidStandardizer to the raw data, and then apply the scaling out of place into +//our second FluidDataSet, so we can compare. +//Download the dataset contents into arrays for plotting +( +~standardizer.fit(~raw); +~standardizer.transform(~raw,~stand); +~rawarray = Array.new(10); +~stdarray= Array.new(10); +fork{ + 10.do{|i| + ~raw.getPoint(i,~datapoint,{ + ~datapoint.loadToFloatArray(action:{|a| ~rawarray.add(Array.newFrom(a))}) + }); + s.sync; + ~stand.getPoint(i,~datapoint,{ + + ~datapoint.loadToFloatArray(action:{|a| ~stdarray.add(Array.newFrom(a))}) + }); + s.sync; + if(i==9){"Data downloaded".postln}; + } +} +) +//Plot side by side. Before standardization the two dimensions have radically different scales +//which can be unhelpful in many cases. Now they are zero-centered, and comparable +( +~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; +) :: \ No newline at end of file From b58360d4d41665f46d0145a0d96af6d960bde137 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 12:09:18 +0100 Subject: [PATCH 112/550] FluidPCA and FluidMDS updates and help files --- release-packaging/Classes/FluidMDS.sc | 18 +- release-packaging/Classes/FluidPCA.sc | 17 +- .../HelpSource/Classes/FluidMDS.schelp | 139 +++++++++++++++ .../HelpSource/Classes/FluidPCA.schelp | 163 ++++++++++++++++++ 4 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 release-packaging/HelpSource/Classes/FluidMDS.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidPCA.schelp diff --git a/release-packaging/Classes/FluidMDS.sc b/release-packaging/Classes/FluidMDS.sc index 9912b61..57139d3 100644 --- a/release-packaging/Classes/FluidMDS.sc +++ b/release-packaging/Classes/FluidMDS.sc @@ -1,17 +1,23 @@ FluidMDS : FluidManipulationClient { - var id; + classvar < manhattan = 0; + classvar < euclidean = 1; + classvar < sqeuclidean = 2; + classvar < max = 3; + classvar < min = 4; + classvar < kl = 5; + classvar < cosine = 5; - *new {|server| + *new {|server| var uid = UniqueID.next; - ^super.new(server,uid).init(uid); + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} } init {|uid| id = uid; } - fitTransform{|sourceDataset, k, dist, destDataset, action| - this.pr_sendMsg(\fitTransform,[sourceDataset.asString, k, dist, destDataset.asString],action); - } + fitTransform{|sourceDataset, destDataset, k, dist, action| + this.prSendMsg(\fitTransform,[sourceDataset.asSymbol, destDataset.asSymbol, k, dist],action); + } } diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index ef7cf7a..29c4be8 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -11,15 +11,15 @@ FluidPCA : FluidManipulationClient { } fit{|dataset, k, action| - this.prSendMsg(\fit,[dataset.asString, k],action); + this.prSendMsg(\fit,[dataset.asSymbol, k],action); } transform{|sourceDataset, destDataset, action| - this.prSendMsg(\transform,[sourceDataset.asString, destDataset.asString],action); + this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); } - fitTransform{|sourceDataset, k, destDataset, action| - this.prSendMsg(\fitTransform,[sourceDataset.asString,k, destDataset.asString],action); + fitTransform{|sourceDataset, destDataset, k, action| + this.prSendMsg(\fitTransform,[sourceDataset.asSymbol, destDataset.asSymbol, k],action); } @@ -28,11 +28,10 @@ FluidPCA : FluidManipulationClient { } cols {|action| - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - rows {|action| - this.prSendMsg(\rows,[],action,[numbers(FluidMessageResponse,_,1,_)]); + action ?? {action = postit}; + + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); } read{|filename,action| @@ -43,4 +42,4 @@ FluidPCA : FluidManipulationClient { this.prSendMsg(\write,[filename],action); } -} +} diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp new file mode 100644 index 0000000..d69d282 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -0,0 +1,139 @@ +TITLE:: FluidMDS +summary:: Dimensionality Reduction with Multidimensional Scaling +categories:: Dimensionality Reduction, Data Processing +related:: Classes/FluidMDS, Classes/FluidDataSet + +DESCRIPTION:: + +https://scikit-learn.org/stable/modules/manifold.html#multi-dimensional-scaling-mds + + +CLASSMETHODS:: + + +METHOD:: new +Make a new instance +ARGUMENT:: server +The server on which to run this model + +METHOD:: euclidean +Euclidean distance (default) + +METHOD:: sqeuclidean +Squared Euclidean distance + +METHOD:: manhattan +Manhattan distance + +METHOD:: max +Minowski max + +METHOD:: min +Minowski max + +METHOD:: kl +Symmetric Kulback Leiber divergance (only makes sense with non-negative data) + +METHOD:: cosine +Cosine distance + +INSTANCEMETHODS:: + +PRIVATE:: init + +METHOD:: fitTransform +Fit the model to a link::Classes/FluidDataSet:: and write the new projected data to a destination FluidDataSet. +ARGUMENT:: sourceDataset +Source data, or the dataset name +ARGUMENT:: destDataset +Destination data, or the dataset name +ARGUMENT:: k +The number of dimensions to reduce to +ARGUMENT:: dist +The distance metric to use (integer, 0-6, see flags above) +ARGUMENT:: action +Run when done + +EXAMPLES:: + +code:: +//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidMDS +( +~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; +~raw = FluidDataSet(s,\mds_help_12D); +~reduced = FluidDataSet(s,\mds_help_2D); +~audio = Buffer.read(s,~audiofile); +~mfcc_feature = Buffer.new(s); +~stats = Buffer.new(s); +~datapoint = Buffer.alloc(s,12); +~standardizer = FluidStandardize(s); +~mds = FluidMDS(s); +) + +// Do a mfcc analysis on the audio, which gives us 13 points, and we'll throw the 0th away +// Divide the time series in to 100, and take the mean of each segment and add this as a point to +// the 'raw' FluidDataSet +( +~raw.clear; +~norm.clear; +FluidBufMFCC.process(s,~audio,features:~mfcc_feature,action:{ + "MFCC analysis.complete. Doing stats".postln; + fork{ + var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; + 100.do{ |i| + s.sync; FluidBufStats.process(s,~mfcc_feature,startFrame:i*chunkLen,numFrames:chunkLen,startChan:1, stats:~stats, action:{ + ~stats.loadToFloatArray(action:{ |statsdata| + [statsdata[0],statsdata[1]].postln; + ~datapoint.setn(0,[statsdata[0],statsdata[1]]); + s.sync; + ("Adding point" ++ i).postln; + ~raw.addPoint(i,~datapoint); + }) + }); + if(i == 99) {"Analysis done, dataset ready".postln} + } + } +}); +) + +//First standardize our dataset, so that the MFCC dimensions are on comensurate scales +//Then apply the MDS in-place on the standardized data to get 2 dimensions, using a Euclidean distance metric +//Download the dataset contents into an array for plotting +( +~standardizer.fit(~raw); +~standardizer.transform(~raw, ~reduced); +~mds.fitTransform(~raw,~reduced,2, FluidMDS.euclidean); +~reducedarray= Array.new(100); +fork{ + 100.do{|i| + ~reduced.getPoint(i,~datapoint,{ + + ~datapoint.loadToFloatArray(action:{|a| ~reducedarray.add(Array.newFrom(a))}) + }); + s.sync; + if(i==99){"Data downloaded".postln}; + } +} +) + +//Visualise the 2D projection of our original 12D data +( +d = ~reducedarray.flatten(1).unlace.deepCollect(1, { |x| x.normalize}); +// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; +w = Window("scatter", Rect(128, 64, 200, 200)); +w.drawFunc = { + Pen.use { + d[0].size.do{|i| + var x = (d[0][i]*200); + var y = (d[1][i]*200); + var r = Rect(x,y,5,5); + Pen.fillColor = Color.blue; + Pen.fillOval(r); + } + } +}; +w.refresh; +w.front; +) + +:: diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp new file mode 100644 index 0000000..d21fe7f --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -0,0 +1,163 @@ +TITLE:: FluidPCA +summary:: Dimensionality Reduction with Principal Component Analysis +categories:: Dimensionality Reduction, Data Processing +related:: Classes/FluidMDS, Classes/FluidDataSet + +DESCRIPTION:: + +https://scikit-learn.org/stable/modules/decomposition.html#principal-component-analysis-pca + +CLASSMETHODS:: + +METHOD:: new +Make a new instance +ARGUMENT:: server +The server on which to run this model + +INSTANCEMETHODS:: + +PRIVATE:: init + +METHOD:: fit +Train this model on a link::Classes/FluidDataSet:: but don't transform the data +ARGUMENT:: dataset +A link::Classes/FluidDataSet:: to analyse +ARGUMENT:: k +The number of dimensions to reduce to +ARGUMENT:: action +Run when done + +METHOD:: transform +Given a trained model, apply the reduction to a source link::Classes/FluidDataSet:: and write to a destination. Can be the same +ARGUMENT:: sourceDataset +Source data, or the dataset name +ARGUMENT:: destDataset +Destination data, or the dataset name +ARGUMENT:: action +Run when done + +METHOD:: fitTransform +link::Classes/FluidPCA#fit:: and link::Classes/FluidPCA#transform:: in a single pass +ARGUMENT:: sourceDataset +Source data, or the dataset name +ARGUMENT:: destDataset +Destination data, or the dataset name +ARGUMENT:: k +The number of dimensions to reduce to +ARGUMENT:: action +Run when done + +METHOD:: transformPoint +Given a trained model, transform the data point in a link::Classes/Buffer:: and write to an output +ARGUMENT:: sourceBuffer +Input data +ARGUMENT:: destBuffer +Output data +ARGUMENT:: action +Run when done + +METHOD:: cols +Return the dimensionaliy of the data the model was trained on +ARGUMENT:: action +Run when done, taking the number of columns as an argument. If nil, defaults to posting to window + +METHOD:: read +Read a data set from a JSON file on disk +ARGUMENT:: filename +The absolute path of the JSON file to read +ARGUMENT:: action +A function to run when the file has been read +​ +METHOD:: write +Write the data set to disk as a JSON file. +ARGUMENT:: filename +Absolute path for the new file +ARGUMENT:: action +A function to run when the file has been written + +EXAMPLES:: + +code:: + +s.boot; +//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidPCA +( +~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; +~raw = FluidDataSet(s,\pca_help_12D); +~reduced = FluidDataSet(s,\pca_help_2D); +~audio = Buffer.read(s,~audiofile); +~mfcc_feature = Buffer.new(s); +~stats = Buffer.new(s); +~datapoint = Buffer.alloc(s,12); +~standardizer = FluidStandardize(s); +~pca = FluidPCA(s); +) + +// Do a mfcc analysis on the audio, which gives us 13 points, and we'll throw the 0th away +// Divide the time series in to 100, and take the mean of each segment and add this as a point to +// the 'raw' FluidDataSet +( +~raw.clear; +~norm.clear; +FluidBufMFCC.process(s,~audio,features:~mfcc_feature,action:{ + "MFCC analysis.complete. Doing stats".postln; + fork{ + var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; + 100.do{ |i| + s.sync; FluidBufStats.process(s,~mfcc_feature,startFrame:i*chunkLen,numFrames:chunkLen,startChan:1, stats:~stats, action:{ + ~stats.loadToFloatArray(action:{ |statsdata| + [statsdata[0],statsdata[1]].postln; + ~datapoint.setn(0,[statsdata[0],statsdata[1]]); + s.sync; + ("Adding point" ++ i).postln; + ~raw.addPoint(i,~datapoint); + }) + }); + if(i == 99) {"Analysis done, dataset ready".postln} + } + } +}); +) + +//First standardize our dataset, so that the MFCC dimensions are on comensurate scales +//Then apply the PCA in-place on the standardized data +//Download the dataset contents into an array for plotting +( +~standardizer.fit(~raw); +~standardizer.transform(~raw, ~reduced); +~pca.fitTransform(~raw,~reduced,2); +~reducedarray= Array.new(100); +fork{ + 100.do{|i| + ~reduced.getPoint(i,~datapoint,{ + + ~datapoint.loadToFloatArray(action:{|a| ~reducedarray.add(Array.newFrom(a))}) + }); + s.sync; + if(i==99){"Data downloaded".postln}; + } +} +) + + +//Visualise the 2D projection of our original 12D data +( +d = ~reducedarray.flatten(1).unlace.deepCollect(1, { |x| x.normalize}); +// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; +w = Window("scatter", Rect(128, 64, 200, 200)); +w.drawFunc = { + Pen.use { + d[0].size.do{|i| + var x = (d[0][i]*200); + var y = (d[1][i]*200); + var r = Rect(x,y,5,5); + Pen.fillColor = Color.blue; + Pen.fillOval(r); + } + } +}; +w.refresh; +w.front; +) + +:: From 9d139ba7c83bde96ed28d8fc675a3d545a28626a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 16:17:23 +0100 Subject: [PATCH 113/550] Make dataset help work --- .../HelpSource/Classes/FluidDataSet.schelp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 0212061..418158e 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -125,13 +125,18 @@ returns:: A link::Classes/Server:: EXAMPLES:: CODE:: -( // Make a one-dimensional data set called 'simple1data' -~ds = FluidDataSet.new(s,\simple1data,1) +( +~ds = FluidDataSet.new(s,\simple1data,1); +) + +( +Routine{ +~ds.clear; // Make a buffer to use for adding points -~point = Buffer.alloc(s,1,1) +~point = Buffer.alloc(s,1,1); //Add 10 points, using the index as a label. -Routine{ + s.sync; 10.do{|i| ~point.set(0,i); ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); From ae86d967a7e645abaf7962454469cf59a099e4f1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 17:42:56 +0100 Subject: [PATCH 114/550] Fixed classes and new help for KNNClassifier/Regressor --- .../Classes/FluidKNNClassifier.sc | 27 ++- .../Classes/FluidKNNRegressor.sc | 27 ++- .../Classes/FluidKNNClassifier.schelp | 161 ++++++++++++++++++ .../Classes/FluidKNNRegressor.schelp | 108 ++++++++++++ 4 files changed, 305 insertions(+), 18 deletions(-) create mode 100644 release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp create mode 100644 release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index ef6a7b6..5f1fbac 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -1,18 +1,27 @@ FluidKNNClassifier : FluidManipulationClient { + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } + + init {|uid| + id = uid; + } + fit{|dataset, labelset, action| - this.pr_sendMsg(\fit,[dataset.asString, labelset.asString], action); + this.prSendMsg(\fit,[dataset.asSymbol, labelset.asSymbol], action); } - predict{ |dataset, labelset, k, action| - this.pr_sendMsg(\predict, - [dataset.asString, labelset.asString, k], - action, [string(FluidMessageResponse,_,_)] - ); + predict{ |dataset, labelset, k, uniform = 0, action| + this.prSendMsg(\predict, + [dataset.asSymbol, labelset.asSymbol, k, uniform], + action); } - predictPoint { |buffer, k, action| - this.pr_sendMsg(\predictPoint, - [buffer.asUGenInput, k], action, + predictPoint { |buffer, k, uniform = 0, action| + this.prSendMsg(\predictPoint, + [buffer.asUGenInput, k,uniform], action, [number(FluidMessageResponse,_,_)] ); } diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index d3716b0..74c0023 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -1,20 +1,29 @@ FluidKNNRegressor : FluidManipulationClient { + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } + + init {|uid| + id = uid; + } + fit{|sourceDataset, targetDataset, action| - this.pr_sendMsg(\fit, - [sourceDataset.asString, targetDataset.asString], + this.prSendMsg(\fit, + [sourceDataset.asSymbol, targetDataset.asSymbol], action ); } - predict{ |sourceDataset, targetDataset, k, action| - this.pr_sendMsg(\predict, - [sourceDataset.asString, targetDataset.asString, k], - action, - [string(FluidMessageResponse,_,_)]); + predict{ |sourceDataset, targetDataset, k, uniform = 0, action| + this.prSendMsg(\predict, + [sourceDataset.asSymbol, targetDataset.asSymbol, k, uniform], + action); } - predictPoint { |buffer, k, action| - this.pr_sendMsg(\predictPoint, [buffer.asUGenInput, k], action, + predictPoint { |buffer, k, uniform = 0, action| + this.prSendMsg(\predictPoint, [buffer.asUGenInput, k,uniform], action, [number(FluidMessageResponse,_,_)]); } } diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp new file mode 100644 index 0000000..eac0ecb --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -0,0 +1,161 @@ +TITLE:: FluidKNNClassifier +summary:: Classify data with K Nearest Neighbours +categories:: Classification, KNN +related:: Classes/FluidKNNRegressor, Classes/FluidDataSet, Classes/FluidLabelSet + +DESCRIPTION:: + +CLASSMETHODS:: + +METHOD:: new +Create a new KNNClassifier +ARGUMENT:: server +The server to make the model on + +INSTANCEMETHODS:: + +METHOD:: fit +Fit the model to a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet:: . These need to be the sime size +ARGUMENT:: dataset +Source data +ARGUMENT:: labelset +Labels for the source data +ARGUMENT:: action +Run when done + +METHOD:: predict +Given a fitted model, predict labels for a link::Classes/FluidDataSet:: and write these to a link::Classes/FluidLabelSet:: +ARGUMENT:: dataset +data to predict labels for +ARGUMENT:: labelset +place to write labels +ARGUMENT:: k +the number of neighours to consider +ARGUMENT:: uniform +true / false: whether the neighbours shold be weighted by distance +ARGUMENT:: action +Run when done + +METHOD:: predictPoint +Given a fitted model, predict labels for a data point in a link::Classes/Buffer:: and return these to the caller +ARGUMENT:: buffer +A data point +ARGUMENT:: k +Number of neighbours to consider +ARGUMENT:: uniform +true / false: whether the neighbours shold be weighted by distance +ARGUMENT:: action +Run when done, passes predicted label as argument + +EXAMPLES:: + +code:: +//A dataset of example points, and a label set of corresponding labels +//+ +//A dataset of test data and a labelset for predicted labels +( +~source= FluidDataSet(s,\knnclassify_help_examples); +~labels = FluidLabelSet(s,\knnclassify_help_labels); +~test = FluidDataSet(s,\knnclassify_help_test); +~mapping = FluidLabelSet(s,\knnclassify_help_mapping); +) + +//Make some clumped 2D points and place into a dataset +( +~examplepoints = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]]; +~examplelabels = [\red,\orange,\green,\blue]; +~source.clear; +~labels.clear; +~tmpbuf = Buffer.alloc(s,2); +fork{ + s.sync; + ~examplepoints.do{|x,i| + (""++(i+1)++"/4").postln; + ~tmpbuf.setn(0,x); + ~source.addPoint(i,~tmpbuf); + ~labels.addLabel(i,~examplelabels[i]); + s.sync + } +} +) + +//Make some random, but clustered test points +( +~testpoints = (4.collect{64.collect{(1.sum3rand) + [1,-1].choose}.clump(2)}).flatten(1) * 0.5; +~test.clear; +fork { + s.sync; + ~testpoints.do{|x,i| + ~tmpbuf.setn(0,x); + ~test.addPoint(i,~tmpbuf); + s.sync; + if(i==(~testpoints.size - 1)){"Generated test data".postln;} + } +} +) + +//Make a new KNN classifier model, fit it to the example dataset and labels, and then run preduction on the test data into our mapping label set +( +fork{ + ~classifier = FluidKNNClassifier(s); + s.sync; + ~classifier.fit(~source,~labels); + ~classifier.predict(~test, ~mapping, 1); + s.sync; +} +) + +//Dims of kmeans should match dataset +~kmeans.cols + +//Return labels of clustered points +( +~assignments = Array.new(~testpoints.size); +fork{ + ~testpoints.do{|x,i| + ~mapping.getLabel(i,action:{|l| + ~assignments.add(l); + }); + s.sync; + if(i==(~testpoints.size - 1)){"Got assignments".postln;} + }; + ~assignments.postln; +} +) + +//Visualise: we're hoping to see colours neatly mapped to quandrants... +( +c = IdentityDictionary(); + +c.add(\red->Color.red); +c.add(\blue->Color.blue); +c.add(\green->Color.green); +c.add(\orange-> Color.new255(255, 127, 0)); + +e = 200 * ((~examplepoints + 1) * 0.5).flatten(1).unlace; +d = ((~testpoints + 1) * 0.5).flatten(1).unlace; +// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; +w = Window("scatter", Rect(128, 64, 200, 200)); +~colours = [Color.blue,Color.red,Color.green,Color.magenta]; +w.drawFunc = { + Pen.use { + e[0].size.do{|i| + var r = Rect(e[0][i],e[1][i],10,10); + Pen.fillColor = c[~examplelabels[i]]; + Pen.fillOval(r); + }; + d[0].size.do{|i| + var x = (d[0][i]*200); + var y = (d[1][i]*200); + var r = Rect(x,y,5,5); + Pen.fillColor = c[~assignments[i].asSymbol].alpha_(0.3); + Pen.fillOval(r); + } + } +}; +w.refresh; +w.front; +) + + +:: diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp new file mode 100644 index 0000000..7eb7625 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -0,0 +1,108 @@ +TITLE:: FluidKNNRegressor +summary:: Regression with K Nearest Neighbours +categories:: Regression +related:: Classes/FluidKNNClassifier, Classes/FluidDataSet + +DESCRIPTION:: + +CLASSMETHODS:: + +METHOD:: new +Create a new KNN regressor on the server +ARGUMENT:: server +The server to run this model on. + +INSTANCEMETHODS:: + +METHOD:: fit +Map a source link::Classes/FluidDataSet:: to a target; they must be the same size, but can have different dimesionality +ARGUMENT:: sourceDataset +Source data +ARGUMENT:: targetDataset +Target data +ARGUMENT:: action +Run when done + + +METHOD:: predict +Apply learned mapping to a link::Classes/FluidDataSet:: and write to an output dataset +ARGUMENT:: sourceDataset +data to regress +ARGUMENT:: targetDataset +output data +ARGUMENT:: k +number of neigbours to consider in mapping, min 1 +ARGUMENT:: uniform +Whether to weight neighbours by distance when producing new point +ARGUMENT:: action +Run when done + +METHOD:: predictPoint +Apply learned mapping to a data point in a link::Classes/Buffer:: +ARGUMENT:: buffer +data point +ARGUMENT:: k +number of neigbours to consider in mapping, min 1 +ARGUMENT:: uniform +Whether to weight neighbours by distance when producing new point +ARGUMENT:: action +Run when done + +EXAMPLES:: + +code:: + +//Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp +( +~source = FluidDataSet(s,\knn_regress_src); +~target = FluidDataSet(s,\knn_regress_tgt); +~test = FluidDataSet(s,\knn_regress_test); +~output = FluidDataSet(s,\knn_regress_out); +~tmpbuf = Buffer.alloc(s,1); +) + +//Make source, target and test data +( +~sourcedata = 128.collect{|i|i/128}; +~targetdata = 128.collect{|i| sin(2*pi*i/128) }; +fork{ + 128.do{ |i| + ((i + 1).asString ++ "/128").postln; + ~tmpbuf.setn(0,i/128); + ~source.addPoint(i,~tmpbuf); + s.sync; + ~tmpbuf.setn(0,sin(2*pi*i/128)); + ~target.addPoint(i,~tmpbuf); + s.sync; + ~tmpbuf.setn(0,(i/128)**2); + ~test.addPoint(i,~tmpbuf); + s.sync; + if(i==127){"Source, target and test generated".postln}; + } +} +) + +// 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 +( +~outputdata = Array(128); +fork{ + ~regressor = FluidKNNRegressor(s); + s.sync; + ~regressor.fit(~source,~target); + ~regressor.predict(~test,~output,1); + s.sync; + 128.do{|i| + ~output.getPoint(i,~tmpbuf,{ + ~tmpbuf.loadToFloatArray(action:{|x| + ~outputdata.addAll(x) + }) + }); + s.sync; + if(i==127){"Model fitted, output generated".postln}; + } +} +) +//We should see a single cycle of a chirp +~outputdata.plot; +:: \ No newline at end of file From bb5efd3c67fdc19036e18b8ca9217c3859eac7ac Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 18:00:42 +0100 Subject: [PATCH 115/550] Add dump and print messages to dataset --- release-packaging/Classes/FluidDataSet.sc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 06499ea..b0253a5 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -85,6 +85,16 @@ FluidDataSet : FluidManipulationClient { this.prSendMsg(\clear,[],action); } + print { |action| + action ?? {action = postit}; + this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]); + } + + dump { |action| + action ?? {action = postit}; + this.prSendMsg(\dump,[],action,[string(FluidMessageResponse,_,_)]); + } + free { serverCaches.remove(server,id); super.free; From 148ca0ac10e3e5cc641b1c57da449b22cd8622eb Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 20 May 2020 19:49:01 +0100 Subject: [PATCH 116/550] add FluidDataSetQuery --- .../Classes/FluidDataSetQuery.sc | 48 +++++++++++++++++++ src/FluidManipulation/FluidManipulation.cpp | 2 + 2 files changed, 50 insertions(+) create mode 100644 release-packaging/Classes/FluidDataSetQuery.sc diff --git a/release-packaging/Classes/FluidDataSetQuery.sc b/release-packaging/Classes/FluidDataSetQuery.sc new file mode 100644 index 0000000..ba3d656 --- /dev/null +++ b/release-packaging/Classes/FluidDataSetQuery.sc @@ -0,0 +1,48 @@ + +FluidDataSetQuery : FluidManipulationClient { + + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } + + init {|uid| + id = uid; + } + + addColumn{|column, action| + this.prSendMsg(\addColumn, [column], action); + } + + + addRange{|start, count, action| + this.prSendMsg(\addRange, [start, count], action); + } + + + filter{|column, condition, value, action| + this.prSendMsg(\filter, [column, condition.asSymbol, value], action); + } + + + and{|column, condition, value, action| + this.prSendMsg(\and, [column, condition, value], action); + } + + or{|column, condition, value, action| + this.prSendMsg(\or, [column, condition, value], action); + } + + reset{|action| + this.prSendMsg(\reset, [], action); + } + + limit{|rows, action| + this.prSendMsg(\limit, [rows], action); + } + + transform{|sourceDataset, destDataset, action| + this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); + } + +} diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index ebd4ee2..15f3448 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -2,6 +2,7 @@ // 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 +#include #include #include #include @@ -21,6 +22,7 @@ PluginLoad(FluidSTFTUGen) ft = inTable; using namespace fluid::client; makeSCWrapper("FluidDataSet",ft); + makeSCWrapper("FluidDataSetQuery",ft); makeSCWrapper("FluidLabelSet",ft); makeSCWrapper("FluidKDTree",ft); makeSCWrapper("FluidKMeans",ft); From 1d8652df93d6a308783b47edb74b4e2ad4d96a34 Mon Sep 17 00:00:00 2001 From: Gerard Date: Wed, 20 May 2020 20:42:11 +0100 Subject: [PATCH 117/550] Add FluidDataSetQuery help --- .../Classes/FluidDataSetQuery.schelp | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp diff --git a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp new file mode 100644 index 0000000..07e82fe --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp @@ -0,0 +1,140 @@ +TITLE:: FluidDataSetQuery +summary:: Query a FluidDataSet +categories:: UGens>FluidManipulation +related:: Classes/FluidDataSet + +DESCRIPTION:: +A selection of columns and a set of conditions that match rows of a FluidDataSet. +Use to filter and search in a database of descriptors. + +CLASSMETHODS:: + +METHOD:: new +Make a new instance +ARGUMENT:: server +The server on which to run this object + +INSTANCEMETHODS:: + +PRIVATE:: init + +METHOD:: addColumn +Add a column to the query +ARGUMENT:: column +Column index +ARGUMENT:: action +Run when done + +METHOD:: addRange +Add a range of columns to the query +ARGUMENT:: start +First index +ARGUMENT:: count +Number of columns +ARGUMENT:: action +Run when done + +METHOD:: filter +Filter rows according to some condition. + +Example: (3, ">", 0.5) filters rows where the value of the 4th column (starting at 0) is larger than 0.5. +ARGUMENT:: column +Column index +ARGUMENT:: condition +Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" +ARGUMENT:: value +Condition value +ARGUMENT:: action +Run when done + +METHOD:: and +Add a condition to an existing filter with an "and" connector. +ARGUMENT:: column +Column index +ARGUMENT:: condition +Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" +ARGUMENT:: value +Condition value +ARGUMENT:: action +Run when done + + +METHOD:: or +Add a condition to an existing filter with an "or" connector. +ARGUMENT:: column +Column index +ARGUMENT:: condition +Condition string. Possible values: "==", "!=", "<", "<=", ">", ">=" +ARGUMENT:: value +Condition value +ARGUMENT:: action +Run when done + +METHOD:: limit +Limit the number of resulting rows. +ARGUMENT:: rows +Maximum number of rows +ARGUMENT:: action +Run when done + +METHOD:: reset +Clear the query, remove all columns, filters and limit. +ARGUMENT:: action +Run when done + +METHOD:: transform +Apply the query to a source link::Classes/FluidDataSet:: and write to a destination. Can be the same. +ARGUMENT:: sourceDataset +Source data, or the dataset name +ARGUMENT:: destDataset +Destination data, or the dataset name +ARGUMENT:: action +Run when done + + +EXAMPLES:: + +code:: + +s.boot; + + +// Create a dataset with random data +~dataset= FluidDataSet(s,\help_fluid_dataset_query); + +( +~points = 100.collect{24.collect{100.rand}}; +~dataset.clear; +~tmpbuf = Buffer.alloc(s,24); + +fork{ + s.sync; + ~points.do{|x,i| + ~tmpbuf.setn(0,x); + ~dataset.addPoint(i,~tmpbuf); + s.sync + } +} +) + +// Prepare a FluidDataSetQuery object +~query = FluidDataSetQuery.new; +~query.reset; +~query.addColumn(5); +~query.addRange(0,2); + +~query.filter(0,"<",50); +~query.and(1,"<",50); +~query.and(2,"<",50); +~query.limit(10); + +// Create an output dataset and run the query +~out = FluidDataSet(s,\help_fluid_dataset_query_out); +~query.transform(~dataset, ~out); + +// Check the results +~dataset.print; +~out.print; + + +:: From 2ef9d586358f8d42a23a6f87d6ceac6f57741e5a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 20 May 2020 20:44:10 +0100 Subject: [PATCH 118/550] Add dump in the dataset help file, and I really am on leave, honest --- .../HelpSource/Classes/FluidDataSet.schelp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 418158e..4a3733d 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -144,4 +144,16 @@ Routine{ } }.play ) + +//Inspect the dataset using print (abbreviated output) or dump (JSON output) + +~ds.print //to post window by default, but you can supply a custom action instead +~ds.dump //likewise +//for example +~ds.dump{|j| + ~dict = j.parseJSON +} +//Now we have a Dictionary of our data and IDs +~dict.postcs + :: From f27989dd84b72bb8bfffcdbc322455505e50b4e5 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 20 May 2020 22:39:02 +0100 Subject: [PATCH 119/550] typo found --- release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp index 7eb7625..e5d576c 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -15,7 +15,7 @@ The server to run this model on. INSTANCEMETHODS:: METHOD:: fit -Map a source link::Classes/FluidDataSet:: to a target; they must be the same size, but can have different dimesionality +Map a source link::Classes/FluidDataSet:: to a target; they must be the same size, but can have different dimensionality ARGUMENT:: sourceDataset Source data ARGUMENT:: targetDataset From af9ac0332a8824c5a0acc15199e95662c304b666 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 20 May 2020 23:05:02 +0100 Subject: [PATCH 120/550] commenting the buffer input to FluidKMeans to avoid errors. --- release-packaging/Classes/FluidKMeans.sc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 22b95f7..daf8d61 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -14,8 +14,9 @@ FluidKMeans : FluidManipulationClient { fit{|dataset,k, maxIter = 100, buffer, action| buffer = buffer ? -1; this.k = k; - this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); - } + // this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); + this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); + } fitPredict{|dataset,labelset, k, maxIter = 100, action| this.k = k; From 82f3efe98df6ccda647ddc686164c7af92f16252 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 20 May 2020 23:05:24 +0100 Subject: [PATCH 121/550] updating the Example folder - part 1 --- release-packaging/Examples/dataset/myfirstdataset.scd | 6 +++--- .../Examples/dataset/super-simple-1D-example.scd | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/release-packaging/Examples/dataset/myfirstdataset.scd b/release-packaging/Examples/dataset/myfirstdataset.scd index 508fd85..0c821de 100644 --- a/release-packaging/Examples/dataset/myfirstdataset.scd +++ b/release-packaging/Examples/dataset/myfirstdataset.scd @@ -22,14 +22,14 @@ FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]; ) //STEP 2: Make a FluidDataSet -~dataset = FluidDataSet.new(s,"mfccs", 96) //12 dims * 4 stats * 2 derivatives +~dataset = FluidDataSet.new(s,"mfccs"); //STEP 3A: EITHER populate the dataset like so (and cry about how long the data point assembly takes) ( Routine{ var tmpMFCCs = Buffer.new(s); var tmpStats = Buffer.new(s); - var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); + var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1);//12 dims * 4 stats * 2 derivatives s.sync; ~audioBuffers.do{|b| ("Analyzing" + b.path).postln; @@ -61,7 +61,7 @@ Routine{ var tmpStats = Buffer.new(s); var langStats; var langFlat; - var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); + var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); //12 dims * 4 stats * 2 derivatives s.sync; ~audioBuffers.do{|b| ("Analyzing" + b.path).postln; diff --git a/release-packaging/Examples/dataset/super-simple-1D-example.scd b/release-packaging/Examples/dataset/super-simple-1D-example.scd index 0e6a240..43fa2f9 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example.scd @@ -1,5 +1,5 @@ s.reboot -~ds = FluidDataSet.new(s,\simple1data,1) +~ds = FluidDataSet.new(s,\simple1data) ~point = Buffer.alloc(s,1,1) ( Routine{ @@ -50,8 +50,9 @@ Routine{ ( Routine{ var n; - ~labels.size({|x| n = x.asInt}); - n.asInt.do{|i| + ~labels.size({|x| n = x.asInteger;}); + n.postln; + n.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); } }.play From e89df42917878ab8df5aa1f5498f37da32d2ba09 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 20 May 2020 23:15:45 +0100 Subject: [PATCH 122/550] updating examples --- .../Examples/dataset/super-simple-1D-example.scd | 13 +++++-------- .../dataset/super-simple-1D-example2.scd | 16 ++++++++-------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/release-packaging/Examples/dataset/super-simple-1D-example.scd b/release-packaging/Examples/dataset/super-simple-1D-example.scd index 43fa2f9..3e8d2b6 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example.scd @@ -30,8 +30,8 @@ Routine{ /*** KMEANS ***/ ~kmeans = FluidKMeans.new(s) -~nClusters = 4; //play with this -~kmeans.fit(~ds,~nClusters,100,action:{"Done fitting".postln}) +~nClusters = 2; //play with this +~kmeans.fit(~ds,~nClusters,100,action:{|x| "Done fitting with these number of items per cluster ".post;x.postln;}) ( Routine{ @@ -50,12 +50,9 @@ Routine{ ( Routine{ var n; - ~labels.size({|x| n = x.asInteger;}); - n.postln; - n.do{|i| + ~labels.size({|x| + x.asInteger.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); - } + };}); }.play ) - - diff --git a/release-packaging/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/Examples/dataset/super-simple-1D-example2.scd index a74a305..5a22bb9 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example2.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example2.scd @@ -1,5 +1,5 @@ s.reboot -~ds = FluidDataSet.new(s,\simple1data,1) +~ds = FluidDataSet.new(s,\simple1data) ~point = Buffer.alloc(s,1,1) ( Routine{ @@ -34,7 +34,8 @@ Routine{ ~kmeans = FluidKMeans.new(s) ~nClusters = 2; //play with this -~kmeans.fit(~ds,~nClusters,100,action:{"Done fitting".postln}) +~kmeans.fit(~ds,~nClusters,100,action:{|x| "Done fitting with these number of items per cluster ".post;x.postln;}) + ( Routine{ 10.do{|i| @@ -48,14 +49,13 @@ Routine{ ~labels = FluidLabelSet(s,\simple1label); ~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) -( + +s( Routine{ var n; - ~labels.size({|x| n = x.asInt}); - n.asInt.do{|i| + ~labels.size({|x| + x.asInteger.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); - } + };}); }.play ) - - From 95f3f7411b697b9ed21279bd24b03c777ca265b1 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Wed, 20 May 2020 23:30:28 +0100 Subject: [PATCH 123/550] corrected help typo and updated the last examples --- .../dataset/super-simple-classifier-example.scd | 6 +++--- ...-simple-normalization-standardization-example.scd | 12 ++++++------ .../HelpSource/Classes/FluidKNNClassifier.schelp | 3 --- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/release-packaging/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/Examples/dataset/super-simple-classifier-example.scd index 8c58f29..855035f 100644 --- a/release-packaging/Examples/dataset/super-simple-classifier-example.scd +++ b/release-packaging/Examples/dataset/super-simple-classifier-example.scd @@ -1,6 +1,6 @@ ( -~simpleInput = FluidDataSet(s,\simpleInput,2); -~simpleOutput = FluidLabelSet(s,\simpleOutput,2); +~simpleInput = FluidDataSet(s,\simpleInput); +~simpleOutput = FluidLabelSet(s,\simpleOutput); b = Buffer.alloc(s,2); ~knn = FluidKNNClassifier(s); k = 3 @@ -23,7 +23,7 @@ v.mouseDownAction = {|view, x, y|myx=x;myy=y;w.refresh; Routine{ b.setn(0,[myx,myy]); s.sync; - ~knn.predictPoint(b, k, {|x|x.postln;}); + ~knn.predictPoint(b, k, action: {|x|x.postln;}); }.play;}; //custom redraw function diff --git a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd index 62193a4..8604714 100644 --- a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd +++ b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -39,7 +39,7 @@ ~normed_dataset = FluidDataSet(s,\normed,~nb_of_dim); // normalize the full dataset -~normalize.normalize(~dataset,~normed_dataset,{"done".postln;}); +~normalize.transform(~dataset,~normed_dataset,{"done".postln;}); // look at a point to see that it has points in it ~normed_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); @@ -54,7 +54,7 @@ // standardize the full dataset ~standardized_dataset = FluidDataSet(s,\standardized,~nb_of_dim); -~standardize.standardize(~dataset,~standardized_dataset,{"done".postln;}); +~standardize.transform(~dataset,~standardized_dataset,{"done".postln;}); // look at a point to see that it has points in it ~standardized_dataset.getPoint("point-0",~query_buf,{~query_buf.getn(0,~nb_of_dim,{|x|x.postln;});}); @@ -79,7 +79,7 @@ // normalise that point (~query_buf) to be at the right scale ~normbuf = Buffer.alloc(s,~nb_of_dim); -~normalize.normalizePoint(~query_buf,~normbuf); +~normalize.transformPoint(~query_buf,~normbuf); ~normbuf.getn(0,~nb_of_dim,{arg vec;vec.postln;}); // make a tree of the normalized database and query with the normalize buffer @@ -91,7 +91,7 @@ // standardize that same point (~query_buf) to be at the right scale ~stdbuf = Buffer.alloc(s,~nb_of_dim); -~standardize.standardizePoint(~query_buf,~stdbuf); +~standardize.transformPoint(~query_buf,~stdbuf); ~stdbuf.getn(0,~nb_of_dim,{arg vec;vec.postln;}); // make a tree of the standardized database and query with the normalize buffer @@ -107,8 +107,8 @@ ~query_buf.fill(0,~nb_of_dim,50); // normalize and standardize the query buffer. Note that we do not need to fit since we have not added a point to our reference dataset -~normalize.normalizePoint(~query_buf,~normbuf); -~standardize.standardizePoint(~query_buf,~stdbuf); +~normalize.transformPoint(~query_buf,~normbuf); +~standardize.transformPoint(~query_buf,~stdbuf); //query the single nearest neighbourg via 3 different data scaling. Depending on the random source at the begining, you will get small to large differences between the 3 answers! ~tree.kNearest(~query_buf,1, {|x| ("Original:" + x).post;~tree.kNearestDist(~query_buf,1, {|x| (" with a distance of " + x).postln});}); diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp index eac0ecb..d76b446 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -105,9 +105,6 @@ fork{ } ) -//Dims of kmeans should match dataset -~kmeans.cols - //Return labels of clustered points ( ~assignments = Array.new(~testpoints.size); From 40cf1f46805753cba507bc9a4247b1cc84789d05 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 21 May 2020 00:12:18 +0100 Subject: [PATCH 124/550] FluidKNNClassifier: return string in predictPoint --- release-packaging/Classes/FluidKNNClassifier.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index 5f1fbac..1314812 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -22,7 +22,7 @@ FluidKNNClassifier : FluidManipulationClient { predictPoint { |buffer, k, uniform = 0, action| this.prSendMsg(\predictPoint, [buffer.asUGenInput, k,uniform], action, - [number(FluidMessageResponse,_,_)] + [string(FluidMessageResponse,_,_)] ); } } \ No newline at end of file From 95cce618c5cbed6a9cd598d8baad3d0ffe6160e9 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 21 May 2020 00:32:47 +0100 Subject: [PATCH 125/550] finished updating the basic files. --- .../Examples/dataset/super-simple-1D-example.scd | 5 +++-- .../Examples/dataset/super-simple-1D-example2.scd | 7 ++++--- release-packaging/HelpSource/Classes/FluidDataSet.schelp | 8 ++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/release-packaging/Examples/dataset/super-simple-1D-example.scd b/release-packaging/Examples/dataset/super-simple-1D-example.scd index 3e8d2b6..010477e 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example.scd @@ -36,9 +36,9 @@ Routine{ ( Routine{ 10.do{|i| - ~point.set(0,i * 10); + ~point.set(0,i); s.sync; - ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + (i * 10) ++ ":" + x).postln}) + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + i ++ ":" + x).postln}) } }.play ) @@ -53,6 +53,7 @@ Routine{ ~labels.size({|x| x.asInteger.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); + s.sync; };}); }.play ) diff --git a/release-packaging/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/Examples/dataset/super-simple-1D-example2.scd index 5a22bb9..f42cdcc 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example2.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example2.scd @@ -39,9 +39,9 @@ Routine{ ( Routine{ 10.do{|i| - ~point.set(0,i * 10); + ~point.set(0,i); s.sync; - ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + (i * 10) ++ ":" + x).postln}) + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + i ++ ":" + x).postln}) } }.play ) @@ -50,12 +50,13 @@ Routine{ ~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) -s( +( Routine{ var n; ~labels.size({|x| x.asInteger.do{|i| ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); + s.sync; };}); }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 4a3733d..ec85964 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -105,6 +105,12 @@ ARGUMENT:: filename Absolute path for the new file ARGUMENT:: action A function to run when the file has been written + +METHOD:: dump +(buggy, please use Print for now) Post the content of the dataset in JSON format in the window by default, but you can supply a custom action instead; + +METHOD:: print +Post an abbreviated content of the dataset in the window by default, but you can supply a custom action instead; ​​ METHOD:: asString ​Responds with the name of the data set as a pretty(ish) string @@ -150,9 +156,11 @@ Routine{ ~ds.print //to post window by default, but you can supply a custom action instead ~ds.dump //likewise //for example +( ~ds.dump{|j| ~dict = j.parseJSON } +) //Now we have a Dictionary of our data and IDs ~dict.postcs From d138471955e9e6a72a6df6d1be93c48d3b191e6f Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 26 May 2020 16:19:30 +0100 Subject: [PATCH 126/550] removed asInt everywhere --- release-packaging/Classes/FluidSubscriberTest.sc | 4 ++-- release-packaging/HelpSource/Classes/FluidBufPitch.schelp | 2 +- release-packaging/HelpSource/Classes/FluidBufStats.schelp | 4 ++-- release-packaging/HelpSource/Classes/FluidTransients.schelp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/release-packaging/Classes/FluidSubscriberTest.sc b/release-packaging/Classes/FluidSubscriberTest.sc index 04a799d..f6cedb0 100644 --- a/release-packaging/Classes/FluidSubscriberTest.sc +++ b/release-packaging/Classes/FluidSubscriberTest.sc @@ -17,7 +17,7 @@ FluidSubscriberTest : UGen { //so server plugin knows what's going on specialIndex = -1; inputs = [size].addAll(chars); - providerName = chars.collectAs({|x|x.asInt.asAscii}, String); + providerName = chars.collectAs({|x|x.asInteger.asAscii}, String); } providerLookup { |server, nodeID, label, action| @@ -37,4 +37,4 @@ FluidSubscriberTest : UGen { if(action.notNil){action.value(result)}{action.value}; },'/'++msg).oneShot; } -} \ No newline at end of file +} diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 3b8f530..f202e83 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -143,7 +143,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInt, 2)})}); + FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}); (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index ce83e04..6b9cf4c 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -134,10 +134,10 @@ g= Array.new; Routine({ e.doAdjacentPairs({ arg start,end; - FluidBufStats.processBlocking(s,c,(start/512).asInt,((end-start)/512).max(2).asInt,0,1,d,1, action: {d.loadToFloatArray(action: { + FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1, action: {d.loadToFloatArray(action: { arg array; g = g.add(array[12]); - "% % %\n".postf((start/512).asInt,((end-start)/512).max(2).asInt, array[12]); + "% % %\n".postf((start/512).asInteger,((end-start)/512).max(2).asInteger, array[12]); })}); }); "Done".postln; diff --git a/release-packaging/HelpSource/Classes/FluidTransients.schelp b/release-packaging/HelpSource/Classes/FluidTransients.schelp index 475f080..1fdafbc 100644 --- a/release-packaging/HelpSource/Classes/FluidTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidTransients.schelp @@ -57,7 +57,7 @@ b = Buffer.read(s,File.realpath(FluidTransients.class.filenameSymbol).dirname.wi // basic parameters {FluidTransients.ar(PlayBuf.ar(1, b, loop:1))}.play -// tweaked parameterss +// tweaked parameters {FluidTransients.ar(PlayBuf.ar(1, b, loop:1), 80, threshFwd:MouseX.kr(0,5), threshBack:MouseY.kr(0,2))}.play // null test (the process add a latency of (blockSize + padding - order) samples From 1823737c760cf176dde55bdfad19e7237bee8f61 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 26 May 2020 16:19:51 +0100 Subject: [PATCH 127/550] corrected the example patches of tb2 --- ...-normalization-standardization-example.scd | 3 +- .../super-simple-regressor-example.scd | 32 ++++++++----------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd index 8604714..9309ded 100644 --- a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd +++ b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -1,7 +1,6 @@ ( // set some variables ~nb_of_dim = 10; -~dataset.clear; ~dataset = FluidDataSet(s,\test,~nb_of_dim); ) @@ -17,6 +16,8 @@ }); ) +~dataset.print; + // make a buf for getting points back ~query_buf = Buffer.alloc(s,~nb_of_dim); diff --git a/release-packaging/Examples/dataset/super-simple-regressor-example.scd b/release-packaging/Examples/dataset/super-simple-regressor-example.scd index 4c5c7bd..c5a078f 100644 --- a/release-packaging/Examples/dataset/super-simple-regressor-example.scd +++ b/release-packaging/Examples/dataset/super-simple-regressor-example.scd @@ -9,26 +9,29 @@ n = 200 // creates the dataset with these associated indices and values ( -~simpleInput = FluidDataSet(s,\simpleInput,1); -~simpleOutput = FluidDataSet(s,\simpleOutput,1); +~simpleInput = FluidDataSet(s,\simpleInput); +~simpleOutput = FluidDataSet(s,\simpleOutput); b = Buffer.alloc(s,1,1); ~mappingviz = Buffer.alloc(s,512); ) ( Routine{ - n.do{|i| - b.set(0,~idx[i]); - s.sync; - ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); - b.set(0,~data[i]); - s.sync; - ~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln}); - ~mappingviz.set((~idx[i]/61).asInt,~data[i]) - } + n.do{|i| + b.set(0,~idx[i]); + ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); + s.sync; + b.set(0,~data[i]); + ~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln}); + s.sync; + ~mappingviz.set((~idx[i]/61).asInteger,~data[i]) + } }.play ) +~simpleInput.print +~simpleOutput.print + //look at the seeing material ~mappingviz.plot(minval:-1,maxval:1) @@ -45,7 +48,6 @@ k = 1; // change to see how many points the system uses to regress Routine{ 512.do{|i| b.set(0,i*61); - s.sync; ~knn.predictPoint(b,k,action:{|d|~mappingresult.set(i,d);}); s.sync; i.postln; @@ -55,9 +57,3 @@ Routine{ // look at the interpolated values ~mappingresult.plot - -// clears both datasets -( -~simpleInput.clear; -~simpleOutput.clear; -) From d6776226be621518372f61e27713e59696fa9254 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 6 May 2020 17:30:57 +0100 Subject: [PATCH 128/550] Add triggering to NRT processors, remove doneAction --- include/FluidSCWrapper.hpp | 47 +++++++++---------- release-packaging/Classes/FluidBufAmpGate.sc | 8 ++-- release-packaging/Classes/FluidBufAmpSlice.sc | 8 ++-- .../Classes/FluidBufAudioTransport.sc | 6 +-- release-packaging/Classes/FluidBufCompose.sc | 8 ++-- release-packaging/Classes/FluidBufHPSS.sc | 8 ++-- release-packaging/Classes/FluidBufLoudness.sc | 8 ++-- release-packaging/Classes/FluidBufMFCC.sc | 8 ++-- release-packaging/Classes/FluidBufMelBands.sc | 8 ++-- release-packaging/Classes/FluidBufNMF.sc | 8 ++-- release-packaging/Classes/FluidBufNMFCross.sc | 8 ++-- .../Classes/FluidBufNoveltySlice.sc | 8 ++-- .../Classes/FluidBufOnsetSlice.sc | 8 ++-- release-packaging/Classes/FluidBufPitch.sc | 8 ++-- release-packaging/Classes/FluidBufSines.sc | 8 ++-- .../Classes/FluidBufSpectralShape.sc | 8 ++-- release-packaging/Classes/FluidBufStats.sc | 8 ++-- .../Classes/FluidBufThreadDemo.sc | 8 ++-- .../Classes/FluidBufTransientSlice.sc | 8 ++-- .../Classes/FluidBufTransients.sc | 8 ++-- .../Classes/FluidManipulationClient.sc | 1 - release-packaging/Classes/FluidMessageTest.sc | 4 +- release-packaging/Classes/FluidNRTProcess.sc | 2 +- 23 files changed, 100 insertions(+), 104 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 661cc4c..fa971ad 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -248,8 +248,8 @@ public: }); } - /// Penultimate input is the doneAction, final is blocking mode. Neither are - /// params, so we skip them in the controlsIterator. We may also have an ID for Model objects + /// Penultimate input is the trigger, final is blocking mode. Neither are + /// params, so we skip them in the controlsIterator NonRealTime() : mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false} {} @@ -272,7 +272,7 @@ public: void init() { mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); - mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); + // we want to poll thread roughly every 20ms checkThreadInterval = static_cast(0.02 / controlDur()); set_calc_function(); @@ -285,6 +285,12 @@ public: { out0(0) = mDone ? 1.0f : static_cast(client().progress()); + index triggerInput = mInBuf[mNumInputs - mSpecialIndex - 2][0]; + bool trigger = (mPreviousTrigger <= 0) && triggerInput > 0; + mPreviousTrigger = triggerInput; + + if(trigger) mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); + if (0 == pollCounter++ && !mCheckingForDone) { mCheckingForDone = true; @@ -302,6 +308,7 @@ public: { auto w = static_cast(f->mData); w->mDone = false; + w->mJobDone = false; w->mCancelled = false; Result result = validateParameters(w); @@ -353,7 +360,7 @@ public: return false; } - w->mDone = true; + w->mJobDone = true; return true; } return false; @@ -375,17 +382,7 @@ public: static void destroy(World* world, void* data) { auto w = static_cast(data); - if (w->mDone && - w->mNumInputs > - 2) // don't check for doneAction if UGen has no ins (there should be - // 3 minimum -> sig, doneAction, blocking mode) - { - int doneAction = static_cast( - w->in0(int(w->mNumInputs) - - 2)); // doneAction is penultimate input; THIS IS THE LAW - world->ft->fDoneAction(doneAction, w); - return; - } + w->mDone = w->mJobDone; w->mCheckingForDone = false; } @@ -414,7 +411,7 @@ private: // At this point, we can see if we're finished and let the language know (or // it can wait for the doneAction, but that takes extra time) use replyID to // convey status (0 = normal completion, 1 = cancelled) - if (mDone) + if (mJobDone) world->ft->fSendNodeReply(&mParent->mNode, 0, "/done", 0, nullptr); if (mCancelled) world->ft->fSendNodeReply(&mParent->mNode, 1, "/done", 0, nullptr); @@ -451,15 +448,15 @@ private: const char* mName = nullptr; index checkThreadInterval; index pollCounter{0}; - + index mPreviousTrigger{0}; protected: - bool mSynchronous{true}; - bool mQueueEnabled{false}; - bool mCheckingForDone{false}; // only write to this from RT thread kthx - bool mCancelled{false}; -private: - Wrapper* mWrapper{static_cast(this)}; - Result mResult; + bool mSynchronous{true}; + bool mQueueEnabled{false}; + bool mCheckingForDone{false}; // only write to this from RT thread kthx + bool mCancelled{false}; + bool mJobDone{false}; + Wrapper* mWrapper{static_cast(this)}; + Result mResult; }; //////////////////////////////////////////////////////////////////////////////// @@ -1190,7 +1187,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase }); - x->mDone = false; +// x->mDone = false; ft->fDoAsynchronousCommand( x->mWorld, nullptr, getName(), msg, [](World*, void* data) // NRT thread: invocation diff --git a/release-packaging/Classes/FluidBufAmpGate.sc b/release-packaging/Classes/FluidBufAmpGate.sc index cd32d7b..6636ae0 100644 --- a/release-packaging/Classes/FluidBufAmpGate.sc +++ b/release-packaging/Classes/FluidBufAmpGate.sc @@ -1,6 +1,6 @@ FluidBufAmpGate : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0, blocking| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking| var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); source = source.asUGenInput; @@ -9,12 +9,12 @@ FluidBufAmpGate : UGen { source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, doneAction,blocking:0); + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, trig,blocking:0); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action | diff --git a/release-packaging/Classes/FluidBufAmpSlice.sc b/release-packaging/Classes/FluidBufAmpSlice.sc index d2ceae4..72f29da 100644 --- a/release-packaging/Classes/FluidBufAmpSlice.sc +++ b/release-packaging/Classes/FluidBufAmpSlice.sc @@ -1,6 +1,6 @@ FluidBufAmpSlice : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, doneAction = 0, blocking| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking| source = source.asUGenInput; indices = indices.asUGenInput; @@ -8,12 +8,12 @@ FluidBufAmpSlice : UGen { source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, doneAction = 0| + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, doneAction,blocking:0); + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig,blocking:0); } *process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, action | diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 0d00f67..52069e6 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -2,7 +2,7 @@ FluidBufAudioTransport : UGen{ var <>synth, <>server; - *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| + *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; var blocking = 0; @@ -17,7 +17,7 @@ FluidBufAudioTransport : UGen{ //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^this.multiNew(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize, doneAction,blocking); + ^this.multiNew(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize, trig,blocking); } @@ -40,7 +40,7 @@ FluidBufAudioTransport : UGen{ "WARNING: Server not running".postln; ^nil; }); - synth = { instance = FluidBufAudioTransport.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, doneAction:Done.freeSelf)}.play(server); + synth = { instance = FluidBufAudioTransport.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, trig:1)}.play(server); forkIfNeeded{ synth.waitForFree; server.sync; diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index 51d1f80..47f323d 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -1,6 +1,6 @@ FluidBufCompose : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, doneAction = 0, blocking| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking| source = source.asUGenInput; destination = destination.asUGenInput; @@ -8,12 +8,12 @@ FluidBufCompose : UGen { source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); } -/* *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, doneAction = 0| +/* *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1| - ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, doneAction, blocking:1); + ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking:1); }*/ diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index a1f2aef..fa5fb1e 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -1,6 +1,6 @@ FluidBufHPSS : UGen { - *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking| + *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -13,13 +13,13 @@ FluidBufHPSS : UGen { //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, trig, blocking); } - *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| + *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| ^this.multiNew( - 'control', source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, doneAction, blocking:0 + 'control', source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, trig, blocking:0 ); } diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 1b0b994..80dbabf 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -1,5 +1,5 @@ FluidBufLoudness : UGen{ - *new1 { |rate,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, doneAction = 0, blocking = 0| + *new1 { |rate,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0| var maxwindowSize = windowSize.nextPowerOfTwo; @@ -9,11 +9,11 @@ FluidBufLoudness : UGen{ source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, doneAction = 0| - ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, doneAction, blocking:0 ); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1| + ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, trig, blocking:0 ); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action| diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 5c336a4..4a13115 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -1,5 +1,5 @@ FluidBufMFCC : UGen{ - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; @@ -12,11 +12,11 @@ FluidBufMFCC : UGen{ //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) // same goes to maxNumCoeffs, which is passed numCoeffs in this case - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action | diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index 6ec3f8f..182f5ca 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -1,5 +1,5 @@ FluidBufMelBands : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -13,11 +13,11 @@ FluidBufMelBands : UGen { //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) // same for maxNumBands which is passed numBands - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action| diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 1d41eed..4ffcd7b 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -1,6 +1,6 @@ FluidBufNMF : UGen { - *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0, blocking = 0| + *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1, blocking = 0| source = source.asUGenInput; resynth = resynth.asUGenInput; @@ -13,12 +13,12 @@ FluidBufNMF : UGen { bases = bases ? -1; activations = activations ? -1; - ^super.new1(rate,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking); + ^super.new1(rate,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking); } - *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0| - ^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction); + *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1| + ^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig); } diff --git a/release-packaging/Classes/FluidBufNMFCross.sc b/release-packaging/Classes/FluidBufNMFCross.sc index 628be69..b794b3f 100644 --- a/release-packaging/Classes/FluidBufNMFCross.sc +++ b/release-packaging/Classes/FluidBufNMFCross.sc @@ -1,6 +1,6 @@ FluidBufNMFCross : UGen{ - *new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| source = source.asUGenInput; target = target.asUGenInput; @@ -9,11 +9,11 @@ FluidBufNMFCross : UGen{ target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw}; - ^super.new1(rate, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, doneAction, blocking); + ^super.new1(rate, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking); } - *kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, doneAction); + *kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig); } *process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, action| diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index 7467349..0b2df12 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -1,5 +1,5 @@ FluidBufNoveltySlice : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0 | + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0 | var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -9,13 +9,13 @@ FluidBufNoveltySlice : UGen { source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0 | + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1 | - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, doneAction); + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, trig); } diff --git a/release-packaging/Classes/FluidBufOnsetSlice.sc b/release-packaging/Classes/FluidBufOnsetSlice.sc index 218e71c..7f0d323 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -1,5 +1,5 @@ FluidBufOnsetSlice : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -12,11 +12,11 @@ FluidBufOnsetSlice : UGen { //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index 71e89c7..a268401 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -1,6 +1,6 @@ FluidBufPitch : UGen{ - *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -12,12 +12,12 @@ FluidBufPitch : UGen{ //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, doneAction); + *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, trig); } diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index dce7f43..4837f31 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -1,6 +1,6 @@ FluidBufSines : UGen{ - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -13,11 +13,11 @@ FluidBufSines : UGen{ //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, action| diff --git a/release-packaging/Classes/FluidBufSpectralShape.sc b/release-packaging/Classes/FluidBufSpectralShape.sc index bdbdb4a..7acd7b5 100644 --- a/release-packaging/Classes/FluidBufSpectralShape.sc +++ b/release-packaging/Classes/FluidBufSpectralShape.sc @@ -1,6 +1,6 @@ FluidBufSpectralShape : UGen { - *new1{ |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0, blocking = 0| + *new1{ |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; @@ -13,13 +13,13 @@ FluidBufSpectralShape : UGen { //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } - *kr{ |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, doneAction); + *kr{ |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, trig); } diff --git a/release-packaging/Classes/FluidBufStats.sc b/release-packaging/Classes/FluidBufStats.sc index 830b6c8..51349b2 100644 --- a/release-packaging/Classes/FluidBufStats.sc +++ b/release-packaging/Classes/FluidBufStats.sc @@ -1,6 +1,6 @@ FluidBufStats : UGen{ - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, doneAction=0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, trig = 1, blocking = 0| source = source.asUGenInput; stats = stats.asUGenInput; @@ -8,11 +8,11 @@ FluidBufStats : UGen{ source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, doneAction=0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high,trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, action| diff --git a/release-packaging/Classes/FluidBufThreadDemo.sc b/release-packaging/Classes/FluidBufThreadDemo.sc index 44d960b..429d97f 100644 --- a/release-packaging/Classes/FluidBufThreadDemo.sc +++ b/release-packaging/Classes/FluidBufThreadDemo.sc @@ -1,14 +1,14 @@ FluidBufThreadDemo : UGen{ - *new1 {|rate, result, time, doneAction = 0, blocking = 0 | + *new1 {|rate, result, time, trig = 1, blocking = 0 | result = result.asUGenInput; result.isNil.if {this.class.name+": Invalid output buffer".throw}; - ^super.new1(rate, result, time, doneAction, blocking); + ^super.new1(rate, result, time, trig, blocking); } - *kr {|result, time, doneAction = 0| - ^this.new1(\control, result, time, doneAction); + *kr {|result, time, trig = 1| + ^this.new1(\control, result, time, trig); } *process { |server, result, time = 1000, action| diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index 19362ea..989fcea 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -1,5 +1,5 @@ FluidBufTransientSlice : UGen{ - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, doneAction = 0, blocking = 0| + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1, blocking = 0| source = source.asUGenInput; indices = indices.asUGenInput; @@ -7,11 +7,11 @@ FluidBufTransientSlice : UGen{ source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig); } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, action| diff --git a/release-packaging/Classes/FluidBufTransients.sc b/release-packaging/Classes/FluidBufTransients.sc index 00cecbb..7e9deb2 100644 --- a/release-packaging/Classes/FluidBufTransients.sc +++ b/release-packaging/Classes/FluidBufTransients.sc @@ -1,7 +1,7 @@ FluidBufTransients : UGen { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, doneAction = 0, blocking = 0 | + *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0 | source = source.asUGenInput; transients = transients.asUGenInput; @@ -9,12 +9,12 @@ FluidBufTransients : UGen { source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction, blocking); + ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, doneAction = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, doneAction); + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1| + ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig); } diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 552aa23..40cd442 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -148,4 +148,3 @@ FluidServerCache { } } - diff --git a/release-packaging/Classes/FluidMessageTest.sc b/release-packaging/Classes/FluidMessageTest.sc index f440d37..5018b36 100644 --- a/release-packaging/Classes/FluidMessageTest.sc +++ b/release-packaging/Classes/FluidMessageTest.sc @@ -2,9 +2,9 @@ FluidMessageTest : UGen { var server; - *kr{ |doneAction = 0| + *kr{ |trig = 1| - ^this.multiNew('control', doneAction); + ^this.multiNew('control', trig); } testReturnStrings { |server, nodeID, action| diff --git a/release-packaging/Classes/FluidNRTProcess.sc b/release-packaging/Classes/FluidNRTProcess.sc index 35e8ce4..abfc03f 100644 --- a/release-packaging/Classes/FluidNRTProcess.sc +++ b/release-packaging/Classes/FluidNRTProcess.sc @@ -27,7 +27,7 @@ FluidNRTProcess : Object{ var c = Condition.new(false); synth = { - ugen.performList(\new1,\control, ugenArgs.collect{|a| a.asUGenInput} ++ Done.freeSelf ++ blocking); + FreeSelfWhenDone.kr(ugen.performList(\new1,\control, ugenArgs.collect{|a| a.asUGenInput} ++ 1 ++ blocking)); }.play(server); synth.postln; From 00b2b5f8d54b05932ea2cb1ef03248ae9faa8405 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 13 May 2020 22:40:04 +0100 Subject: [PATCH 129/550] Better argument validation for RT inputs --- include/FluidSCWrapper.hpp | 82 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index fa971ad..932ffb7 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -15,6 +15,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include #include #include +#include #include #include #include @@ -87,6 +88,74 @@ class RealTime : public SCUnit using HostVector = FluidTensorView; using ParamSetType = typename Client::ParamSetType; + template + struct doExpectedCount; + + template + struct doExpectedCount + { + static void count(const T& d,FloatControlsIter& c,Result& status) + { + if(!status.ok()) return; + + if(c.remain()) + { + index statedSize = d.fixedSize; + + if(c.remain() < statedSize) + status = {Result::Status::kError,"Ran out of arguments at ", d.name}; + + //fastforward + for(index i=0; i < statedSize; ++i) c.next(); + + } + } + }; + + template + struct doExpectedCount + { + static void count(const T& d,FloatControlsIter& c,Result& status) + { + if(!status.ok()) return; + + if(c.remain()) + { + index statedSize = static_cast(c.next()); + + if(c.remain() < statedSize) + status = {Result::Status::kError,"Ran out of arguments at ", d.name}; + + //fastforward + for(index i=0; i < statedSize; ++i) c.next(); + + } + } + }; + + + template + struct ExpectedCount{ + void operator ()(const T& descriptor,FloatControlsIter& c, Result& status) + { + doExpectedCount::value>::count(descriptor,c,status); + } + }; + + Result expectedSize(FloatControlsIter& controls) + { + if(controls.size() < Client::getParameterDescriptors().count()) + { + return {Result::Status::kError,"Fewer parameters than exepected. Got ", controls.size(), "expect at least", Client::getParameterDescriptors().count()}; + } + + Result countScan; + Client::getParameterDescriptors().template iterate( + std::forward(mControlsIterator), + std::forward(countScan)); + return countScan; + } + public: static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; } @@ -121,20 +190,19 @@ public: !(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) && "Client can't have both audio and control outputs"); - // If we don't the number of arguments we expect, the language side code is - // probably the wrong version set plugin to no-op, squawk, and bail; - if (static_cast(this)->mControlsIterator.size() != Client::getParameterDescriptors().count()) + Result r; + if(!(r = expectedSize(mControlsIterator)).ok()) { mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; std::cout << "ERROR: " << Wrapper::getName() - << " wrong number of arguments. Expected " - << Client::getParameterDescriptors().count() << ", got " - << static_cast(this)->mControlsIterator.size() - << ". Your .sc file and binary plugin might be different versions." + << " wrong number of arguments." + << r.message() << std::endl; return; } + + mControlsIterator.reset(mInBuf + mSpecialIndex + 1); client.sampleRate(fullSampleRate()); mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); From 32ac90c28d251a1d99ea2a4654166f5fe976b999 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 27 May 2020 17:55:28 +0100 Subject: [PATCH 130/550] Bring wrapper into line with upstream --- include/FluidSCWrapper.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 932ffb7..6838dce 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -151,7 +151,7 @@ class RealTime : public SCUnit Result countScan; Client::getParameterDescriptors().template iterate( - std::forward(mControlsIterator), + std::forward(mWrapper->mControlsIterator), std::forward(countScan)); return countScan; } @@ -191,7 +191,7 @@ public: "Client can't have both audio and control outputs"); Result r; - if(!(r = expectedSize(mControlsIterator)).ok()) + if(!(r = expectedSize(mWrapper->mControlsIterator)).ok()) { mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; std::cout @@ -202,7 +202,7 @@ public: return; } - mControlsIterator.reset(mInBuf + mSpecialIndex + 1); + mWrapper->mControlsIterator.reset(mInBuf + mSpecialIndex + 1); client.sampleRate(fullSampleRate()); mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); @@ -267,6 +267,7 @@ private: std::vector mAudioInputs; std::vector mOutputs; FluidContext mContext; + Wrapper* mWrapper{static_cast(this)}; }; //////////////////////////////////////////////////////////////////////////////// @@ -353,7 +354,7 @@ public: { out0(0) = mDone ? 1.0f : static_cast(client().progress()); - index triggerInput = mInBuf[mNumInputs - mSpecialIndex - 2][0]; + index triggerInput = static_cast(mInBuf[static_cast(mNumInputs) - mSpecialIndex - 2][0]); bool trigger = (mPreviousTrigger <= 0) && triggerInput > 0; mPreviousTrigger = triggerInput; @@ -445,9 +446,8 @@ public: return static_cast(data)->tidyUp(world); } - /// Now we're actually properly done, call the UGen's done action (possibly - /// destroying this instance) - static void destroy(World* world, void* data) + /// if we're properly done set the Unit done flag + static void destroy(World*, void* data) { auto w = static_cast(data); w->mDone = w->mJobDone; @@ -517,7 +517,7 @@ private: index checkThreadInterval; index pollCounter{0}; index mPreviousTrigger{0}; -protected: + bool mSynchronous{true}; bool mQueueEnabled{false}; bool mCheckingForDone{false}; // only write to this from RT thread kthx From 1cbe5d01871fa2ced16b8284cde511e4ce0ccf96 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 22:58:29 +0100 Subject: [PATCH 131/550] Error message spacing --- include/SCBufferAdaptor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index bca7445..c1aebdf 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -185,8 +185,8 @@ public: { if(frames > thisThing->frames || channels > thisThing->channels) { - return {Result::Status::kError, "Local buffer must be presized adequetly, need", - frames, "frames", channels, "channels." }; + return {Result::Status::kError, "Local buffer must be presized adequetly, need ", + frames, "frames, ", channels, " channels." }; } else return {}; } From 23785e88be173be9956f7c59619bc156e9c60723 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 22:59:18 +0100 Subject: [PATCH 132/550] Constrain params before constructing clients --- include/FluidSCWrapper.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6838dce..93a1734 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -595,7 +595,7 @@ struct LifetimePolicy { FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; auto params = typename Wrapper::ParamSetType{Client::getParameterDescriptors()}; - Wrapper::setParams(unit, params, controlsReader); + Wrapper::setParams(unit, params, controlsReader,true); Client client{params}; new (static_cast(unit)) Wrapper(std::move(controlsReader), std::move(client), std::move(params)); } @@ -631,7 +631,7 @@ struct LifetimePolicy std::cout << "ERROR: ID " << uid << "is already being used by the cache" << std::endl; return; } - Wrapper::setParams(unit, entry.params,controlsReader); + Wrapper::setParams(unit, entry.params,controlsReader,true); new (static_cast(unit)) Wrapper{std::move(controlsReader),std::move(client),std::move(params)}; static_cast(unit)->uid = uid; entry.leased = true; @@ -698,8 +698,10 @@ struct LifetimePolicy FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; auto params = typename Client::ParamSetType{Client::getParameterDescriptors()}; - Wrapper::setParams(unit, params,controlsReader); + Wrapper::setParams(unit, params,controlsReader,true); + auto& name = params.template get<0>(); + auto client = Client{params}; auto clientRef = SharedType::lookup(name); From 73530260b56b7b129c41481f0ad0d99fe16149ee Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 23:00:25 +0100 Subject: [PATCH 133/550] Toughen checks for job status now that start time is decoupled from construction More toughening of threads vs triggers --- include/FluidSCWrapper.hpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 93a1734..11aad2a 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -358,9 +358,12 @@ public: bool trigger = (mPreviousTrigger <= 0) && triggerInput > 0; mPreviousTrigger = triggerInput; - if(trigger) mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); + if(trigger) + { + mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); + } - if (0 == pollCounter++ && !mCheckingForDone) + if (0 == pollCounter++ && !mCheckingForDone && mHasTriggered) { mCheckingForDone = true; mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), @@ -391,6 +394,7 @@ public: w->mClient.setSynchronous(w->mSynchronous); w->mClient.enqueue(w->mParams); w->mResult = w->mClient.process(); + w->mHasTriggered = true; } /// Check result and report if bad @@ -419,16 +423,21 @@ public: std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; w->mCancelled = true; + w->mHasTriggered = false; + w->mJobDone = true; return false; } if (!r.ok()) { - std::cout << "ERROR: " << Wrapper::getName() << ": " + if(!w->mDone) + std::cout << "ERROR: " << Wrapper::getName() << ": " << r.message().c_str() << std::endl; + w->mJobDone = true; + w->mHasTriggered = false; return false; } - + w->mHasTriggered = false; w->mJobDone = true; return true; } @@ -523,6 +532,7 @@ private: bool mCheckingForDone{false}; // only write to this from RT thread kthx bool mCancelled{false}; bool mJobDone{false}; + bool mHasTriggered{false}; Wrapper* mWrapper{static_cast(this)}; Result mResult; }; From b7c6c9e09ca8c76099e499aff1651e24cc293133 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 23:04:16 +0100 Subject: [PATCH 134/550] Add FluidDataSetWr --- include/clients/rt/FluidDataSetWr.hpp | 48 +++++++++++++++++++++ release-packaging/Classes/FluidDataSetWr.sc | 12 ++++++ src/FluidManipulation/FluidManipulation.cpp | 2 + 3 files changed, 62 insertions(+) create mode 100644 include/clients/rt/FluidDataSetWr.hpp create mode 100644 release-packaging/Classes/FluidDataSetWr.sc diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp new file mode 100644 index 0000000..e700a4f --- /dev/null +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fluid { +namespace client { + +class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut +{ +public: + FLUID_DECLARE_PARAMS(StringParam("label", "Label"), + BufferParam("buf", "Data Buffer"), + DataSetClientRef::makeParam("dataSet", "DataSet Name")); + + DataSetWriterClient(ParamSetViewType& p) : mParams(p) {} + + template + Result process(FluidContext&) + { + auto& idx = get<0>(); + auto buf = get<1>(); + auto dataset = get<2>().get(); + if (auto datasetPtr = dataset.lock()) + return datasetPtr->addPoint(idx, buf); + else + return {Result::Status::kError, "No dataset"}; + } +}; + +using NRTThreadedDataSetWriter = + NRTThreadingAdaptor>; +} // namespace client +} // namespace fluid diff --git a/release-packaging/Classes/FluidDataSetWr.sc b/release-packaging/Classes/FluidDataSetWr.sc new file mode 100644 index 0000000..bb239fa --- /dev/null +++ b/release-packaging/Classes/FluidDataSetWr.sc @@ -0,0 +1,12 @@ +FluidDataSetWr : UGen { + + *new1 { |rate,label,buf,dataset,trig,blocking| + buf ?? {"No input buffer provided".error}; + ^super.new1(rate,*(FluidManipulationClient.prServerString(label.asSymbol) + ++buf.asUGenInput++FluidDataSet.asUGenInput(dataset.asSymbol)++trig++blocking)); + } + + *kr { |index, buf, dataset,trig=1| + ^this.new1(\control,index,buf,dataset,trig,1) + } +} diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index 15f3448..4df8b6f 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include static InterfaceTable *ft; @@ -34,4 +35,5 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidMDS",ft); makeSCWrapper("FluidAudioTransport",ft); makeSCWrapper("FluidBufAudioTransport",ft); + makeSCWrapper("FluidDataSetWr", ft); } From 377604f602502ffcc91ab2de791a1b2d02535907 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 23:04:55 +0100 Subject: [PATCH 135/550] Add FluidBufFlatten --- release-packaging/Classes/FluidBufFlatten.sc | 31 ++++++++++++++++++++ src/FluidBufFlatten/CMakeLists.txt | 21 +++++++++++++ src/FluidBufFlatten/FluidBufFlatten.cpp | 22 ++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 release-packaging/Classes/FluidBufFlatten.sc create mode 100644 src/FluidBufFlatten/CMakeLists.txt create mode 100644 src/FluidBufFlatten/FluidBufFlatten.cpp diff --git a/release-packaging/Classes/FluidBufFlatten.sc b/release-packaging/Classes/FluidBufFlatten.sc new file mode 100644 index 0000000..825e7d3 --- /dev/null +++ b/release-packaging/Classes/FluidBufFlatten.sc @@ -0,0 +1,31 @@ +FluidBufFlatten : UGen { + + *new1 { |rate, source, destination, axis = 1, trig = 1, blocking| + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + ^super.new1(rate, source, destination, axis, trig, blocking); + } + + *kr { |source, destination, axis = 1, trig = 1| + ^this.new1('control', source, destination, axis, trig, 1); + } + + *process { |server, source, destination, axis = 1, action| + ^FluidNRTProcess.new( + server, this, action, [destination], blocking:1 + ).process( + source, destination, axis + ); + + } + + *processBlocking { |server, source, destination, axis = 1, action| + ^process( + source, destination, axis + ); + } +} diff --git a/src/FluidBufFlatten/CMakeLists.txt b/src/FluidBufFlatten/CMakeLists.txt new file mode 100644 index 0000000..9646a4e --- /dev/null +++ b/src/FluidBufFlatten/CMakeLists.txt @@ -0,0 +1,21 @@ +# Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +# Copyright 2017-2019 University of Huddersfield. +# Licensed under the BSD-3 License. +# See license.md file in the project root for full license information. +# This project has received funding from the European Research Council (ERC) +# under the European Union’s Horizon 2020 research and innovation programme +# (grant agreement No 725899). + +cmake_minimum_required(VERSION 3.11) + +get_filename_component(PLUGIN ${CMAKE_CURRENT_LIST_DIR} NAME_WE) +message("Configuring ${PLUGIN}") +set(FILENAME ${PLUGIN}.cpp) + +add_library( + ${PLUGIN} + MODULE + ${FILENAME} +) + +include(${CMAKE_CURRENT_LIST_DIR}/../../scripts/target_post.cmake) diff --git a/src/FluidBufFlatten/FluidBufFlatten.cpp b/src/FluidBufFlatten/FluidBufFlatten.cpp new file mode 100644 index 0000000..0a6746c --- /dev/null +++ b/src/FluidBufFlatten/FluidBufFlatten.cpp @@ -0,0 +1,22 @@ +/* +Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) +Copyright 2017-2019 University of Huddersfield. +Licensed under the BSD-3 License. +See license.md file in the project root for full license information. +This project has received funding from the European Research Council (ERC) +under the European Union’s Horizon 2020 research and innovation programme +(grant agreement No 725899). +*/ + +#include + +#include + +static InterfaceTable *ft; + +PluginLoad(OfflineFluidDecompositionUGens) +{ + ft = inTable; + using namespace fluid::client; + makeSCWrapper("FluidBufFlatten", ft); +} From 2c0142b0022a8d5cf2a1e6e22361eeea6ee152b9 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 1 Jun 2020 23:39:26 +0100 Subject: [PATCH 136/550] Add corpus utils Corpus builders: tidy up --- .../Classes/FluidCorpusBuilders.sc | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 release-packaging/Classes/FluidCorpusBuilders.sc diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc new file mode 100644 index 0000000..dea1c3e --- /dev/null +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -0,0 +1,149 @@ +FluidLoadFolder { + var path, labelFunc,channelFunc; + var < files; + var < index; + var < buffer; + + *new{ |path, labelFunc, channelFunc | + ^super.newCopyArgs(path, labelFunc,channelFunc); + } + + play { |server, action| + var sizes,channels,maxChan, startEnd; + server ?? server = Server.default; + files = SoundFile.collect(path +/+ '*'); + sizes = files.collect{|f|f.numFrames()}; + channels = files.collect{|f| f.numChannels()}; + startEnd = sizes.inject([0],{|a,b| a ++ (b + a[a.size - 1])}).slide(2).clump(2); + maxChan = channels[channels.maxIndex]; + + index = IdentityDictionary(); + fork{ + buffer = Buffer.alloc(server,sizes.reduce('+'),maxChan); + buffer.updateInfo; + server.sync; + buffer.query; + this.files.do{|f,i| + var channelMap,label,entry; + if(channelFunc.notNil) + { channelMap = channelFunc.value(channels[i],maxChan,i) } + { channelMap = Array.series(channels[i]).wrapExtend(maxChan) }; + buffer.readChannel(f.path,bufStartFrame:sizes[0..i].sum(), channels:channelMap); + server.sync; + + if(labelFunc.notNil) + { label = labelFunc.value(path,i) } + { label = (f.path.basename).asSymbol }; + entry = IdentityDictionary(); + entry.add(\points->startEnd[i]); + entry.add(\channels->f.numChannels); + entry.add(\sampleRate->f.sampleRate); + index.add(label->entry); + if(i == (files.size - 1)) {action !? action.value(index)}; + } + } + } +} + + +FluidSliceCorpus { + var < sliceFunc, labelFunc; + var < index; + + *new { |sliceFunc, labelFunc| + ^super.newCopyArgs(sliceFunc,labelFunc); + } + + play{ |server,sourceBuffer,bufIdx, action| + var counter, tmpIndices,perf,jobs,total,uid; + uid = UniqueID.next; + sourceBuffer ?? {"No buffer to slice".error; ^nil}; + bufIdx ?? {"No slice point dictionary passed".error;^nil}; + server ?? {server = Server.default}; + index = IdentityDictionary(); + counter = 0; + jobs = List.newFrom(bufIdx.keys); + total = jobs.size; + tmpIndices = Buffer.new; + perf = { + var v,k = jobs.pop; + v = bufIdx[k]; + OSCFunc({ + tmpIndices.loadToFloatArray(action:{ |a| + counter = counter + 1; + ("FluidSliceCorpus:" + ( counter.asString ++ "/" ++ total)).postln; + if(a[0] != -1){ + var slicePoints = Array.newFrom(a).slide(2).clump(2); + slicePoints.do{|s,j| + var label = (k ++ j).asSymbol; + index.add(label->IdentityDictionary(proto:v)); + index.at(label).put(\points,s); + } + }{ + index.put((k++ '0').asSymbol->IdentityDictionary(proto:v)); + }; + if(jobs.size > 0){perf.value} + { + tmpIndices.free; + action !? action.value(index); + }; + }) + },'/doneslice' ++ uid ++ counter,server.addr).oneShot; + + { + var numframes,onsets; + numframes = v[\points].reverse.reduce('-'); + onsets = sliceFunc.value(sourceBuffer, v[\points][0],numframes,tmpIndices); + SendReply.kr(Done.kr(onsets),'/doneslice' ++ uid ++ counter); + FreeSelfWhenDone.kr(onsets); + }.play; + }; + perf.value; + } +} + +FluidProcessSlices{ + var < featureFunc, labelFunc; + var < index; + + *new { |featureFunc, labelFunc| + ^super.newCopyArgs(featureFunc,labelFunc); + } + + play{ |server,sourceBuffer,bufIdx, action| + var counter, tmpIndices,perf,jobs,total,uid; + + sourceBuffer ?? {"No buffer to slice".error; ^nil}; + bufIdx ?? {"No slice point dictionary passed".error;^nil}; + server ?? {server = Server.default}; + index = IdentityDictionary(); + + uid = UniqueID.next; + jobs = List.newFrom(bufIdx.keys); + total = jobs.size; + counter = 0; + + perf = { + var v, k = jobs.pop; + v = bufIdx[k]; + OSCFunc({ + counter = counter + 1; + ("FluidProcessSlices:" + (counter.asString ++ "/" ++ total)).postln; + if(jobs.size > 0){perf.value} + { + tmpIndices.free; + action !? action.value(index); + }; + },"/doneFeature" ++ uid ++ counter,server.addr).oneShot; + + { + var numframes,feature; + numframes = v[\points].reverse.reduce('-'); + feature = featureFunc.value(sourceBuffer, v[\points][0],numframes,counter); + SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ counter); + FreeSelfWhenDone.kr(feature); + }.play(server); + }; + perf.value; + } +} From a85287a7850deca52c587bde8a1d2bd5c63c91c8 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 2 Jun 2020 16:20:49 +0100 Subject: [PATCH 137/550] Refactor file loading to try and improve speed by removing syncs --- .../Classes/FluidCorpusBuilders.sc | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index dea1c3e..304037a 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -9,39 +9,42 @@ FluidLoadFolder { } play { |server, action| - var sizes,channels,maxChan, startEnd; + var sizes,channels,maxChan, startEnd,counter; server ?? server = Server.default; files = SoundFile.collect(path +/+ '*'); sizes = files.collect{|f|f.numFrames()}; channels = files.collect{|f| f.numChannels()}; startEnd = sizes.inject([0],{|a,b| a ++ (b + a[a.size - 1])}).slide(2).clump(2); maxChan = channels[channels.maxIndex]; - + counter = 0; index = IdentityDictionary(); - fork{ + files.postln; + server.bind{ buffer = Buffer.alloc(server,sizes.reduce('+'),maxChan); - buffer.updateInfo; server.sync; + buffer.updateInfo; buffer.query; + server.sync; this.files.do{|f,i| var channelMap,label,entry; + OSCFunc({ + if(labelFunc.notNil) + { label = labelFunc.value(path,i) } + { label = (f.path.basename).asSymbol }; + entry = IdentityDictionary(); + entry.add(\points->startEnd[i]); + entry.add(\channels->f.numChannels); + entry.add(\sampleRate->f.sampleRate); + index.add(label->entry); + counter = counter + 1; + if(counter == (files.size)) {action !? action.value(index)}; + },"/done",server.addr,argTemplate:["/b_readChannel"]).oneShot; if(channelFunc.notNil) { channelMap = channelFunc.value(channels[i],maxChan,i) } { channelMap = Array.series(channels[i]).wrapExtend(maxChan) }; buffer.readChannel(f.path,bufStartFrame:sizes[0..i].sum(), channels:channelMap); - server.sync; - - if(labelFunc.notNil) - { label = labelFunc.value(path,i) } - { label = (f.path.basename).asSymbol }; - entry = IdentityDictionary(); - entry.add(\points->startEnd[i]); - entry.add(\channels->f.numChannels); - entry.add(\sampleRate->f.sampleRate); - index.add(label->entry); - if(i == (files.size - 1)) {action !? action.value(index)}; } - } + }; } } From 75f05627f6df156cb442a810c9026f199124ca62 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 2 Jun 2020 18:31:21 +0100 Subject: [PATCH 138/550] loading is now working and very, very fast --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 304037a..1a0077e 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -19,7 +19,7 @@ FluidLoadFolder { counter = 0; index = IdentityDictionary(); files.postln; - server.bind{ + forkIfNeeded{ buffer = Buffer.alloc(server,sizes.reduce('+'),maxChan); server.sync; buffer.updateInfo; From e21aa5e2fe0fca96e383e05fbd2596eec4259c1b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 2 Jun 2020 19:02:14 +0100 Subject: [PATCH 139/550] Fix file start times --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 1a0077e..3517439 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -42,7 +42,7 @@ FluidLoadFolder { if(channelFunc.notNil) { channelMap = channelFunc.value(channels[i],maxChan,i) } { channelMap = Array.series(channels[i]).wrapExtend(maxChan) }; - buffer.readChannel(f.path,bufStartFrame:sizes[0..i].sum(), channels:channelMap); + buffer.readChannel(f.path,bufStartFrame:startEnd[i][0], channels:channelMap); } }; } From 7cbfecc3e7d595070a52f227479d041f30513beb Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 2 Jun 2020 19:22:15 +0100 Subject: [PATCH 140/550] Experimental paralellism for performance boost? --- release-packaging/Classes/FluidCorpusBuilders.sc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 3517439..8a7b97d 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -67,8 +67,7 @@ FluidSliceCorpus { counter = 0; jobs = List.newFrom(bufIdx.keys); total = jobs.size; - tmpIndices = Buffer.new; - perf = { + perf = { |tmpIndices| var v,k = jobs.pop; v = bufIdx[k]; OSCFunc({ @@ -85,11 +84,8 @@ FluidSliceCorpus { }{ index.put((k++ '0').asSymbol->IdentityDictionary(proto:v)); }; - if(jobs.size > 0){perf.value} - { - tmpIndices.free; - action !? action.value(index); - }; + if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; + if(counter == total) {action !? action.value(index)}; }) },'/doneslice' ++ uid ++ counter,server.addr).oneShot; @@ -101,7 +97,7 @@ FluidSliceCorpus { FreeSelfWhenDone.kr(onsets); }.play; }; - perf.value; + 4.do{perf.value(Buffer.new)}; } } From 699c648e2702f3952d555925ae73aa498889a2bd Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 2 Jun 2020 21:41:51 +0100 Subject: [PATCH 141/550] Add concurrent processing for feature extraction, sort out counting, be consistent with dictionary methods --- .../Classes/FluidCorpusBuilders.sc | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 8a7b97d..005129b 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -58,34 +58,37 @@ FluidSliceCorpus { } play{ |server,sourceBuffer,bufIdx, action| - var counter, tmpIndices,perf,jobs,total,uid; + var counter, tmpIndices,perf,jobs,total,uid, completed; uid = UniqueID.next; sourceBuffer ?? {"No buffer to slice".error; ^nil}; bufIdx ?? {"No slice point dictionary passed".error;^nil}; server ?? {server = Server.default}; index = IdentityDictionary(); counter = 0; + completed = 0; jobs = List.newFrom(bufIdx.keys); total = jobs.size; perf = { |tmpIndices| - var v,k = jobs.pop; + var idx,v,k = jobs.pop; v = bufIdx[k]; + counter = counter + 1; + idx = counter; OSCFunc({ tmpIndices.loadToFloatArray(action:{ |a| - counter = counter + 1; - ("FluidSliceCorpus:" + ( counter.asString ++ "/" ++ total)).postln; + completed = completed + 1; + ("FluidSliceCorpus:" + ( completed.asString ++ "/" ++ total)).postln; if(a[0] != -1){ var slicePoints = Array.newFrom(a).slide(2).clump(2); slicePoints.do{|s,j| var label = (k ++ j).asSymbol; index.add(label->IdentityDictionary(proto:v)); - index.at(label).put(\points,s); + index.at(label).add(\points->s); } }{ - index.put((k++ '0').asSymbol->IdentityDictionary(proto:v)); + index.add((k ++ '0').asSymbol->IdentityDictionary(proto:v)); }; if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; - if(counter == total) {action !? action.value(index)}; + if(completed == total) {action !? action.value(index)}; }) },'/doneslice' ++ uid ++ counter,server.addr).oneShot; @@ -93,7 +96,7 @@ FluidSliceCorpus { var numframes,onsets; numframes = v[\points].reverse.reduce('-'); onsets = sliceFunc.value(sourceBuffer, v[\points][0],numframes,tmpIndices); - SendReply.kr(Done.kr(onsets),'/doneslice' ++ uid ++ counter); + SendReply.kr(Done.kr(onsets),'/doneslice' ++ uid ++ idx); FreeSelfWhenDone.kr(onsets); }.play; }; @@ -110,7 +113,7 @@ FluidProcessSlices{ } play{ |server,sourceBuffer,bufIdx, action| - var counter, tmpIndices,perf,jobs,total,uid; + var counter,perf,jobs,total,uid, completed; sourceBuffer ?? {"No buffer to slice".error; ^nil}; bufIdx ?? {"No slice point dictionary passed".error;^nil}; @@ -121,28 +124,27 @@ FluidProcessSlices{ jobs = List.newFrom(bufIdx.keys); total = jobs.size; counter = 0; - + completed = 0; perf = { - var v, k = jobs.pop; + var idx,v, k = jobs.pop; v = bufIdx[k]; + counter = counter + 1; + idx = counter; OSCFunc({ - counter = counter + 1; - ("FluidProcessSlices:" + (counter.asString ++ "/" ++ total)).postln; - if(jobs.size > 0){perf.value} - { - tmpIndices.free; - action !? action.value(index); - }; + completed = completed + 1; + ("FluidProcessSlices:" + (completed.asString ++ "/" ++ total)).postln; + if(jobs.size > 0){perf.value}; + if(completed == total){action !? action.value(index);}; },"/doneFeature" ++ uid ++ counter,server.addr).oneShot; { var numframes,feature; numframes = v[\points].reverse.reduce('-'); - feature = featureFunc.value(sourceBuffer, v[\points][0],numframes,counter); - SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ counter); + feature = featureFunc.value(sourceBuffer, v[\points][0],numframes,idx); + SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); }; - perf.value; + 4.do{perf.value}; } } From 48dc167c1b05155f96dbb8db2f56dad1111382f5 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 3 Jun 2020 17:18:15 +0100 Subject: [PATCH 142/550] Toughen NRT processing by decoupling ownership of shared state from synth Node --- include/FluidSCWrapper.hpp | 231 ++++++++++++++++++++----------------- 1 file changed, 126 insertions(+), 105 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 11aad2a..ce7926a 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -21,6 +21,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include #include #include +#include #include #include #include @@ -35,6 +36,21 @@ namespace client { template class FluidSCWrapper; + +template +struct WrapperState +{ + typename Client::ParamSetType params; + Client client; + Node* mNode; + bool mCancelled{false}; + bool mJobDone{false}; + bool mHasTriggered{false}; + bool mSynchronous{false}; + bool mInNRT{false}; + Result mResult{}; +}; + namespace impl { template @@ -169,7 +185,7 @@ public: static void doLatency(Unit* unit, sc_msg_iter*) { float l[]{ - static_cast(static_cast(unit)->mClient.latency()) + static_cast(static_cast(unit)->client().latency()) }; auto ft = Wrapper::getInterfaceTable(); @@ -185,7 +201,7 @@ public: void init() { - auto& client =static_cast(this)->mClient; + auto& client =static_cast(this)->client(); assert( !(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) && "Client can't have both audio and control outputs"); @@ -233,13 +249,13 @@ public: void next(int) { - auto& client = static_cast(this)->mClient; - auto& params = static_cast(this)->mParams; + auto& client = mWrapper->client(); + auto& params = mWrapper->params(); - static_cast(this)->mControlsIterator.reset(mInBuf + mSpecialIndex + + mWrapper->mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn()); - Wrapper::setParams(static_cast(this), - params, static_cast(this)->mControlsIterator); // forward on inputs N + audio inputs as params + Wrapper::setParams(mWrapper, + params, mWrapper->mControlsIterator); // forward on inputs N + audio inputs as params params.constrainParameterValues(); const Unit* unit = this; for (index i = 0; i < client.audioChannelsIn(); ++i) @@ -280,7 +296,7 @@ template class NonRealTime : public SCUnit { using ParamSetType = typename Client::ParamSetType; - + using SharedState = std::shared_ptr>; public: static index ControlOffset(Unit*) { return 0; } @@ -289,32 +305,32 @@ public: static void setup(InterfaceTable* ft, const char* name) { ft->fDefineUnitCmd(name, "cancel", doCancel); - ft->fDefineUnitCmd( - name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args) { - auto w = static_cast(unit); - w->mQueueEnabled = args->geti(0); - w->mFifoMsg.Set( - w->mWorld, - [](FifoMsg* f) { - auto w = static_cast(f->mData); - w->mClient.setQueueEnabled(w->mQueueEnabled); - }, - nullptr, w); - Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); - }); - ft->fDefineUnitCmd( - name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args) { - auto w = static_cast(unit); - w->mSynchronous = args->geti(0); - w->mFifoMsg.Set( - w->mWorld, - [](FifoMsg* f) { - auto w = static_cast(f->mData); - w->mClient.setSynchronous(w->mSynchronous); - }, - nullptr, w); - Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); - }); +// ft->fDefineUnitCmd( +// name, "queue_enabled", [](struct Unit* unit, struct sc_msg_iter* args) { +// auto w = static_cast(unit); +// w->mQueueEnabled = args->geti(0); +// w->mFifoMsg.Set( +// w->mWorld, +// [](FifoMsg* f) { +// auto w = static_cast(f->mData); +// w->client().setQueueEnabled(w->mQueueEnabled); +// }, +// nullptr, w); +// Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); +// }); +// ft->fDefineUnitCmd( +// name, "synchronous", [](struct Unit* unit, struct sc_msg_iter* args) { +// auto w = static_cast(unit); +// w->mSynchronous = args->geti(0); +// w->mFifoMsg.Set( +// w->mWorld, +// [](FifoMsg* f) { +// auto w = static_cast(f->mData); +// w->client().setSynchronous(w->mSynchronous); +// }, +// nullptr, w); +// Wrapper::getInterfaceTable()->fSendMsgFromRT(w->mWorld, w->mFifoMsg); +// }); } /// Penultimate input is the trigger, final is blocking mode. Neither are @@ -325,7 +341,7 @@ public: ~NonRealTime() { - if (client().state() == ProcessState::kProcessing) + if (mWrapper->client().state() == ProcessState::kProcessing) { std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done", @@ -340,6 +356,8 @@ public: /// UGen calc function going void init() { + mWrapper->state()->mSynchronous = mSynchronous; + mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); // we want to poll thread roughly every 20ms @@ -352,22 +370,31 @@ public: /// launches tidy up when complete void poll(int) { - out0(0) = mDone ? 1.0f : static_cast(client().progress()); + out0(0) = mDone ? 1.0f : static_cast(mWrapper->client().progress()); index triggerInput = static_cast(mInBuf[static_cast(mNumInputs) - mSpecialIndex - 2][0]); bool trigger = (mPreviousTrigger <= 0) && triggerInput > 0; mPreviousTrigger = triggerInput; + auto& sharedState = mWrapper->state(); + mWrapper->mDone = sharedState->mJobDone; if(trigger) { + SharedState* statePtr = static_cast(mWorld->ft->fRTAlloc(mWorld, sizeof(SharedState))); + statePtr = new (statePtr) SharedState(sharedState); + mFifoMsg.Set(mWorld, initNRTJob, nullptr, statePtr); mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); + return; } - if (0 == pollCounter++ && !mCheckingForDone && mHasTriggered) + if (0 == pollCounter++ && !sharedState->mInNRT && sharedState->mHasTriggered) { - mCheckingForDone = true; + sharedState->mInNRT = true; + + SharedState* statePtr = static_cast(mWorld->ft->fRTAlloc(mWorld, sizeof(SharedState))); + statePtr = new (statePtr) SharedState(sharedState); mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), - this, postProcess, exchangeBuffers, + statePtr, postProcess, exchangeBuffers, tidyUp, destroy, 0, nullptr); } pollCounter %= checkThreadInterval; @@ -378,12 +405,13 @@ public: /// new thread static void initNRTJob(FifoMsg* f) { - auto w = static_cast(f->mData); - w->mDone = false; - w->mJobDone = false; - w->mCancelled = false; - - Result result = validateParameters(w); + if(!f->mData) return; + auto w = static_cast(f->mData); + SharedState& s = *w; + s->mInNRT = true; + s->mJobDone = false; + s->mCancelled = false; + Result result = validateParameters(s->params); if (!result.ok()) { @@ -391,18 +419,25 @@ public: << result.message().c_str() << std::endl; return; } - w->mClient.setSynchronous(w->mSynchronous); - w->mClient.enqueue(w->mParams); - w->mResult = w->mClient.process(); - w->mHasTriggered = true; + s->client.setSynchronous(s->mSynchronous); + s->client.enqueue(s->params); + s->mResult = s->client.process(); + s->mHasTriggered = true; + s->mInNRT = false; + w->~SharedState(); + f->mWorld->ft->fRTFree(f->mWorld,w); } /// Check result and report if bad static bool postProcess(World*, void* data) { - auto w = static_cast(data); + + if(!data) return false; + + auto& w = *static_cast(data); Result r; - ProcessState s = w->mClient.checkProgress(r); + w->mInNRT = true; + ProcessState s = w->client.checkProgress(r); if(w->mSynchronous) r = w->mResult; @@ -430,7 +465,7 @@ public: if (!r.ok()) { - if(!w->mDone) + if(!w->mJobDone) std::cout << "ERROR: " << Wrapper::getName() << ": " << r.message().c_str() << std::endl; w->mJobDone = true; @@ -447,34 +482,48 @@ public: /// swap NRT buffers back to RT-land static bool exchangeBuffers(World* world, void* data) { - return static_cast(data)->exchangeBuffers(world); + if(!data) return false; + + SharedState& s = *(static_cast(data)); + s->params.template forEachParamType(world); + // At this point, we can see if we're finished and let the language know (or + // it can wait for the doneAction, but that takes extra time) use replyID to + // convey status (0 = normal completion, 1 = cancelled) + if (s->mJobDone && !s->mCancelled) + world->ft->fSendNodeReply(s->mNode, 0, "/done", 0, nullptr); + if (s->mCancelled) + world->ft->fSendNodeReply(s->mNode, 1, "/done", 0, nullptr); + return true; + } /// Tidy up any temporary buffers - static bool tidyUp(World* world, void* data) + static bool tidyUp(World* , void* data) { - return static_cast(data)->tidyUp(world); + if(!data) return false; + SharedState& s = *(static_cast(data)); + s->params.template forEachParamType(); + return true; } /// if we're properly done set the Unit done flag - static void destroy(World*, void* data) + static void destroy(World* world, void* data) { - auto w = static_cast(data); - w->mDone = w->mJobDone; - w->mCheckingForDone = false; + if(!data) return; + auto& s = *static_cast(data); + s->mInNRT = false; + s.~SharedState(); + world->ft->fRTFree(world,data); } static void doCancel(Unit* unit, sc_msg_iter*) { - static_cast(unit)->mClient.cancel(); + static_cast(unit)->client().cancel(); } - ParamSetType& params() { return mWrapper->mParams; } - Client& client() { return mWrapper->mClient; } - private: - static Result validateParameters(NonRealTime* nrt) + static Result validateParameters(ParamSetType& p) { - auto results = nrt->params().constrainParameterValues(); + auto results = p.constrainParameterValues(); for (auto& r : results) { if (!r.ok()) return r; @@ -482,25 +531,6 @@ private: return {}; } - bool exchangeBuffers(World* world) // RT thread - { - params().template forEachParamType(world); - // At this point, we can see if we're finished and let the language know (or - // it can wait for the doneAction, but that takes extra time) use replyID to - // convey status (0 = normal completion, 1 = cancelled) - if (mJobDone) - world->ft->fSendNodeReply(&mParent->mNode, 0, "/done", 0, nullptr); - if (mCancelled) - world->ft->fSendNodeReply(&mParent->mNode, 1, "/done", 0, nullptr); - return true; - } - - bool tidyUp(World*) // NRT thread - { - params().template forEachParamType(); - return true; - } - template struct AssignBuffer { @@ -528,13 +558,8 @@ private: index mPreviousTrigger{0}; bool mSynchronous{true}; - bool mQueueEnabled{false}; - bool mCheckingForDone{false}; // only write to this from RT thread kthx - bool mCancelled{false}; - bool mJobDone{false}; - bool mHasTriggered{false}; Wrapper* mWrapper{static_cast(this)}; - Result mResult; + Result mResult; }; //////////////////////////////////////////////////////////////////////////////// @@ -1260,14 +1285,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ft->fRTFree(x->mWorld, msgptr); return; } - - /// ForEach(args,[x,&inArgs](auto& arg){ arg = ParamReader::fromArgs(x, inArgs,arg,0); }); - -// x->mDone = false; ft->fDoAsynchronousCommand( x->mWorld, nullptr, getName(), msg, [](World*, void* data) // NRT thread: invocation @@ -1312,7 +1333,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, std::index_sequence) { - return x->mClient.template invoke(x->mClient, std::get(args)...); + return x->client().template invoke(x->client(), std::get(args)...); } template // call from RT @@ -1372,16 +1393,17 @@ public: using Client = C; using ParamSetType = typename C::ParamSetType; - - FluidSCWrapper(FloatControlsIter&& i, Client&& c, ParamSetType&& p): mControlsIterator{std::move(i)}, - mParams{std::move(p)}, mClient{std::move(c)} + mState{new WrapperState{std::move(p),std::move(c),&SCUnit::mParent->mNode}} { - mClient.setParams(mParams); //<-IMPORTANT: client's ref to params is by address, and this has just changed + client().setParams(params()); //<-IMPORTANT: client's ref to params is by address, and this has just changed impl::FluidSCWrapperBase::init(); } + std::shared_ptr>& state() { return mState; } + + static const char* getName(const char* setName = nullptr) { static const char* name = nullptr; @@ -1449,10 +1471,10 @@ public: } } - auto& client() { return mClient; } - auto& params() { return mParams; } + auto& client() { return mState->client; } + auto& params() { return mState->params; } - private: +private: static void registerUnit(InterfaceTable* ft, const char* name) { UnitCtorFunc ctor =impl::FluidSCWrapperBase::constructClass; @@ -1460,9 +1482,8 @@ public: (*ft->fDefineUnit)(name, sizeof(FluidSCWrapper), ctor, dtor, 0); } - FloatControlsIter mControlsIterator; - ParamSetType mParams; - Client mClient; + FloatControlsIter mControlsIterator; + std::shared_ptr> mState; }; template From e5798aaa358ccecabf2b3d5c1e9908efcb1877d3 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 4 Jun 2020 15:47:46 +0100 Subject: [PATCH 143/550] Harden cross thread code, and fix retriggering / busy check. By actually looking at the result. --- include/FluidSCWrapper.hpp | 43 ++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index ce7926a..1c52928 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -20,6 +20,7 @@ under the European Union’s Horizon 2020 research and innovation programme #include #include #include +#include #include #include #include @@ -43,11 +44,12 @@ struct WrapperState typename Client::ParamSetType params; Client client; Node* mNode; - bool mCancelled{false}; - bool mJobDone{false}; - bool mHasTriggered{false}; - bool mSynchronous{false}; - bool mInNRT{false}; + std::atomic mCancelled{false}; + std::atomic mJobDone{false}; + std::atomic mHasTriggered{false}; + std::atomic mSynchronous{false}; + std::atomic mInNRT{false}; + std::atomic mNodeAlive{true}; Result mResult{}; }; @@ -341,7 +343,8 @@ public: ~NonRealTime() { - if (mWrapper->client().state() == ProcessState::kProcessing) + auto state = mWrapper->client().state(); + if (state == ProcessState::kProcessing) { std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done", @@ -371,7 +374,7 @@ public: void poll(int) { out0(0) = mDone ? 1.0f : static_cast(mWrapper->client().progress()); - + index triggerInput = static_cast(mInBuf[static_cast(mNumInputs) - mSpecialIndex - 2][0]); bool trigger = (mPreviousTrigger <= 0) && triggerInput > 0; mPreviousTrigger = triggerInput; @@ -408,21 +411,29 @@ public: if(!f->mData) return; auto w = static_cast(f->mData); SharedState& s = *w; - s->mInNRT = true; - s->mJobDone = false; - s->mCancelled = false; Result result = validateParameters(s->params); if (!result.ok()) { std::cout << "ERROR: " << Wrapper::getName() << ": " << result.message().c_str() << std::endl; + s->mInNRT = false; return; } s->client.setSynchronous(s->mSynchronous); - s->client.enqueue(s->params); - s->mResult = s->client.process(); + result = s->client.enqueue(s->params); + if (!result.ok()) + { + std::cout << "ERROR: " << Wrapper::getName() << ": " + << result.message().c_str() << std::endl; + s->mInNRT = false; + return; + } + + s->mJobDone = false; + s->mCancelled = false; s->mHasTriggered = true; + s->mResult = s->client.process(); s->mInNRT = false; w->~SharedState(); f->mWorld->ft->fRTFree(f->mWorld,w); @@ -483,8 +494,8 @@ public: static bool exchangeBuffers(World* world, void* data) { if(!data) return false; - SharedState& s = *(static_cast(data)); + if(!s->mNodeAlive) return false; s->params.template forEachParamType(world); // At this point, we can see if we're finished and let the language know (or // it can wait for the doneAction, but that takes extra time) use replyID to @@ -1401,6 +1412,12 @@ public: impl::FluidSCWrapperBase::init(); } + ~FluidSCWrapper() + { + mState->mNodeAlive = false; + } + + std::shared_ptr>& state() { return mState; } From 3d02da998f4364a4b3fea9c3bdb34f73fce31d79 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 4 Jun 2020 15:50:07 +0100 Subject: [PATCH 144/550] FluidBufNMF: Fix Ugen construction and trigger argument passing from *kr --- release-packaging/Classes/FluidBufNMF.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 4ffcd7b..3b7dc09 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -18,7 +18,7 @@ FluidBufNMF : UGen { } *kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1| - ^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig); + ^this.new1(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig:trig); } From f9fb3f869df6d574a6de6577caacbc6b45fbb418 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 4 Jun 2020 18:44:02 +0100 Subject: [PATCH 145/550] Harmonize Dictionary layout with Max --- .../Classes/FluidCorpusBuilders.sc | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 005129b..b7524e7 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -18,7 +18,6 @@ FluidLoadFolder { maxChan = channels[channels.maxIndex]; counter = 0; index = IdentityDictionary(); - files.postln; forkIfNeeded{ buffer = Buffer.alloc(server,sizes.reduce('+'),maxChan); server.sync; @@ -32,9 +31,9 @@ FluidLoadFolder { { label = labelFunc.value(path,i) } { label = (f.path.basename).asSymbol }; entry = IdentityDictionary(); - entry.add(\points->startEnd[i]); - entry.add(\channels->f.numChannels); - entry.add(\sampleRate->f.sampleRate); + entry.add(\bounds->startEnd[i]); + entry.add(\numchans->f.numChannels); + entry.add(\sr->f.sampleRate); index.add(label->entry); counter = counter + 1; if(counter == (files.size)) {action !? action.value(index)}; @@ -58,7 +57,7 @@ FluidSliceCorpus { } play{ |server,sourceBuffer,bufIdx, action| - var counter, tmpIndices,perf,jobs,total,uid, completed; + var counter, tmpIndices,perf,jobs,total,uid, completed, pointstotal; uid = UniqueID.next; sourceBuffer ?? {"No buffer to slice".error; ^nil}; bufIdx ?? {"No slice point dictionary passed".error;^nil}; @@ -68,6 +67,7 @@ FluidSliceCorpus { completed = 0; jobs = List.newFrom(bufIdx.keys); total = jobs.size; + pointstotal = 0; perf = { |tmpIndices| var idx,v,k = jobs.pop; v = bufIdx[k]; @@ -78,24 +78,32 @@ FluidSliceCorpus { completed = completed + 1; ("FluidSliceCorpus:" + ( completed.asString ++ "/" ++ total)).postln; if(a[0] != -1){ - var slicePoints = Array.newFrom(a).slide(2).clump(2); + var rawPoints,slicePoints; + rawPoints = Array.newFrom(a).asInteger; + if(rawPoints[0] != [v[\bounds][0]]){rawPoints = [v[\bounds][0]] ++ rawPoints}; + if(rawPoints.last != [v[\bounds][1]]){rawPoints=rawPoints ++ [v[\bounds][1]]}; + + slicePoints = Array.newFrom(rawPoints).slide(2).clump(2); slicePoints.do{|s,j| - var label = (k ++ j).asSymbol; - index.add(label->IdentityDictionary(proto:v)); - index.at(label).add(\points->s); + var dict,label = (k ++ j).asSymbol; + dict = IdentityDictionary(); + dict.putAll(v); + dict[\bounds] = s; + index.add(label->dict); } }{ - index.add((k ++ '0').asSymbol->IdentityDictionary(proto:v)); + var dict = IdentityDictionary(); + dict.putAll(v); + index.add((k ++ '0').asSymbol->dict); }; if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; if(completed == total) {action !? action.value(index)}; }) },'/doneslice' ++ uid ++ counter,server.addr).oneShot; - { var numframes,onsets; - numframes = v[\points].reverse.reduce('-'); - onsets = sliceFunc.value(sourceBuffer, v[\points][0],numframes,tmpIndices); + numframes = v[\bounds].reverse.reduce('-'); + onsets = sliceFunc.value(sourceBuffer, v[\bounds][0],numframes,tmpIndices); SendReply.kr(Done.kr(onsets),'/doneslice' ++ uid ++ idx); FreeSelfWhenDone.kr(onsets); }.play; @@ -139,8 +147,8 @@ FluidProcessSlices{ { var numframes,feature; - numframes = v[\points].reverse.reduce('-'); - feature = featureFunc.value(sourceBuffer, v[\points][0],numframes,idx); + numframes = v[\bounds].reverse.reduce('-'); + feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,idx); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); From f3f1d2d2c204788560a112adae30d7304bf3e662 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 5 Jun 2020 10:15:44 +0100 Subject: [PATCH 146/550] check for 0 size and use sexy doAdjacentPairs in slicer util --- .../Classes/FluidCorpusBuilders.sc | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index b7524e7..6ee31f1 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -75,26 +75,29 @@ FluidSliceCorpus { idx = counter; OSCFunc({ tmpIndices.loadToFloatArray(action:{ |a| + var sliceindex = 1; completed = completed + 1; ("FluidSliceCorpus:" + ( completed.asString ++ "/" ++ total)).postln; if(a[0] != -1){ - var rawPoints,slicePoints; - rawPoints = Array.newFrom(a).asInteger; + var rawPoints = Array.newFrom(a).asInteger; if(rawPoints[0] != [v[\bounds][0]]){rawPoints = [v[\bounds][0]] ++ rawPoints}; if(rawPoints.last != [v[\bounds][1]]){rawPoints=rawPoints ++ [v[\bounds][1]]}; - slicePoints = Array.newFrom(rawPoints).slide(2).clump(2); - slicePoints.do{|s,j| - var dict,label = (k ++ j).asSymbol; - dict = IdentityDictionary(); - dict.putAll(v); - dict[\bounds] = s; - index.add(label->dict); + rawPoints.postln; + rawPoints.doAdjacentPairs{|a,b| + var dict; + if ((b - a) >= 1){ + dict = IdentityDictionary(); + dict.putAll(v); + dict[\bounds] = [a,b]; + index.add(((k ++ "-" ++sliceindex).asSymbol)->dict); + sliceindex = sliceindex + 1; + } } }{ var dict = IdentityDictionary(); dict.putAll(v); - index.add((k ++ '0').asSymbol->dict); + index.add((k ++ "-1").asSymbol->dict); }; if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; if(completed == total) {action !? action.value(index)}; From ab8f45141896ab98dcf8990e2785df2197a10be0 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 5 Jun 2020 10:42:06 +0100 Subject: [PATCH 147/550] remove troubleshooting printing --- release-packaging/Classes/FluidCorpusBuilders.sc | 1 - 1 file changed, 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 6ee31f1..d6698b3 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -83,7 +83,6 @@ FluidSliceCorpus { if(rawPoints[0] != [v[\bounds][0]]){rawPoints = [v[\bounds][0]] ++ rawPoints}; if(rawPoints.last != [v[\bounds][1]]){rawPoints=rawPoints ++ [v[\bounds][1]]}; - rawPoints.postln; rawPoints.doAdjacentPairs{|a,b| var dict; if ((b - a) >= 1){ From 4893a53d1d7f90e0dc5d7740a27db90d715435fe Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 5 Jun 2020 12:36:03 +0100 Subject: [PATCH 148/550] sorted the helper's dataset label --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index d6698b3..5feb958 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -150,7 +150,7 @@ FluidProcessSlices{ { var numframes,feature; numframes = v[\bounds].reverse.reduce('-'); - feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,idx); + feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,k); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); From 6f68cf4e2d1decda83b57f4766ee815efd3c8021 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 12:56:33 +0100 Subject: [PATCH 149/550] Add FluidLoadFolder help --- .../HelpSource/Classes/FluidLoadFolder.schelp | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidLoadFolder.schelp diff --git a/release-packaging/HelpSource/Classes/FluidLoadFolder.schelp b/release-packaging/HelpSource/Classes/FluidLoadFolder.schelp new file mode 100644 index 0000000..1697203 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidLoadFolder.schelp @@ -0,0 +1,78 @@ +TITLE:: FluidLoadFolder +summary:: Load a folder of audio into a single buffer +categories:: FluidCorpusManipulation +related:: Classes/FluidSliceCorpus, Classes/FluidProcessSlices + +DESCRIPTION:: +Given a path name, load every audio file in the path (using link::Classes/SoundFile#*collect::), allocate a link::Classes/Buffer::, and load the files top to tail into the buffer. Information about start stop points, channel counts and sample rates is then stored in link::Classes/FluidLoadFolder#index:: as a link::Classes/IdentityDictionary::. + + +CLASSMETHODS:: + +METHOD:: new +Construct a new instance + +ARGUMENT:: path +A string pointing to a folder on disk + +ARGUMENT:: labelFunc +A function that determines how the chunks in the index are labelled; default is file name without path. + +ARGUMENT:: channelFunc +A funciton that controls what to do with differently wide files. Default behaviour is for loaded buffer to have as many channels as the widest file in the path, and for narrower files to repeat their channels across this buffer. The funciton is passed the channels for the current file, the maximum channel count, and the current index. + +INSTANCEMETHODS:: + +METHOD:: index +A link::Classes/IdentityDictionary:: containing the metadata on the loaded files + +The keys of this dictionary are the labels produced by the code::labelFunc:: passed to link::Classes/FluidLoadFolder#*new:: (or the default of the filename if nil). The value for each key is a further dictionary consisting of: + +definitionlist:: +## bounds +|| A two element array giving the start and end point of this files eventual position in the overall buffer (in samples). +## sr +|| The sampling rate of the original file +## numchans +|| Number of channels in the original file +:: + +METHOD:: files +The list of files loaded + +METHOD:: buffer +The buffer in which the files are placed, end to end. + +METHOD:: play +Load the files. + +ARGUMENT:: server +The server on which to run + +ARGUMENT:: action +Function to execute on completion + + +EXAMPLES:: + +code:: +s.reboot; + +( +// We'll load all the Fluid Corpus Manipulation audio example files +~path = File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"; + +~loader = FluidLoadFolder(~path); + +~loader.play(s,action:{ |dataDictionary| + ("Done loading into" + ~loader.buffer).postln; + //we get passed an IdentityDictionary of slice data, let's look at it + dataDictionary.pairsDo{|label,data| + //data is also a dictionary + (label ++ '(').post; + data.pairsDo{|k,v| (k ++ ':' + v + ' ').post }; + ')'.postln; + } +}); +) +:: From 066a6f3affb919cb2df9dd009a0384d378f3bdda Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 15:02:34 +0100 Subject: [PATCH 150/550] Add FluidSliceCorpus help --- .../Classes/FluidSliceCorpus.schelp | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp diff --git a/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp b/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp new file mode 100644 index 0000000..c69722b --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp @@ -0,0 +1,107 @@ +TITLE:: FluidSliceCorpus +summary:: A utility for batch slicing of a corpus +categories:: FluidManipulation +related:: Classes/FluidLoadFolder, Classes/FluidProcessSlices, Classes/FluidBufOnsetSlice, Classes/FluidBufNoveltySlice, Classes/FluidBufTransientSlice + +DESCRIPTION:: +A utility class that abstracts the boiler plate code involved with batch slicing a buffer containing distinct chunks of audio (a 'corpus' for these purposes). + +Whilst this class is designed to be used most easily in conjunction with link::Classes/FluidLoadFolder::, it doesn't have to be. However, it does excpect to be passed an link::Classes/IdentityDictionary:: of a particular format (see link::#indexFormat#description below::). + +The actual mechanism for doing the slicing is provided by the user, in the form of a function that will form part of a larger link::Classes/Synth:: (see link::#sliceFuncDescription#below::). + +CLASSMETHODS:: + +METHOD:: new +Create a new instance with the specified slicing and labelling behaviour. + +ARGUMENT:: sliceFunc +ANCHOR::sliceFuncDescription:: +A function that does the slicing, returning a link::Classes/UGen::. This function is passed the following arguments: +definitionlist:: +## src +|| The source link::Classes/Buffer:: for slicing +## start +|| The frame to start slicing from, in samples +## num +|| The number of frames to slice, in samples +## dst +|| The destination link::Classes/Buffer:: into which to write slice indices +:: + +This configuration assumes that you are using one of the link::Guides/FluidDecomposition:: buffer-based slicing objects, or at least following their conventions, notably: +list:: +## slice points are written into a buffer as sample positions. +## If no slices are found, then a single value of -1 is written instead +:: + +warning:: +This function strong::must:: return a link::Classes/UGen:: that sets a code::done:: flag (see link::Classes/Done::), in order for the iteration and housekeeping to work. All code::FluidBuf*:: objects do this. +:: + +A concrete example of a code::sliceFunc:: could be: +code:: +~sliceFn = { |src,start,num,dest| + FluidBufOnsetSlice.kr(src,start,num,indices:dest) +}); +:: + +ARGUMENT:: labelFunc +ANCHOR::labelling:: +warning::Not yet implemented:: + +Override the default labelling behaviour for slices. The default is to append the original label with code::-:: counting from 1. + +INSTANCEMETHODS:: + +METHOD:: sliceFunc +Retreive the link::#sliceFuncDescription#slicing function:: used by this instance. + +METHOD:: play +Run the slicing function over each entry in the supplied link::#indexformat#index dictionary:: + +ARGUMENT:: server +The link::Classes/Server:: on which to execute + +ARGUMENT:: sourceBuffer +The link::Classes/Buffer:: containing the audio to slice + +ARGUMENT:: bufIdx +ANCHOR::indexformat:: +An link::Classes/IdentityDictionary:: that details labels and start-end positions for each chunk in the source buffer. See link::Classes/FluidLoadFolder#index:: + +ARGUMENT:: action +A function that runs on complettion, will be passed the link::Classes/IdentityDictionary:: from link::#index:: as an argument. + +METHOD:: index +A link::Classes/IdentityDictionary:: containing information about the position of each discovered slice, using labels based on those passed into link::#play:: (see link::#labelling::). This dictionary copies all other entries from the source dictionary on a per-key basis (so you can store arbitary stuff in there should you wish, and it will remain oassciated with its original source chunk). + + +EXAMPLES:: + +code:: +s.reboot + +( +~path = File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"; +~loader = FluidLoadFolder(~path); +~loader.play(s,action:{ |dataDictionary| "Done loading".postln}); +) + +~slicer = FluidSliceCorpus({ |src,start,num,dest| + FluidBufOnsetSlice.kr(src,start,num,indices:dest, threshold:0.7) +}); + +~slicer.play(s,~loader.buffer,~loader.index,{|dataDictionary| + "Slicing done".postln; + //we get passed an IdentityDictionary of slice data, let's look at it + dataDictionary.pairsDo{|label,data| + //data is also a dictionary + (label ++ '(').post; + data.pairsDo{|k,v| (k ++ ':' + v + ' ').post }; + ')'.postln; + } +}); + + +:: From 14c36d8f345702808cded0ceae210d48a072aaa2 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 5 Jun 2020 15:51:51 +0100 Subject: [PATCH 151/550] draft of the commented datasetmaker utilities demo --- .../dataset/demo-dataset-maker-utilities.scd | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd new file mode 100644 index 0000000..2fcdf8c --- /dev/null +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -0,0 +1,165 @@ +// define a few processes +( +~ds = FluidDataSet(s,\test); // still need a name on the server to make sure we do not forget it exists. it is now permanent aka will resist cmd+. +~mfccbuf = Buffer.new; +~statsbuf = Buffer.new; +~flatbuf = Buffer.new; + +// here we instantiate a loader which creates a single large buffer with a dictionary of what was included in it +// ~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/smallnum/"); +~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/segments/"); + +// here we instantiate a further slicing step if needs be, which iterate through all the items of the FluidLoadFolder and slice the slices with the declared function. Here it is a very picky onset slicer +~slicer = FluidSliceCorpus({ |src,start,num,dest| + FluidBufOnsetSlice.kr(src,start,num,metric: 9, minSliceLength: 17, indices:dest, threshold:2) +}); + +// here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers. +~extractor = FluidProcessSlices({|src,start, num, idx| + var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf; + mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf,trig:1); + stats = FluidBufStats.kr(~mfccbuf,stats:~statsbuf,trig:Done.kr(mfcc)); + flatten = FluidBufFlatten.kr(~statsbuf,~flatbuf,trig:Done.kr(stats)); + writer = FluidDataSetWr.kr(idx,~flatbuf,~ds,trig:Done.kr(flatten)) +}); +) + + +////////////////////////////////////////////////////////////////////////// +//loading process + +// just run the loader +( +t = Main.elapsedTime; +~loader.play(s,action:{(Main.elapsedTime - t).postln;"Loaded".postln;}); +) + +//load and play to test if it is that quick - it is! +( +t = Main.elapsedTime; +~loader.play(s,action:{(Main.elapsedTime - t).postln;"Loaded".postln;{var start, stop; PlayBuf.ar(~loader.index[~loader.index.keys.asArray.last.asSymbol][\numchans],~loader.buffer,startPos: ~loader.index[~loader.index.keys.asArray.last.asSymbol][\bounds][0])}.play;}); +) + +//ref to the buffer +~loader.buffer +//size of item +~loader.index.keys.size +//a way to get all keys info sorted by time +~stuff = Array.newFrom(~loader.index.keys).sort.collect{|x|~loader.index[x][\bounds]}.sort{|a,b| a[0]u).postln; + } +} +) + +// or write to file a human readable, sorted version of the database after sorting it by index. +( +a = File("/Users/pa/Desktop/sc-loading.json","w"); +~stuffsorted = Array.newFrom(~loader.index.keys).sort{|a,b| ~loader.index[a][\bounds][0]< ~loader.index[b][\bounds][0]}.do{|k| + v = ~loader.index[k]; + a.write(k.asString ++ "\n"); + v.pairsDo{|l,u,j| + a.write("\t\t\t" ++ (l->u).asString ++ "\n"); + } +}; +a.close; +) + +////////////////////////////////////////////////////////////////////////// +// slicing process + +// just run the slicer +( +t = Main.elapsedTime; +~slicer.play(s,~loader.buffer,~loader.index,action:{(Main.elapsedTime - t).postln;"Slicing done".postln}); +) + +//slice count +~slicer.index.keys.size + +// iterate +( +~slicer.index.pairsDo{ |k,v,i| + k.postln; + v.pairsDo{|l,u,j| + "\t\t\t".post; + (l->u).postln; + } +} +) + +///// write to file in human readable format, in order. +( +a = File("/Users/pa/Desktop/sc-spliting.json","w"); +~stuffsorted = Array.newFrom(~slicer.index.keys).sort{|a,b| ~slicer.index[a][\bounds][0]< ~slicer.index[b][\bounds][0]}.do{|k| + v = ~slicer.index[k]; + a.write(k.asString ++ "\n"); + v.pairsDo{|l,u,j| + a.write("\t\t\t" ++ (l->u).asString ++ "\n"); + } +}; +a.close; +) + +////////////////////////////////////////////////////////////////////////// +// description process + +// just run the descriptor extractor +( +t = Main.elapsedTime; +~extractor.play(s,~loader.buffer,~slicer.index,action:{(Main.elapsedTime - t).postln;"Features done".postln}); +) + +// write the dataset to file with the native JSON +~ds.write("/tmp/sc-dataset.json") +~ds.clear + +// open the file in your favourite json editor +"open /tmp/sc-dataset.json".unixCmd + +////////////////////////////////////////////////////////////////////////// +// manipulating and querying the data + +//building a tree +~tree = FluidKDTree(s); +~tree.fit(~ds,{"Fitted".postln;}); + +//retrieve a sound to match +~targetsound = Buffer(s); +~targetname = ~slicer.index.keys.asArray.last.asSymbol; +#a,b = ~slicer.index[~targetname][\bounds]; +FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targetsound,action: {~targetsound.play;}) + +//describe the sound to match +( +{ + var mfcc, stats, flatten; + mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf,trig:1); + stats = FluidBufStats.kr(~mfccbuf,stats:~statsbuf,trig:Done.kr(mfcc)); + flatten = FluidBufFlatten.kr(~statsbuf,~flatbuf,trig:Done.kr(stats)); +}.play; +) + +//find its nearest neighbours +~friends = Array; +~tree.kNearest(~flatbuf,5,{|x| ~friends = x.postln;}) + +// play them in a row +( +Routine{ +5.do{|i| + var dur; + v = ~slicer.index[~friends[i].asSymbol]; + dur = (v[\bounds][1] - v[\bounds][0]) / s.sampleRate; + {BufRd.ar(v[\numchans],~loader.buffer,Line.ar(v[\bounds][0],v[\bounds][1],dur, doneAction: 2))}.play; + ~friends[i].postln; + dur.wait; + }; +}.play; +) \ No newline at end of file From a2ca1e68d4f1f61c01a9ca9b46ab92c713dee841 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 17:50:30 +0100 Subject: [PATCH 152/550] Amend index to label in FluidDataSetWr --- release-packaging/Classes/FluidDataSetWr.sc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidDataSetWr.sc b/release-packaging/Classes/FluidDataSetWr.sc index bb239fa..0787ed6 100644 --- a/release-packaging/Classes/FluidDataSetWr.sc +++ b/release-packaging/Classes/FluidDataSetWr.sc @@ -6,7 +6,7 @@ FluidDataSetWr : UGen { ++buf.asUGenInput++FluidDataSet.asUGenInput(dataset.asSymbol)++trig++blocking)); } - *kr { |index, buf, dataset,trig=1| - ^this.new1(\control,index,buf,dataset,trig,1) + *kr { |label, buf, dataset,trig=1| + ^this.new1(\control,label,buf,dataset,trig,1) } } From 361dd8ccbad225a0c0e07d51680df2d48ba7d816 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 17:50:43 +0100 Subject: [PATCH 153/550] Add FluidDataSetWr help --- .../HelpSource/Classes/FluidDataSetWr.schelp | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidDataSetWr.schelp diff --git a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp new file mode 100644 index 0000000..b066740 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp @@ -0,0 +1,43 @@ +TITLE:: FluidDataSetWr +summary:: Write to FluidDataSet on the server +categories:: FluidManipulation +related:: Classes/FLuidDataSet + +DESCRIPTION:: +A UGen that writes to a link::Classes/FluidDataSet:: + +CLASSMETHODS:: + +private:: *new1 + +METHOD:: kr +The equivalent of calling link::Classes/FluidDataSet#-addPoint::, but within a link::Classes/Synth:: + +ARGUMENT:: label +A symbol for the label of the new point + +ARGUMENT:: buf +The link::Classes/Buffer:: containing the data point. + +ARGUMENT:: dataset +An instance of link::Classes/FluidDataSet:: or an instance's name. + +ARGUMENT:: trig +A kr trigger signal + +EXAMPLES:: + +code:: +s.reboot; +( +~ds = FluidDataSet(s,\FluidDataSetWr); +) + +{ + var b = LocalBuf.newFrom([0,1,2,3]); + FreeSelfWhenDone.kr(FluidDataSetWr.kr("help_data_point",b,~ds)); +}.play(s); + +~ds.print; + +:: From 54e6cf98a8e5617e61b07fdf2238251c90891872 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 18:41:45 +0100 Subject: [PATCH 154/550] Pass all the goodies from FluidProcessSlices --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 5feb958..f5a5578 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -150,7 +150,7 @@ FluidProcessSlices{ { var numframes,feature; numframes = v[\bounds].reverse.reduce('-'); - feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,k); + feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,k,v,counter-1); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); From fee544adfab8033ddbc0d8f0a5c03d90a23e36ff Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 18:42:03 +0100 Subject: [PATCH 155/550] Add FluidProcessSlices help --- .../Classes/FluidProcessSlices.schelp | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidProcessSlices.schelp diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp new file mode 100644 index 0000000..fc4301a --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -0,0 +1,96 @@ +TITLE:: FluidProcessSlices +summary:: Utility for batch processing slices +categories:: FluidManipulation +related:: Classes/FluidLoadFolder, Classes/FluidSliceCorpus,Guides/FluidDecomposition + +DESCRIPTION:: +This class abstracts some of the boilerplate involved in batch processing a sequence of segments in a link::Classes/Buffer:: on the server. It does this by iteratively running a user supplied function and using slice point information passed as an link::Classes/IdentityDictionary:: (see link::Classes/FluidLoadFolder#-index:: for details on the format of this). + + +CLASSMETHODS:: + +METHOD:: new +Creates a new instance + +ARGUMENT:: featureFunc +ANCHOR::featureFunction:: +A function that will perform some processing on a section of a buffer. It is passed the following arguments +definitionlist:: +##src +|| The source link::Classes/Buffer:: containing the audio to process +##start +|| The start frame of the section to process, in samples +##num +|| The number of frames to process, in samples +##label +|| The label for the segment from the supplied dictionary to link::#-play:: +:: + +warning:: +This function strong::must:: return a link::Classes/UGen:: that sets a code::done:: flag (see link::Classes/Done::), in order for the iteration and housekeeping to work. All code::FluidBuf*:: objects do this. +:: + +An example function that records statistics about the pitch of a segment in to a link::Classes/FluidDataSet:: could look like + +code:: +~avgPitch = { |src,start,num,label| + var pitch, stats,statsbuf; + statsbuf = LocalBuf(7); + pitch = FluidBufPitch.kr(src,start,num,features:~someotherbuffer); + stats = FluidBufStats.kr(~someotherbuffer,stats:statsbuf,trig:Done.kr(pitch)); + FluidDataSetWr.kr(label,statsbuf,~mydataset,Done.kr(stats)) +} +:: + +INSTANCEMETHODS:: + +METHOD:: play +Run the link::#featureFunction:: iteratively over segments of a link::Classes/Buffer::, specified by an link::Classes/IdentityDictionary:: + +ARGUMENT:: server +The link::Classes/Server:: on which to process + +ARGUMENT:: sourceBuffer + The source link::Classes/Buffer:: containing the audio to process + +ARGUMENT:: bufIdx +An link::Classes/IdentityDictionary:: specifying labels, boundaries, sample rate and channel count for the segment. See link::Classes/FluidLoadFolder#-index:: for details. + +ARGUMENT:: action +A function to run when processing is complete + +METHOD:: featureFunc +Return the function uses by this instance. + +EXAMPLES:: + +code:: +s.reboot; +//Load all the Fluid Corpus Manipulation audio f +( +~path = File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"; +~loader = FluidLoadFolder(~path); +~loader.play(s,action:{ |dataDictionary| "Done loading".postln}); +~slicer = FluidSliceCorpus({ |src,start,num,dest| + FluidBufOnsetSlice.kr(src,start,num,indices:dest, threshold:2) +}); +~pitchdata = FluidDataSet(s,\FluidProcessSlicesHelp); +~pitchbufs = 4.collect{Buffer.new}; +~statsbufs = 4.collect{Buffer.new}; +) +//segment +~slicer.play(s,~loader.buffer,~loader.index,{|dataDictionary| "Slicing done".postln;}); + +//In the interests of brevity, let's just take a subset of the slices and process these +~subset = IdentityDictionary.newFrom(~slicer.index.asSortedArray[0..3].flatten(1)); +//write pitch statistics into a dataset +~extractor = FluidProcessSlices({|src,start,num,label,data,i| + var pitch, stats; + pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); + stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); + FluidDataSetWr.kr(label,~statsbufs[i],~pitchdata,Done.kr(stats)) +}); +~extractor.play(s,~loader.buffer,~subset,{"Feature extraction done".postln}); +//view the data +~pitchdata.print +:: From c1be03b8dbc29e2e3c2370603eb98e0f1e128f9c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 19:01:05 +0100 Subject: [PATCH 156/550] Add FluidBufFlatten help --- .../HelpSource/Classes/FluidBufFlatten.schelp | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 release-packaging/HelpSource/Classes/FluidBufFlatten.schelp diff --git a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp new file mode 100644 index 0000000..a70f609 --- /dev/null +++ b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp @@ -0,0 +1,91 @@ +TITLE:: FluidBufFlatten +summary:: Flatten a multichannel buffer on the server +categories:: FluidCorpusManipulation +related:: Classes/Buffer + +DESCRIPTION:: +Flatten a multichannel link::Classes/Buffer:: to a single channel. This can be useful for constructing n-dimensional data points for use with link::Classes/FluidDataSet:: + +The code::axis:: determines how the flattening is arranged. The default value, 1, flattens channel-wise, such that (if we imagine channels are rows, time positions are columns): + +table:: + ## a 1 || a 2 || a 3 + ## b 1 || b 2 || b 3 + ## c 1 || c 2 || c 3 +:: + +becomes + +table:: +## a 1 || b 1 || c 1 || a 2 || b 2 || c 2 || a 3 || b 3 || c 3 +:: + +whereas with code::axis = 0:: we get + +table:: + ## a 1 || a 2 || a 3 || b 1 || b 2 || b 3 || c 1 || c 2 || c 3 +:: + + +CLASSMETHODS:: + +private::new1 + +METHOD:: process, processBlocking + +Run the process on the given sever, and perfrom code::action:: when done + +ARGUMENT:: server +The link::Classes/Server:: on which to run + +ARGUMENT:: source +The link::Classes/Buffer:: to flatten + +ARGUMENT:: destination +The link::Classes/Buffer:: to write the flattened data to + +ARGUMENT:: axis +Whether to group points channel-wise or frame-wise + +ARGUMENT:: action +Runs when processing is complete + +METHOD:: kr +Run as a control rate link::Classes/UGen:: + +ARGUMENT:: source +The link::Classes/Buffer:: to flatten + +ARGUMENT:: destination +The link::Classes/Buffer:: to write the flattened data to + +ARGUMENT:: axis +Whether to group points channel-wise or frame-wise + +ARGUMENT:: trig +Trigger signal to defer / retrigger processing + +EXAMPLES:: + +code:: +//FluidBufPitch is useful to illustrate the effect of this, because the pitch and confidence values are easily distinguishable + +( +~path = File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"; +~randomsoundfile = SoundFile.collect(~path +/+ '*').choose; +b = Buffer.read(s,~randomsoundfile.path,action:{"Sound Loaded".postln}); +~pitchdata = Buffer.new; +~flatdata = Buffer.new; +) + +//Pitch analysis, writes pitches as frequecnies to chan 0, confidences [0-1] to chan 1 +FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln}); + +// Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1) +FluidBufFlatten.process(s,~pitchdata,~flatdata,action:{ + ~flatdata.loadToFloatArray(action:{ |a| + a.postln; + }) +}) + +:: \ No newline at end of file From aec77e0b147c65fbacc15a16cc862fa1e6eed696 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 5 Jun 2020 19:04:03 +0100 Subject: [PATCH 157/550] adding FluidManipulationJSON --- .../Classes/FluidManipulationJSON.sc | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 release-packaging/Classes/FluidManipulationJSON.sc diff --git a/release-packaging/Classes/FluidManipulationJSON.sc b/release-packaging/Classes/FluidManipulationJSON.sc new file mode 100644 index 0000000..216e577 --- /dev/null +++ b/release-packaging/Classes/FluidManipulationJSON.sc @@ -0,0 +1,40 @@ ++ FluidManipulationClient { + tmpJSONFilename{ + ^Platform.defaultTempDir++"tmp_fluid_dataset_"++ + Date.localtime.stamp++".json"; + } + + dump {|action| + var filename = this.tmpJSONFilename; + action ?? {action = postit}; + this.write(filename, { + action.value(filename.parseYAMLFile); + File.delete(filename); + }); + } + + load{|dict, action| + var filename = this.tmpJSONFilename; + var str = this.asJSON(dict); + File.use(filename, "w", { |f| f.write(this.asJSON(dict));}); + this.read(filename, { + action.notNil.if{ action.value }; + File.delete(filename); + }); + } + + asJSON{|d| + if(d.isString || d.isNumber){^d}; + if(d.isKindOf(Dictionary), + { + ^"{" ++ ( + d.keys.asList.collect{|k| + k.asString.asCompileString ++ ":" + this.asJSON(d[k]) + }).join(", ") ++ "}" + }); + if(d.isKindOf(SequenceableCollection), + { + ^"[" ++ d.collect({|x|this.asJSON(x)}).join(", ")++ "]"; + }); + } +} From 5df8513bd66a9c5485acd219b9d18bb24ecf7f8e Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 19:29:03 +0100 Subject: [PATCH 158/550] Make help file actually work --- .../HelpSource/Classes/FluidKDTree.schelp | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index 21dbc79..65933da 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -83,26 +83,25 @@ EXAMPLES:: code:: //Make some 2D points and place into a dataset +s.reboot; ( ~points = 100.collect{ [ 1.0.linrand,1.0.linrand] }; ~dataset= FluidDataSet(s,\kdtree_help_rand2d); -~dataset.free -~tmpbuf = Buffer.alloc(s,2) ; +~tmpbuf = Buffer.alloc(s,2); fork{ - ~dataset.ready.wait; - ~points.do{|x,i| - (""++(i+1)++"/100").postln; - ~tmpbuf.setn(0,x); - ~dataset.addPoint(i,~tmpbuf); - s.sync + s.bind{ + ~dataset.ready.wait; + ~points.do{|x,i| + (""++(i+1)++"/100").postln; + ~tmpbuf.setn(0,x); + ~dataset.addPoint(i,~tmpbuf); + s.sync + }; + "Data loaded".postln; } } ) - - - - //Make a new tree, and fit it to the dataset ( fork{ From 138f6011e5faec2a5c8a2964f1f17717aa20552d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 5 Jun 2020 19:41:21 +0100 Subject: [PATCH 159/550] Make KDTree help more helpful --- .../HelpSource/Classes/FluidKDTree.schelp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index 65933da..bf3dd11 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -117,8 +117,23 @@ fork{ //Return labels of k nearest points to new data ( -~tmpbuf.setn(0,[ 1.0.linrand,1.0.linrand ]); -~tree.kNearest(~tmpbuf,5, { |a| a.postln }); +~testpoint = [ 1.0.linrand,1.0.linrand ]; +("\n\nTest point:" + ~testpoint).postln; +~tmpbuf.setn(0,~testpoint); +fork{ + ~tree.kNearest(~tmpbuf,5, { |a| + ("Labels of nearest points" + a).postln; + "Nearest points".postln; + a.do{|l| + ~dataset.getPoint(l,~tmpbuf,action:{ + ~tmpbuf.loadToFloatArray(action:{ |point| + point.postln; + }) + }); + s.sync; + } + }); +} ) //or the distances From b67d7006b1a39cca4f5872f1cb386727505056d5 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Sat, 6 Jun 2020 15:10:24 +0100 Subject: [PATCH 160/550] Manipulation client: make server transaction in new more predictable --- .../Classes/FluidManipulationClient.sc | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 40cd442..7dd8d1f 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -19,13 +19,11 @@ FluidProxyUgen : UGen { FluidManipulationClient { var ugen; - var id; + var ugen; + var id; var defName, def; - var onSynthFree, persist; - + var onSynthFree, keepAlive; var postit; - var < ready; *prServerString{ |s| var ascii = s.ascii; @@ -36,7 +34,6 @@ FluidManipulationClient { ^FluidProxyUgen.newFromDesc(rate, numOutputs, inputs, specialIndex) } - *new{ |server...args| server = server ? Server.default; if(server.serverRunning.not,{ @@ -46,41 +43,36 @@ FluidManipulationClient { } baseinit { |...args| - var makeFirstSynth; + var makeFirstSynth,synthMsg; id = UniqueID.next; postit = {|x| x.postln;}; + keepAlive = true; defName = (this.class.name.asString ++ id).asSymbol; - ready = Condition(false); def = SynthDef(defName,{ var ugen = FluidProxyUgen.kr(this.class.name, *args); this.ugen = ugen; ugen }); + synth = Synth.basicNew(def.name, server); + synthMsg = synth.newMsg(RootNode(server)); + def.doSend(server,synthMsg); - persist = true; onSynthFree = { - ready.test = false; synth = nil; - if(persist){ + if(keepAlive){ //If we don't sync here, cmd-. doesn't reset properly (but server.freeAll does) forkIfNeeded { server.sync; synth = Synth(defName,target: RootNode(server)); synth.onFree{onSynthFree.value}; - ready.test = true; - ready.signal; } } }; - forkIfNeeded{ - def.add; - server.sync; - onSynthFree.value; - } + synth.onFree{onSynthFree.value}; } free{ - persist = false; + keepAlive = false; if(server.serverRunning){server.sendMsg("/cmd","free"++this.class.name,id)}; synth.tryPerform(\free); ^nil @@ -88,7 +80,6 @@ FluidManipulationClient { prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); - synth ?? {"Not ready".warn}; synth !? { OSCFunc( { |msg| From db1bbe778096ee73f80cc6ddc08229f06ad4f3bc Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Sun, 7 Jun 2020 15:55:02 +0100 Subject: [PATCH 161/550] i tried. and failed. please excuse me. --- .../Classes/FluidCorpusBuilders.sc | 7 +++--- .../dataset/demo-dataset-maker-utilities.scd | 25 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index f5a5578..81d5c0c 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -135,7 +135,7 @@ FluidProcessSlices{ total = jobs.size; counter = 0; completed = 0; - perf = { + perf = {|jobID| var idx,v, k = jobs.pop; v = bufIdx[k]; counter = counter + 1; @@ -150,11 +150,12 @@ FluidProcessSlices{ { var numframes,feature; numframes = v[\bounds].reverse.reduce('-'); - feature = featureFunc.value(sourceBuffer, v[\bounds][0],numframes,k,v,counter-1); + jobID.postln; + feature = featureFunc.value(sourceBuffer, v[\bounds][0], numframes, k, v, counter-1, jobID); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); }; - 4.do{perf.value}; + 4.do{|jobIDs|perf.value(jobIDs)}; } } diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 2fcdf8c..59a7fab 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -1,9 +1,9 @@ // define a few processes ( ~ds = FluidDataSet(s,\test); // still need a name on the server to make sure we do not forget it exists. it is now permanent aka will resist cmd+. -~mfccbuf = Buffer.new; -~statsbuf = Buffer.new; -~flatbuf = Buffer.new; +~mfccbuf = Buffer.allocConsecutive(4,s,1); +~statsbuf = Buffer.allocConsecutive(4,s,1); +~flatbuf = Buffer.allocConsecutive(4,s,1); // here we instantiate a loader which creates a single large buffer with a dictionary of what was included in it // ~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/smallnum/"); @@ -15,12 +15,13 @@ }); // here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers. -~extractor = FluidProcessSlices({|src,start, num, idx| +~extractor = FluidProcessSlices({|src,start, num, idx, a, b, i| var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf; - mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf,trig:1); - stats = FluidBufStats.kr(~mfccbuf,stats:~statsbuf,trig:Done.kr(mfcc)); - flatten = FluidBufFlatten.kr(~statsbuf,~flatbuf,trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(idx,~flatbuf,~ds,trig:Done.kr(flatten)) + [src,start, num, idx, a, b, i].postln; + mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[i],trig:1); + stats = FluidBufStats.kr(~mfccbuf[i],stats:~statsbuf[i],trig:Done.kr(mfcc)); + flatten = FluidBufFlatten.kr(~statsbuf[i],~flatbuf[i],trig:Done.kr(stats)); + writer = FluidDataSetWr.kr(idx,~flatbuf[i],~ds,trig:Done.kr(flatten)) }); ) @@ -140,15 +141,15 @@ FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targ ( { var mfcc, stats, flatten; - mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf,trig:1); - stats = FluidBufStats.kr(~mfccbuf,stats:~statsbuf,trig:Done.kr(mfcc)); - flatten = FluidBufFlatten.kr(~statsbuf,~flatbuf,trig:Done.kr(stats)); + mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf[0],trig:1); + stats = FluidBufStats.kr(~mfccbuf[0],stats:~statsbuf[0],trig:Done.kr(mfcc)); + flatten = FluidBufFlatten.kr(~statsbuf[0],~flatbuf[0],trig:Done.kr(stats)); }.play; ) //find its nearest neighbours ~friends = Array; -~tree.kNearest(~flatbuf,5,{|x| ~friends = x.postln;}) +~tree.kNearest(~flatbuf[0],5,{|x| ~friends = x.postln;}) // play them in a row ( From ab3e0e1376fa4aa54336675a33d1aba1ba4e501a Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 8 Jun 2020 10:17:23 +0100 Subject: [PATCH 162/550] Condense extra featureFunc args into assocation labe->dictionary; add 'tasks' argument to play Update help to reflect --- .../Classes/FluidCorpusBuilders.sc | 21 +++---- .../Classes/FluidProcessSlices.schelp | 56 +++++++++++++------ 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 81d5c0c..7313336 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -115,20 +115,18 @@ FluidSliceCorpus { } FluidProcessSlices{ - var < featureFunc, labelFunc; - var < index; + var < featureFunc; - *new { |featureFunc, labelFunc| - ^super.newCopyArgs(featureFunc,labelFunc); + *new { |featureFunc| + ^super.newCopyArgs(featureFunc); } - play{ |server,sourceBuffer,bufIdx, action| + play{ |server, sourceBuffer, bufIdx, action, tasks = 4| var counter,perf,jobs,total,uid, completed; sourceBuffer ?? {"No buffer to slice".error; ^nil}; bufIdx ?? {"No slice point dictionary passed".error;^nil}; server ?? {server = Server.default}; - index = IdentityDictionary(); uid = UniqueID.next; jobs = List.newFrom(bufIdx.keys); @@ -140,22 +138,25 @@ FluidProcessSlices{ v = bufIdx[k]; counter = counter + 1; idx = counter; + v[\index] = counter; + v[\voice] = jobID; OSCFunc({ completed = completed + 1; ("FluidProcessSlices:" + (completed.asString ++ "/" ++ total)).postln; - if(jobs.size > 0){perf.value}; - if(completed == total){action !? action.value(index);}; + if(jobs.size > 0){perf.value(jobID)}; + if(completed == total){action !? action.value(v);}; },"/doneFeature" ++ uid ++ counter,server.addr).oneShot; { var numframes,feature; numframes = v[\bounds].reverse.reduce('-'); jobID.postln; - feature = featureFunc.value(sourceBuffer, v[\bounds][0], numframes, k, v, counter-1, jobID); + feature = featureFunc.value(sourceBuffer, v[\bounds][0], numframes, k->v); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); }.play(server); }; - 4.do{|jobIDs|perf.value(jobIDs)}; + tasks ?? {tasks = 4}; + tasks.asInteger.min(jobs.size).do{|jobIDs|perf.value(jobIDs)}; } } diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index fc4301a..2240113 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -6,7 +6,6 @@ related:: Classes/FluidLoadFolder, Classes/FluidSliceCorpus,Guides/FluidDecompos DESCRIPTION:: This class abstracts some of the boilerplate involved in batch processing a sequence of segments in a link::Classes/Buffer:: on the server. It does this by iteratively running a user supplied function and using slice point information passed as an link::Classes/IdentityDictionary:: (see link::Classes/FluidLoadFolder#-index:: for details on the format of this). - CLASSMETHODS:: METHOD:: new @@ -14,7 +13,11 @@ Creates a new instance ARGUMENT:: featureFunc ANCHOR::featureFunction:: -A function that will perform some processing on a section of a buffer. It is passed the following arguments +A function that will perform some processing on a section of a buffer. +warning:: +This function strong::must:: return a link::Classes/UGen:: that sets a code::done:: flag (see link::Classes/Done::), in order for the iteration and housekeeping to work. All code::FluidBuf*:: objects do this. +:: +The functions is passed the following arguments definitionlist:: ##src || The source link::Classes/Buffer:: containing the audio to process @@ -22,24 +25,35 @@ definitionlist:: || The start frame of the section to process, in samples ##num || The number of frames to process, in samples -##label -|| The label for the segment from the supplied dictionary to link::#-play:: +##data +|| anchor::datadict:: An link::Classes/Association:: of the label for this segment, with an link::Classes/IdentityDictionary:: of useful extra data: +definitionlist:: +## sr +|| The original sample rate of the segment +## numchans +|| The original channel count of the segment +## voice +|| By default link::#-play:: will run multiple jobs in parallel dependning on the link::#ntasks#tasks:: argument. This contains the task number, which allows you to maintain separate set of resources (e.g. temporary link::Classes/Buffer::s) for each task. +## index +|| The absolute count of slices processed. :: - -warning:: -This function strong::must:: return a link::Classes/UGen:: that sets a code::done:: flag (see link::Classes/Done::), in order for the iteration and housekeeping to work. All code::FluidBuf*:: objects do this. :: An example function that records statistics about the pitch of a segment in to a link::Classes/FluidDataSet:: could look like code:: -~avgPitch = { |src,start,num,label| - var pitch, stats,statsbuf; + +~featureBuffers = 4.do{Buffer.new}; + +~avgPitch = { |src,start,num,data| + var pitch, stats,statsbuf,label,voice; + label = data.key; + voice = data.value[\voice]; statsbuf = LocalBuf(7); - pitch = FluidBufPitch.kr(src,start,num,features:~someotherbuffer); - stats = FluidBufStats.kr(~someotherbuffer,stats:statsbuf,trig:Done.kr(pitch)); + pitch = FluidBufPitch.kr(src,start,num,features:~featurebuffers[voice]); + stats = FluidBufStats.kr(~featurebuffers[voice],stats:statsbuf,trig:Done.kr(pitch)); FluidDataSetWr.kr(label,statsbuf,~mydataset,Done.kr(stats)) -} +}; :: INSTANCEMETHODS:: @@ -57,7 +71,11 @@ ARGUMENT:: bufIdx An link::Classes/IdentityDictionary:: specifying labels, boundaries, sample rate and channel count for the segment. See link::Classes/FluidLoadFolder#-index:: for details. ARGUMENT:: action -A function to run when processing is complete +A function to run when processing is complete. This gets passed the same link::Classes/Association:: as link::#datadict#the processing function:: + +ARGUMENT:: tasks +ANCHOR::ntasks:: +The number of parallel processing tasks to run on the server. Default 4. This should probably never be greater than the number of available CPU cores. METHOD:: featureFunc Return the function uses by this instance. @@ -84,11 +102,13 @@ s.reboot; //In the interests of brevity, let's just take a subset of the slices and process these ~subset = IdentityDictionary.newFrom(~slicer.index.asSortedArray[0..3].flatten(1)); //write pitch statistics into a dataset -~extractor = FluidProcessSlices({|src,start,num,label,data,i| - var pitch, stats; - pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); - stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); - FluidDataSetWr.kr(label,~statsbufs[i],~pitchdata,Done.kr(stats)) +~extractor = FluidProcessSlices({|src,start,num,data| + var pitch, stats, label,i; + i = data.value[\voice]; + label = data.key; + pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); + stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); + FluidDataSetWr.kr(label,~statsbufs[i],~pitchdata,Done.kr(stats)) }); ~extractor.play(s,~loader.buffer,~subset,{"Feature extraction done".postln}); //view the data From 14aec9832ae7027f2fb84f4f39034dfbb8caf62b Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Mon, 8 Jun 2020 10:57:28 +0100 Subject: [PATCH 163/550] sorted parallel iterations thanks to @weefuzzy --- .../Classes/FluidCorpusBuilders.sc | 1 - .../dataset/demo-dataset-maker-utilities.scd | 25 ++++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 7313336..30f128d 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -150,7 +150,6 @@ FluidProcessSlices{ { var numframes,feature; numframes = v[\bounds].reverse.reduce('-'); - jobID.postln; feature = featureFunc.value(sourceBuffer, v[\bounds][0], numframes, k->v); SendReply.kr(Done.kr(feature),'/doneFeature' ++ uid ++ idx); FreeSelfWhenDone.kr(feature); diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 59a7fab..3e03c5e 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -1,9 +1,10 @@ // define a few processes ( ~ds = FluidDataSet(s,\test); // still need a name on the server to make sure we do not forget it exists. it is now permanent aka will resist cmd+. -~mfccbuf = Buffer.allocConsecutive(4,s,1); -~statsbuf = Buffer.allocConsecutive(4,s,1); -~flatbuf = Buffer.allocConsecutive(4,s,1); +//define as many buffers as we have parallel voices/threads in the extractor processing (default is 4) +~mfccbuf = 4.collect{Buffer.new}; +~statsbuf = 4.collect{Buffer.new}; +~flatbuf = 4.collect{Buffer.new}; // here we instantiate a loader which creates a single large buffer with a dictionary of what was included in it // ~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/smallnum/"); @@ -15,13 +16,14 @@ }); // here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers. -~extractor = FluidProcessSlices({|src,start, num, idx, a, b, i| - var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf; - [src,start, num, idx, a, b, i].postln; - mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[i],trig:1); - stats = FluidBufStats.kr(~mfccbuf[i],stats:~statsbuf[i],trig:Done.kr(mfcc)); - flatten = FluidBufFlatten.kr(~statsbuf[i],~flatbuf[i],trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(idx,~flatbuf[i],~ds,trig:Done.kr(flatten)) +~extractor = FluidProcessSlices({|src,start,num,data| + var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice; + label = data.key; + voice = data.value[\voice]; + mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1); + stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc)); + flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats)); + writer = FluidDataSetWr.kr(label,~flatbuf[voice],~ds,trig:Done.kr(flatten)) }); ) @@ -119,9 +121,8 @@ t = Main.elapsedTime; // write the dataset to file with the native JSON ~ds.write("/tmp/sc-dataset.json") -~ds.clear -// open the file in your favourite json editor +// open the file in your default json editor "open /tmp/sc-dataset.json".unixCmd ////////////////////////////////////////////////////////////////////////// From fb64db6d50477154dabc2fea3de1098b7547e592 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Mon, 8 Jun 2020 11:22:49 +0100 Subject: [PATCH 164/550] typo corrections in new helpfiles --- .../HelpSource/Classes/FluidBufFlatten.schelp | 3 ++- .../HelpSource/Classes/FluidDataSetWr.schelp | 2 ++ .../HelpSource/Classes/FluidProcessSlices.schelp | 13 ++++++++++--- .../HelpSource/Classes/FluidSliceCorpus.schelp | 8 +++++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp index a70f609..1b744b0 100644 --- a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp @@ -82,10 +82,11 @@ b = Buffer.read(s,~randomsoundfile.path,action:{"Sound Loaded".postln}); FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,action:{"Pitch Analysis Done".postln}); // Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1) +( FluidBufFlatten.process(s,~pitchdata,~flatdata,action:{ ~flatdata.loadToFloatArray(action:{ |a| a.postln; }) }) - +) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp index b066740..7ec2a64 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp @@ -33,10 +33,12 @@ s.reboot; ~ds = FluidDataSet(s,\FluidDataSetWr); ) +( { var b = LocalBuf.newFrom([0,1,2,3]); FreeSelfWhenDone.kr(FluidDataSetWr.kr("help_data_point",b,~ds)); }.play(s); +) ~ds.print; diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index 2240113..98f89c1 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -49,7 +49,7 @@ code:: var pitch, stats,statsbuf,label,voice; label = data.key; voice = data.value[\voice]; - statsbuf = LocalBuf(7); + statsbuf = LocalBuf(7,4); pitch = FluidBufPitch.kr(src,start,num,features:~featurebuffers[voice]); stats = FluidBufStats.kr(~featurebuffers[voice],stats:statsbuf,trig:Done.kr(pitch)); FluidDataSetWr.kr(label,statsbuf,~mydataset,Done.kr(stats)) @@ -84,7 +84,8 @@ EXAMPLES:: code:: s.reboot; -//Load all the Fluid Corpus Manipulation audio f +//Load all the Fluid Corpus Manipulation audio files + ( ~path = File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"; ~loader = FluidLoadFolder(~path); @@ -96,12 +97,15 @@ s.reboot; ~pitchbufs = 4.collect{Buffer.new}; ~statsbufs = 4.collect{Buffer.new}; ) + //segment ~slicer.play(s,~loader.buffer,~loader.index,{|dataDictionary| "Slicing done".postln;}); //In the interests of brevity, let's just take a subset of the slices and process these -~subset = IdentityDictionary.newFrom(~slicer.index.asSortedArray[0..3].flatten(1)); +~subset = IdentityDictionary.newFrom(~slicer.index.asSortedArray[0..7].flatten(1)); + //write pitch statistics into a dataset +( ~extractor = FluidProcessSlices({|src,start,num,data| var pitch, stats, label,i; i = data.value[\voice]; @@ -110,7 +114,10 @@ s.reboot; stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); FluidDataSetWr.kr(label,~statsbufs[i],~pitchdata,Done.kr(stats)) }); +) + ~extractor.play(s,~loader.buffer,~subset,{"Feature extraction done".postln}); + //view the data ~pitchdata.print :: diff --git a/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp b/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp index c69722b..4c1783c 100644 --- a/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp +++ b/release-packaging/HelpSource/Classes/FluidSliceCorpus.schelp @@ -6,7 +6,7 @@ related:: Classes/FluidLoadFolder, Classes/FluidProcessSlices, Classes/FluidBufO DESCRIPTION:: A utility class that abstracts the boiler plate code involved with batch slicing a buffer containing distinct chunks of audio (a 'corpus' for these purposes). -Whilst this class is designed to be used most easily in conjunction with link::Classes/FluidLoadFolder::, it doesn't have to be. However, it does excpect to be passed an link::Classes/IdentityDictionary:: of a particular format (see link::#indexFormat#description below::). +Whilst this class is designed to be used most easily in conjunction with link::Classes/FluidLoadFolder::, it doesn't have to be. However, it does expect to be passed an link::Classes/IdentityDictionary:: of a particular format (see link::#indexFormat#description below::). The actual mechanism for doing the slicing is provided by the user, in the form of a function that will form part of a larger link::Classes/Synth:: (see link::#sliceFuncDescription#below::). @@ -88,10 +88,13 @@ s.reboot ~loader.play(s,action:{ |dataDictionary| "Done loading".postln}); ) +( ~slicer = FluidSliceCorpus({ |src,start,num,dest| FluidBufOnsetSlice.kr(src,start,num,indices:dest, threshold:0.7) }); +) +( ~slicer.play(s,~loader.buffer,~loader.index,{|dataDictionary| "Slicing done".postln; //we get passed an IdentityDictionary of slice data, let's look at it @@ -102,6 +105,5 @@ s.reboot ')'.postln; } }); - - +) :: From 0d32688d29f9b9a6d746dc438171abc9844d32f2 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 8 Jun 2020 12:14:27 +0100 Subject: [PATCH 165/550] Simplify example fuction by strapping numChans to 1 --- .../HelpSource/Classes/FluidProcessSlices.schelp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index 98f89c1..a764c05 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -49,9 +49,9 @@ code:: var pitch, stats,statsbuf,label,voice; label = data.key; voice = data.value[\voice]; - statsbuf = LocalBuf(7,4); - pitch = FluidBufPitch.kr(src,start,num,features:~featurebuffers[voice]); - stats = FluidBufStats.kr(~featurebuffers[voice],stats:statsbuf,trig:Done.kr(pitch)); + statsbuf = LocalBuf(7); + pitch = FluidBufPitch.kr(src,start,num,numChans:1,features:~featurebuffers[voice]); + stats = FluidBufStats.kr(~featurebuffers[voice],numChans:1, stats:statsbuf,trig:Done.kr(pitch)); FluidDataSetWr.kr(label,statsbuf,~mydataset,Done.kr(stats)) }; :: From c1f7725d71748d9c3807f0d8ed607adf78f4f31b Mon Sep 17 00:00:00 2001 From: Gerard Date: Mon, 8 Jun 2020 20:32:48 +0100 Subject: [PATCH 166/550] FluidManipulationJSON: parse JSON manually to avoid change of types --- .../Classes/FluidManipulationJSON.sc | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/release-packaging/Classes/FluidManipulationJSON.sc b/release-packaging/Classes/FluidManipulationJSON.sc index 216e577..780d731 100644 --- a/release-packaging/Classes/FluidManipulationJSON.sc +++ b/release-packaging/Classes/FluidManipulationJSON.sc @@ -1,40 +1,67 @@ + FluidManipulationClient { tmpJSONFilename{ - ^Platform.defaultTempDir++"tmp_fluid_dataset_"++ - Date.localtime.stamp++".json"; + ^Platform.defaultTempDir++"tmp_fluid_data_"++ + Date.localtime.stamp++"_"++UniqueID.next++".json"; } dump {|action| var filename = this.tmpJSONFilename; action ?? {action = postit}; - this.write(filename, { - action.value(filename.parseYAMLFile); - File.delete(filename); - }); + this.write(filename, { + action.value(this.parseJSON(File.readAllString(filename))); + File.delete(filename); + }); } load{|dict, action| var filename = this.tmpJSONFilename; - var str = this.asJSON(dict); - File.use(filename, "w", { |f| f.write(this.asJSON(dict));}); + File.use(filename, "wt", { |f| f.write(this.asJSON(dict));}); this.read(filename, { - action.notNil.if{ action.value }; - File.delete(filename); + action.notNil.if{ action.value; }; + File.delete(filename); + }); + } + + toDict{|obj| + var converted; + if(obj.class === Event){ + converted = obj.as(Dictionary); + converted.keysValuesChange{|k,v|this.toDict(v)} + ^converted; + }; + if(obj.class === Array){ + converted = obj.collect{|v| this.toDict(v)}; + ^converted; + }; + ^obj; + } + + parseJSON{|jsonStr| + var parsed = jsonStr; + jsonStr.do({|char,pos| + var inString = false; + char.switch( + $",{(jsonStr[pos-1]==$\ && inString).not.if({inString = inString.not})}, + ${,{ if(inString.not){parsed[pos] = $(} }, + $},{ if(inString.not){parsed[pos] = $)} } + ) }); + ^this.toDict(parsed.interpret); } asJSON{|d| - if(d.isString || d.isNumber){^d}; - if(d.isKindOf(Dictionary), + if(d.isNumber){^d}; + if(d.isString){^d.asCompileString}; + if(d.isKindOf(Dictionary)) { ^"{" ++ ( d.keys.asList.collect{|k| k.asString.asCompileString ++ ":" + this.asJSON(d[k]) }).join(", ") ++ "}" - }); - if(d.isKindOf(SequenceableCollection), + }; + if(d.isKindOf(SequenceableCollection)) { ^"[" ++ d.collect({|x|this.asJSON(x)}).join(", ")++ "]"; - }); + }; } } From 253339aac2dbd17737f249044db621dc9d0c490b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 8 Jun 2020 22:01:34 +0100 Subject: [PATCH 167/550] Harden message dispatch against cmd+. by using more safely shared state --- include/FluidSCWrapper.hpp | 47 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 1c52928..8323503 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -847,6 +847,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase using FloatControlsIter = impl::FloatControlsIter; + using SharedState = std::shared_ptr>; //I would like to template these to something more scaleable, but baby steps friend class impl::RealTime; friend class impl::NonRealTime; @@ -1185,7 +1186,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase struct MessageDispatch { static constexpr size_t Message = N; - FluidSCWrapper* wrapper; + SharedState state; ArgTuple args; Ret result; std::string name; @@ -1226,7 +1227,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); MessageData* msg = new (msgptr) MessageData; msg->name = '/' + Client::getMessageDescriptors().template name(); - msg->wrapper = x; + msg->state = x->state(); ArgTuple& args = msg->args; // type check OSC message @@ -1306,10 +1307,10 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { MessageData* m = static_cast(data); m->result = - ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; + ReturnType{invokeImpl(m->state, m->args, IndexList{})}; if (!m->result.ok()) - printResult(m->wrapper, m->result); + printResult(m->state, m->result); return true; }, @@ -1321,11 +1322,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase world); if(m->result.status() != Result::Status::kError) - messageOutput(m->wrapper, m->name, m->result); + messageOutput(m->state, m->name, m->result); else { - auto ft = getInterfaceTable(); - ft->fSendNodeReply(&m->wrapper->mParent->mNode, + auto ft = getInterfaceTable(); + if(m->state->mNodeAlive) + ft->fSendNodeReply(m->state->mNode, -1, m->name.c_str(),0, nullptr); } return true; @@ -1341,54 +1343,59 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase } template // Call from NRT - static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, + static decltype(auto) invokeImpl(SharedState& x, ArgsTuple& args, std::index_sequence) { - return x->client().template invoke(x->client(), std::get(args)...); + return x->client.template invoke(x->client, std::get(args)...); } template // call from RT - static void messageOutput(FluidSCWrapper* x, const std::string& s, + static void messageOutput(SharedState& x, const std::string& s, MessageResult& result) { auto ft = getInterfaceTable(); // allocate return values index numArgs = ToFloatArray::allocSize(static_cast(result)); + if(!x->mNodeAlive) return; + float* values = static_cast( - ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + ft->fRTAlloc(x->mNode->mWorld, asUnsigned(numArgs) * sizeof(float))); // copy return data ToFloatArray::convert(values, static_cast(result)); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + ft->fSendNodeReply(x->mNode, -1, s.c_str(), static_cast(numArgs), values); } - static void messageOutput(FluidSCWrapper* x, const std::string& s, + static void messageOutput(SharedState& x, const std::string& s, MessageResult&) { auto ft = getInterfaceTable(); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + if(!x->mNodeAlive) return; + ft->fSendNodeReply(x->mNode, -1, s.c_str(), 0, nullptr); } template - static void messageOutput(FluidSCWrapper* x, const std::string& s, + static void messageOutput(SharedState& x, const std::string& s, MessageResult>& result) { auto ft = getInterfaceTable(); std::array offsets; index numArgs; + if(!x->mNodeAlive) return; + std::tie(offsets, numArgs) = ToFloatArray::allocSize(static_cast>(result)); float* values = static_cast( - ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + ft->fRTAlloc(x->mNode->mWorld, asUnsigned(numArgs) * sizeof(float))); ToFloatArray::convert(values, std::tuple(result), offsets, std::index_sequence_for()); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + ft->fSendNodeReply(x->mNode, -1, s.c_str(), static_cast(numArgs), values); } @@ -1464,14 +1471,14 @@ public: return p; } - static void printResult(FluidSCWrapper* x, Result& r) + static void printResult(SharedState& x, Result& r) { - if (!x) return; + if (!x.get() || !x->mNodeAlive) return; switch (r.status()) { case Result::Status::kWarning: { - if (x->mWorld->mVerbosity > 0) + if (x->mNode->mWorld->mVerbosity > 0) std::cout << "WARNING: " << r.message().c_str() << '\n'; break; } From 7dc44c738ee40d3f608c69a38550c77def440210 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 8 Jun 2020 22:34:24 +0100 Subject: [PATCH 168/550] Don't try and send message responses > 8kb --- include/FluidSCWrapper.hpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 8323503..80d88ff 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1354,10 +1354,16 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase MessageResult& result) { auto ft = getInterfaceTable(); + if(!x->mNodeAlive) return; + // allocate return values index numArgs = ToFloatArray::allocSize(static_cast(result)); - - if(!x->mNodeAlive) return; + + if(numArgs > 2048) + { + std::cout << "ERROR: Message response too big to send (" << asUnsigned(numArgs) * sizeof(float) << " bytes)." << std::endl; + return; + } float* values = static_cast( ft->fRTAlloc(x->mNode->mWorld, asUnsigned(numArgs) * sizeof(float))); @@ -1389,6 +1395,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase std::tie(offsets, numArgs) = ToFloatArray::allocSize(static_cast>(result)); + if(numArgs > 2048) + { + std::cout << "ERROR: Message response too big to send (" << asUnsigned(numArgs) * sizeof(float) << " bytes)." << std::endl; + return; + } + float* values = static_cast( ft->fRTAlloc(x->mNode->mWorld, asUnsigned(numArgs) * sizeof(float))); From 1279678eea9fd6df03c0346c9cbc7c2fba1c5b45 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 8 Jun 2020 22:35:19 +0100 Subject: [PATCH 169/550] FluidDataSet remove dump (now in parent class) --- release-packaging/Classes/FluidDataSet.sc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index b0253a5..8d2d845 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -90,11 +90,6 @@ FluidDataSet : FluidManipulationClient { this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]); } - dump { |action| - action ?? {action = postit}; - this.prSendMsg(\dump,[],action,[string(FluidMessageResponse,_,_)]); - } - free { serverCaches.remove(server,id); super.free; From dc2fecef799ca9394d362ead6ff79762e07bd7b5 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 9 Jun 2020 11:08:31 +0100 Subject: [PATCH 170/550] Fix DataSet help to take Dictionary in dump action, not String --- release-packaging/HelpSource/Classes/FluidDataSet.schelp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index ec85964..1359c91 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -156,11 +156,7 @@ Routine{ ~ds.print //to post window by default, but you can supply a custom action instead ~ds.dump //likewise //for example -( -~ds.dump{|j| - ~dict = j.parseJSON -} -) +~ds.dump{ |d| ~dict = d } //Now we have a Dictionary of our data and IDs ~dict.postcs From 18d5b35e1e9696fb5ed19aaa935fb58e81d440d8 Mon Sep 17 00:00:00 2001 From: Gerard Date: Tue, 9 Jun 2020 11:58:35 +0100 Subject: [PATCH 171/550] read and write for KNNClassifier/Regressor --- release-packaging/Classes/FluidKNNClassifier.sc | 9 +++++++++ release-packaging/Classes/FluidKNNRegressor.sc | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index 1314812..3fb9e47 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -25,4 +25,13 @@ FluidKNNClassifier : FluidManipulationClient { [string(FluidMessageResponse,_,_)] ); } + + read{|filename,action| + this.prSendMsg(\read,[filename.asString],action); + } + + write{|filename,action| + this.prSendMsg(\write,[filename.asString],action); + } + } \ No newline at end of file diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index 74c0023..46916b1 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -26,4 +26,13 @@ FluidKNNRegressor : FluidManipulationClient { this.prSendMsg(\predictPoint, [buffer.asUGenInput, k,uniform], action, [number(FluidMessageResponse,_,_)]); } + + read{|filename,action| + this.prSendMsg(\read,[filename.asString],action); + } + + write{|filename,action| + this.prSendMsg(\write,[filename.asString],action); + } + } From 2cf8b8cceda516c613e6b0e047800c7b00fea41c Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Tue, 9 Jun 2020 16:01:50 +0100 Subject: [PATCH 172/550] added a learning example of dictionary -> dataset / labelset --- .../super-simple-dictionary-json-example.scd | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 release-packaging/Examples/dataset/super-simple-dictionary-json-example.scd diff --git a/release-packaging/Examples/dataset/super-simple-dictionary-json-example.scd b/release-packaging/Examples/dataset/super-simple-dictionary-json-example.scd new file mode 100644 index 0000000..c1d33d4 --- /dev/null +++ b/release-packaging/Examples/dataset/super-simple-dictionary-json-example.scd @@ -0,0 +1,45 @@ +// create the data dictionary +~data = Dictionary.new +7.do{|i| ~data.add(("entry-"++i).asSymbol -> 10.collect{|j|j/10 + i})} + +// nest that dictionary in the dataset format, adding the number of columns +~dict = Dictionary.new +~dict.add(\data -> ~data) +~dict.add(\cols -> 10) + +//create a dataset, then loading the dictionary +~ds = FluidDataSet.new(s,\simple1data); +~ds.load(~dict) +~ds.print + +//fun with kdtree to see it actually works +~kdtree = FluidKDTree.new(s) +~kdtree.fit(~ds,{\done.postln;}) + +~target = Buffer.loadCollection(s,(4).dup(10)); +~kdtree.kNearest(~target,5,{|a|a.postln;}) +~kdtree.kNearestDist(~target,5,{|a|a.postln;}) + + +///////////////////////////////////////////// +// creating a labelset the same way + +// creating the data dictionary +~data2 = Dictionary.new +7.do{|i| ~data2.add(("entry-"++i).asSymbol -> (if( i.odd, {["odd"]},{["even"]})))} + +// nesting again +~dict2 = Dictionary.new +~dict2.add(\data -> ~data2) +~dict2.add(\cols -> 1) + +// creating a labelset and loading the dictionary +~ls = FluidLabelSet.new(s,\simplelabel); +~ls.load(~dict2) +~ls.print + +// testin with a classifier toy example +~classifier = FluidKNNClassifier.new(s); +~classifier.fit(~ds,~ls, {\done.postln;}) + +~classifier.predictPoint(~target,2,action: {|x|x.postln;}) \ No newline at end of file From 9542b9be789a2d16f189259e4ed5e7594b59a158 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 10 Jun 2020 11:37:34 +0100 Subject: [PATCH 173/550] Avoid overloading scheduler in SystemClock or TempoClock by defering all message actions back to AppClock --- release-packaging/Classes/FluidManipulationClient.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 7dd8d1f..8982730 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -83,7 +83,7 @@ FluidManipulationClient { synth !? { OSCFunc( { |msg| - forkIfNeeded{ + defer{ var result; result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); if(action.notNil){action.value(result)}{action.value}; From ce22e867dbc477a621f2afff78b6a171bbb13b6f Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 10 Jun 2020 15:12:22 +0100 Subject: [PATCH 174/550] Mend clean up for Shared Clients --- include/FluidSCWrapper.hpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 80d88ff..34b80eb 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -737,6 +737,7 @@ struct LifetimePolicy using SharedType = typename GetSharedType::type; using ClientPointer = typename SharedType::ClientPointer; + using ParamType = typename Client::ParamSetType; static void constructClass(Unit* unit) { @@ -747,17 +748,19 @@ struct LifetimePolicy Wrapper::setParams(unit, params,controlsReader,true); auto& name = params.template get<0>(); - + + mParamsRegistry.emplace(name, ParamType{params}); + mClientRegistry.emplace(name, Client{params}); + auto client = Client{params}; - auto clientRef = SharedType::lookup(name); - - auto pos = mRegistry.find(clientRef); - if(pos == mRegistry.end()) mRegistry.emplace(clientRef); - new (static_cast(unit)) Wrapper(std::move(controlsReader),std::move(client),std::move(params)); } - static void destroyClass(Unit* unit) { static_cast(unit)->~Wrapper(); } + static void destroyClass(Unit* unit) { + std::cout << "Regsitry size :" << mClientRegistry.size() << '\n'; + static_cast(unit)->~Wrapper(); + + } static void setup(InterfaceTable* ft, const char* name) { @@ -769,10 +772,9 @@ struct LifetimePolicy [](World*,void*,sc_msg_iter* args, void* /*replyAddr*/) { auto objectName = std::string(args->gets()); - auto clientRef = SharedType::lookup(objectName); - auto pos = mRegistry.find(clientRef); - if(pos != mRegistry.end()) mRegistry.erase(clientRef); - }, &mRegistry); + mClientRegistry.erase(objectName); + mParamsRegistry.erase(objectName); + }, &mClientRegistry); } private: @@ -783,14 +785,17 @@ private: return SharedType::lookup(name); } - static std::unordered_set mRegistry; + static std::unordered_map mClientRegistry; + static std::unordered_map mParamsRegistry; }; template -std::unordered_set::ClientPointer> - LifetimePolicy::mRegistry{}; - +std::unordered_map + LifetimePolicy::mClientRegistry{}; +template +std::unordered_map::ParamType> + LifetimePolicy::mParamsRegistry{}; //// Template Specialisations for NRT/RT template From 7d6d169de37ccc5cdd294cd3ab3401408b85cde6 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 11:42:33 +0100 Subject: [PATCH 175/550] Changes to FluidDataSetWr to take separate string prefix and integer counter for label making --- include/clients/rt/FluidDataSetWr.hpp | 23 +++++++++++++------ .../Classes/FluidCorpusBuilders.sc | 7 ++++-- release-packaging/Classes/FluidDataSetWr.sc | 10 ++++---- .../Classes/FluidProcessSlices.schelp | 7 +++--- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index e700a4f..7058289 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -23,23 +23,32 @@ namespace client { class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut { public: - FLUID_DECLARE_PARAMS(StringParam("label", "Label"), - BufferParam("buf", "Data Buffer"), - DataSetClientRef::makeParam("dataSet", "DataSet Name")); + FLUID_DECLARE_PARAMS( + DataSetClientRef::makeParam("dataSet", "DataSet Name"), + StringParam("labelPrefix","Label Prefix"), + LongParam("labelOffset", "Label Counter Offset", 0), + BufferParam("buf", "Data Buffer") + ); DataSetWriterClient(ParamSetViewType& p) : mParams(p) {} template Result process(FluidContext&) { - auto& idx = get<0>(); - auto buf = get<1>(); - auto dataset = get<2>().get(); + auto dataset = get<0>().get(); if (auto datasetPtr = dataset.lock()) - return datasetPtr->addPoint(idx, buf); + { + std::stringstream ss; + ss << get<1>() << get<2>() + (mCounter++); + auto buf = get<3>(); + return datasetPtr->addPoint(ss.str(), buf); + } else return {Result::Status::kError, "No dataset"}; } + + private: + index mCounter{0}; }; using NRTThreadedDataSetWriter = diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 30f128d..0c6bf3b 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -73,6 +73,7 @@ FluidSliceCorpus { v = bufIdx[k]; counter = counter + 1; idx = counter; + ("Slicing" + counter ++ "/" ++ total).postln; OSCFunc({ tmpIndices.loadToFloatArray(action:{ |a| var sliceindex = 1; @@ -82,13 +83,14 @@ FluidSliceCorpus { var rawPoints = Array.newFrom(a).asInteger; if(rawPoints[0] != [v[\bounds][0]]){rawPoints = [v[\bounds][0]] ++ rawPoints}; if(rawPoints.last != [v[\bounds][1]]){rawPoints=rawPoints ++ [v[\bounds][1]]}; - rawPoints.doAdjacentPairs{|a,b| var dict; if ((b - a) >= 1){ dict = IdentityDictionary(); dict.putAll(v); dict[\bounds] = [a,b]; + dict[\prefix] = k; + dict[\index] = sliceindex; index.add(((k ++ "-" ++sliceindex).asSymbol)->dict); sliceindex = sliceindex + 1; } @@ -137,8 +139,9 @@ FluidProcessSlices{ var idx,v, k = jobs.pop; v = bufIdx[k]; counter = counter + 1; + ("Processing" + counter ++ "/" ++ total).postln; idx = counter; - v[\index] = counter; + v[\allindex] = counter; v[\voice] = jobID; OSCFunc({ completed = completed + 1; diff --git a/release-packaging/Classes/FluidDataSetWr.sc b/release-packaging/Classes/FluidDataSetWr.sc index 0787ed6..7b9aacc 100644 --- a/release-packaging/Classes/FluidDataSetWr.sc +++ b/release-packaging/Classes/FluidDataSetWr.sc @@ -1,12 +1,12 @@ FluidDataSetWr : UGen { - *new1 { |rate,label,buf,dataset,trig,blocking| + *new1 { |rate, dataset, labelPrefix = "", labelOffset = 0, buf, trig, blocking| buf ?? {"No input buffer provided".error}; - ^super.new1(rate,*(FluidManipulationClient.prServerString(label.asSymbol) - ++buf.asUGenInput++FluidDataSet.asUGenInput(dataset.asSymbol)++trig++blocking)); + ^super.new1(rate,*(FluidManipulationClient.prServerString(dataset.asSymbol) + ++ FluidDataSet.asUGenInput(labelPrefix.asSymbol) ++ labelOffset.asInteger.asUGenInput ++buf.asUGenInput ++ trig ++ blocking)); } - *kr { |label, buf, dataset,trig=1| - ^this.new1(\control,label,buf,dataset,trig,1) + *kr { |dataset,labelPrefix = "", labelOffset = 0,buf, trig=1| + ^this.new1(\control,dataset,labelPrefix,labelOffset, buf, trig,1) } } diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index a764c05..727a30a 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -107,12 +107,13 @@ s.reboot; //write pitch statistics into a dataset ( ~extractor = FluidProcessSlices({|src,start,num,data| - var pitch, stats, label,i; + var pitch, stats, labelPrefix, labelIndex, i; i = data.value[\voice]; - label = data.key; + labelPrefix = data.value[\prefix]; + labelIndex = data.value[\index]; pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); - FluidDataSetWr.kr(label,~statsbufs[i],~pitchdata,Done.kr(stats)) + FluidDataSetWr.kr(~pitchdata,labelPrefix,labelIndex,buf:~statsbufs[i],trig:Done.kr(stats)) }); ) From 61a74c66e88099c25a0ebddd9135ad01c2639037 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 11:51:43 +0100 Subject: [PATCH 176/550] Update FluidDataSetWr help --- .../HelpSource/Classes/FluidDataSetWr.schelp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp index 7ec2a64..e39eb1d 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp @@ -13,15 +13,18 @@ private:: *new1 METHOD:: kr The equivalent of calling link::Classes/FluidDataSet#-addPoint::, but within a link::Classes/Synth:: -ARGUMENT:: label -A symbol for the label of the new point +ARGUMENT:: dataset +An instance of link::Classes/FluidDataSet:: or an instance's name. + +ARGUMENT:: labelPrefix +A string or symbol with a prefix for generated labels + +ARGUMENT:: labelOffset +An integer with the offset to start labeling from. If the UGen is run in a server-side loop (i.e. repeatedly retriggered), the generated labels will count upwards from this offset. ARGUMENT:: buf The link::Classes/Buffer:: containing the data point. -ARGUMENT:: dataset -An instance of link::Classes/FluidDataSet:: or an instance's name. - ARGUMENT:: trig A kr trigger signal @@ -36,7 +39,7 @@ s.reboot; ( { var b = LocalBuf.newFrom([0,1,2,3]); - FreeSelfWhenDone.kr(FluidDataSetWr.kr("help_data_point",b,~ds)); + FreeSelfWhenDone.kr(FluidDataSetWr.kr(~ds,"help_data_point",buf:b)); }.play(s); ) From cffcd889dd5c6ec7075b21e60e88796decd7eba1 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 12:41:18 +0100 Subject: [PATCH 177/550] Remove debug detritus --- include/FluidSCWrapper.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 34b80eb..f480e4b 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -756,10 +756,8 @@ struct LifetimePolicy new (static_cast(unit)) Wrapper(std::move(controlsReader),std::move(client),std::move(params)); } - static void destroyClass(Unit* unit) { - std::cout << "Regsitry size :" << mClientRegistry.size() << '\n'; - static_cast(unit)->~Wrapper(); - + static void destroyClass(Unit* unit) { + static_cast(unit)->~Wrapper(); } static void setup(InterfaceTable* ft, const char* name) From 3c3659fbd5c8ee41097cc04d62de094609acceda Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 12:42:16 +0100 Subject: [PATCH 178/550] Harden FluiDManipulationClient against multiple cmd-. --- .../Classes/FluidManipulationClient.sc | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 8982730..e0f4ad7 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -18,13 +18,21 @@ FluidProxyUgen : UGen { FluidManipulationClient { + classvar clock; + var ugen; var id; var defName, def; var onSynthFree, keepAlive; + var aliveThread; var postit; + *initClass { + clock = TempoClock.new; + clock.permanent = true; + } + *prServerString{ |s| var ascii = s.ascii; ^[ascii.size].addAll(ascii) @@ -60,15 +68,12 @@ FluidManipulationClient { onSynthFree = { synth = nil; if(keepAlive){ - //If we don't sync here, cmd-. doesn't reset properly (but server.freeAll does) - forkIfNeeded { - server.sync; - synth = Synth(defName,target: RootNode(server)); - synth.onFree{onSynthFree.value}; - } + synth = Synth(defName,target: RootNode(server)); + synth.onFree{clock.sched(0,onSynthFree)}; } }; - synth.onFree{onSynthFree.value}; + CmdPeriod.add({synth = nil}); + synth.onFree{clock.sched(0,onSynthFree)}; } free{ @@ -80,7 +85,8 @@ FluidManipulationClient { prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); - synth !? { + server.bind{ + synth ?? {onSynthFree.value; server.sync}; OSCFunc( { |msg| defer{ @@ -89,7 +95,7 @@ FluidManipulationClient { if(action.notNil){action.value(result)}{action.value}; } },'/'++msg, server.addr, nil,[synth.nodeID]).oneShot; - server.listSendMsg(['/u_cmd',synth.nodeID,ugen.synthIndex,msg].addAll(args)); + server.listSendMsg(['/u_cmd', synth.nodeID, ugen.synthIndex, msg].addAll(args)); } } } From a87df66e354ab85a83bf388c0fff984f2f61b377 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 14:21:32 +0100 Subject: [PATCH 179/550] Fork rather than bind, for greater determinism --- release-packaging/Classes/FluidManipulationClient.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index e0f4ad7..70e7a45 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -85,7 +85,7 @@ FluidManipulationClient { prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); - server.bind{ + forkIfNeeded{ synth ?? {onSynthFree.value; server.sync}; OSCFunc( { |msg| From 2184ab375891ce2f56046dbd57631edfb94d99bf Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 15:58:59 +0100 Subject: [PATCH 180/550] FluidDataset: help file review, minor changes and formatting, FluidLabelSet: add print --- release-packaging/Classes/FluidDataSet.sc | 77 +++++---- release-packaging/Classes/FluidLabelSet.sc | 12 +- .../HelpSource/Classes/FluidDataSet.schelp | 148 ++++++++---------- 3 files changed, 108 insertions(+), 129 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 8d2d845..286f774 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -11,17 +11,17 @@ FluidDataSet : FluidManipulationClient { serverCaches = FluidServerCache.new; } - *at{ |server, id| - ^serverCaches.tryPerform(\at, server,id) + *at{ |server, name| + ^serverCaches.tryPerform(\at, server, name) } - *new { |server,name| + *new { |server, name| if(this.at(server,name).notNil){ FluidDataSetExistsError("A FluidDataset called % already exists.".format(name)).throw; ^nil } ^super.new(server,*FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst} - } + } init {|name| id = name; @@ -31,59 +31,58 @@ FluidDataSet : FluidManipulationClient { cache { serverCaches.initCache(server); serverCaches.put(server,id,this); - } - - *asUGenInput { |input| - var ascii = input.asString.ascii; - ^[ascii.size].addAll(ascii) - } + } + *asUGenInput { |input| + var ascii = input.asString.ascii; + ^[ascii.size].addAll(ascii) + } - asString { + asString { ^"FluidDataSet(%)".format(id).asString; - } + } asSymbol { ^id.asSymbol } - addPoint{|label, buffer, action| - this.prSendMsg(\addPoint,[label.asSymbol,buffer.asUGenInput],action); - } + addPoint{|label, buffer, action| + this.prSendMsg(\addPoint,[label.asSymbol,buffer.asUGenInput],action); + } - getPoint{|label, buffer, action| - this.prSendMsg(\getPoint,[label.asSymbol,buffer.asUGenInput],action); - } + getPoint{|label, buffer, action| + this.prSendMsg(\getPoint,[label.asSymbol,buffer.asUGenInput],action); + } - updatePoint{|label, buffer, action| - this.prSendMsg(\updatePoint,[label.asSymbol,buffer.asUGenInput],action); - } + updatePoint{|label, buffer, action| + this.prSendMsg(\updatePoint,[label.asSymbol,buffer.asUGenInput],action); + } - deletePoint{|label, action| - this.prSendMsg(\deletePoint,[label.asSymbol],action); - } + deletePoint{|label, action| + this.prSendMsg(\deletePoint,[label.asSymbol],action); + } - cols {|action| + cols {|action| action ?? {action = postit}; - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } + read{|filename,action| + this.prSendMsg(\read,[filename.asString],action); + } - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } + write{|filename,action| + this.prSendMsg(\write,[filename.asString],action); + } - size { |action| + size { |action| action ?? {action = postit}; - this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } + this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } - clear { |action| - this.prSendMsg(\clear,[],action); - } + clear { |action| + this.prSendMsg(\clear,[],action); + } print { |action| action ?? {action = postit}; diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 398d972..e4e396c 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -14,14 +14,14 @@ FluidLabelSet : FluidManipulationClient { ^serverCaches.tryPerform(\at, server,id) } - *new { |server,name| + *new { |server,name| serverCaches.at(server,name) !? { FluidLabelSetExistsError("A FluidLabelSet called % already exists.".format(name)).throw; }; ^super.new(server,*FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst} - } + } - init { |name| + init { |name| id = name; this.cache; } @@ -93,4 +93,10 @@ FluidLabelSet : FluidManipulationClient { *freeAll { |server| serverCaches.do(server,{|x|x.free;}); } + + print { |action| + action ?? {action = postit}; + this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]); + } + } diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 1359c91..8d8f125 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -6,29 +6,27 @@ related:: Classes/FluidLabelSet, Classes/FluidKDTree, Classes/FluidKNN, Classes/ DESCRIPTION:: A server-side container associating labels with multi-dimensional data. FluidDataSet is identified by its name. - -​ CLASSMETHODS:: ​ PRIVATE:: asUGenInput METHOD:: new -Create a new instance of the dataset, with the given name. If a Dataset with this name already exists, an exception will be thrown (see link::Classes/FluidDataSet#at:: to access an extant Dataset) +Create a new instance of the dataset, with the given name. If a Dataset with this name already exists, an exception will be thrown (see link::Classes/FluidDataSet#at:: to access an extant Dataset). ARGUMENT:: server -The link::Classes/Server:: on which to create the data set +The link::Classes/Server:: on which to create the data set. ARGUMENT:: name -A symbol or string with the name of the dataset. +A symbol with the name of the dataset. ​ returns:: The new instance METHOD:: at -Retreives a cached instance of a FluidDataSet with the given name, or returns nil if no such object exists. +Retrieves a cached instance of a FluidDataSet with the given name, or returns nil if no such object exists. ARGUMENT:: server -The server associated with this dataset instance -ARGUMENT:: id -The name of the Dataset to retreive from the cache +The server associated with this dataset instance. +ARGUMENT:: name +The name of the Dataset to retrieve from the cache. INSTANCEMETHODS:: @@ -39,88 +37,51 @@ METHOD:: addPoint Add a new point to the data set. The dimensionality of the dataset is governed by the size of the first point added. Will report an error if the label already exists, or if the size of the data does not match the dimensionality of the dataset. ARGUMENT:: label -A symbol or string with the label for the new point +A symbol or string with the label for the new point. ARGUMENT:: buffer -A link::Classes/Buffer:: with the new data point +A link::Classes/Buffer:: with the new data point. ARGUMENT:: action -A function to run when the point has been added +A function to run when the point has been added. ​​ METHOD:: updatePoint Update an existing label's data. Will report an error if the label doesn't exist, or if the size of the data does not match the given dimensionality of the dataset. -ARGUMENT:: label -symbol or string with the label -ARGUMENT:: buffer -A link::Classes/Buffer:: containing the updated data -ARGUMENT:: action -A function to run when the server has updated -METHOD:: size -Report the number of items currently in the data set -​ -METHOD:: getPoint -Retreive a point from the data set into a link::Classes/Buffer::. Will report an error if the label or buffer doesn't exist​ -ARGUMENT:: label -symbol or string with the label to retreive -ARGUMENT:: buffer -link::Classes/Buffer:: to fill -ARGUMENT:: action -function to run when the point has been retreived +METHOD:: getPoint +Retrieve a point from the data set into a link::Classes/Buffer::. Will report an error if the label or buffer doesn't exist​. METHOD:: deletePoint Remove a point from the data set. Will report an error if the label doesn't exist. -ARGUMENT:: label -symbol or string with the label to remove -ARGUMENT:: action -Function to run when the point has been deleted ​​ METHOD:: clear -Empty the data set -ARGUMENT:: action -Function to run when the data set has been emptied +Empty the data set. ​ METHOD:: free -Destroy the object on the server - -METHOD:: cols -Report the dimensionality of the data set. If action is nil, will default to posting result. -ARGUMENT:: action -A function to run when the server responds, whose argument is the data set dimensionality. By default, the method will print the response to the post window. - -METHOD:: size -Report the number of points in the data set. If action is nil, will default to posting result. -ARGUMENT:: action -A function to run when the server responds, whose argument is the data set size. By default, the method will print the response to the post window. - +Destroy the object on the server. METHOD:: read -Read a data set from a JSON file on disk -ARGUMENT:: filename -The absolute path of the JSON file to read -ARGUMENT:: action -A function to run when the file has been read +Read a data set from a JSON file on disk. ​ METHOD:: write Write the data set to disk as a JSON file. -ARGUMENT:: filename -Absolute path for the new file -ARGUMENT:: action -A function to run when the file has been written + +METHOD:: size +Report the number of items currently in the data set. + +METHOD:: cols +Report the dimensionality of the data set. If action is nil, will default to posting result. METHOD:: dump -(buggy, please use Print for now) Post the content of the dataset in JSON format in the window by default, but you can supply a custom action instead; +Get the contents of the dataset as a Dictionary (note: uses a temporary file under the hood); + +METHOD:: load +Fill the dataset with the contents of a dictionary, replacing its current contents (note: uses a temporary file under the hood). METHOD:: print -Post an abbreviated content of the dataset in the window by default, but you can supply a custom action instead; +Post an abbreviated content of the dataset in the window by default, but you can supply a custom action instead. ​​ -METHOD:: asString -​Responds with the name of the data set as a pretty(ish) string - -METHOD:: asSymbol -​Responds with the name of the data set as a symbol - METHOD:: synth The internal synth the object uses to communicate with the server -​ + returns:: A link::Classes/Synth:: ​ METHOD:: server @@ -131,33 +92,46 @@ returns:: A link::Classes/Server:: EXAMPLES:: CODE:: -// Make a one-dimensional data set called 'simple1data' -( -~ds = FluidDataSet.new(s,\simple1data,1); -) - +// Create a simple a one-dimensional data set, three ways +// Using routine +s.boot; ( -Routine{ -~ds.clear; -// Make a buffer to use for adding points -~point = Buffer.alloc(s,1,1); -//Add 10 points, using the index as a label. +fork{ + ~ds = FluidDataSet.new(s,\simple1d_1); + ~point = Buffer.alloc(s,1,1); s.sync; - 10.do{|i| + 10.do{|i| ~point.set(0,i); ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); s.sync; - } -}.play + }; + ~ds.dump; s.sync; ~ds.free; +}; ) -//Inspect the dataset using print (abbreviated output) or dump (JSON output) - -~ds.print //to post window by default, but you can supply a custom action instead -~ds.dump //likewise -//for example -~ds.dump{ |d| ~dict = d } -//Now we have a Dictionary of our data and IDs -~dict.postcs +//Using Dictionary +( +d = Dictionary.new; +d.add(\cols -> 1); +d.add(\data -> Dictionary.newFrom(10.collect{|i|[i.asString, [i.asFloat]]}.flatten)); +fork{ + ~ds = FluidDataSet.new(s,\simple1d_2); s.sync; + ~ds.load(d); s.sync; + ~ds.dump; s.sync; ~ds.free; +} +); + +// Using synth +( +~ds = FluidDataSet.new(s,\simple1d_3); +{ + var trig = Impulse.kr(10); + var count = PulseCount.kr(trig) - 1; + var buf = LocalBuf(1); + BufWr.kr(count, buf); + FluidDataSetWr.kr(\simple1d_3, buf: buf, trig: trig); + FreeSelf.kr(count - 8); +}.play.onFree{~ds.dump{|o| o.postln; ~ds.free}} +) :: From 6836c4070af81c4176db8f61dbf41e8f384f75aa Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 16:45:43 +0100 Subject: [PATCH 181/550] FluidLabelSet: help file review, minor changes and formatting --- release-packaging/Classes/FluidLabelSet.sc | 90 +++++++++---------- .../HelpSource/Classes/FluidDataSet.schelp | 2 +- .../HelpSource/Classes/FluidLabelSet.schelp | 80 +++++++---------- 3 files changed, 76 insertions(+), 96 deletions(-) diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index e4e396c..4c0dc3a 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -10,93 +10,89 @@ FluidLabelSet : FluidManipulationClient { serverCaches = FluidServerCache.new; } - *at{ |server, id| - ^serverCaches.tryPerform(\at, server,id) + *at{ |server, name| + ^serverCaches.tryPerform(\at, server, name) } - *new { |server,name| + *new { |server,name| serverCaches.at(server,name) !? { FluidLabelSetExistsError("A FluidLabelSet called % already exists.".format(name)).throw; }; ^super.new(server,*FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst} - } + } - init { |name| + init { |name| id = name; this.cache; - } + } cache { serverCaches.initCache(server); serverCaches.put(server,id,this); } - asString { - ^"FluidLabelSet(%)".format(id).asString; - } + asString { + ^"FluidLabelSet(%)".format(id).asString; + } asSymbol { ^id } + *asUGenInput { |input| + var ascii = input.asString.ascii; + ^[ascii.size].addAll(ascii) + } - - *asUGenInput { |input| - var ascii = input.asString.ascii; - ^[ascii.size].addAll(ascii) - } - - addLabel{|id, label, action| - this.prSendMsg(\addLabel,[id.asString, label.asString],action); - } + addLabel{|id, label, action| + this.prSendMsg(\addLabel, [id.asString, label.asString],action); + } updateLabel{|id, label, action| - this.prSendMsg(\updateLabel,[id.asString, label.asString],action); + this.prSendMsg(\updateLabel, [id.asString, label.asString],action); } + getLabel{|id, action| + this.prSendMsg(\getLabel, [id.asString], action,[string(FluidMessageResponse,_,_)]); + } - getLabel{|id, action| - this.prSendMsg(\getLabel,[id.asString],action,[string(FluidMessageResponse,_,_)]); - } - - deleteLabel{|id, action| - this.prSendMsg(\deleteLabel,[id.asString],action); - } + deleteLabel{|id, action| + this.prSendMsg(\deleteLabel, [id.asString],action); + } - cols {|action| + cols {|action| action ?? {action = postit}; - this.prSendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); - } + this.prSendMsg(\cols, [], action, [number(FluidMessageResponse,_,_)]); + } - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } + read{|filename,action| + this.prSendMsg(\read, [filename.asString], action); + } - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } + write{|filename,action| + this.prSendMsg(\write, [filename.asString], action); + } - size { |action| + size {|action| action ?? {action = postit}; - this.prSendMsg(\size,[],action,[number(FluidMessageResponse,_,_)]); - } + this.prSendMsg(\size,[], action, [number(FluidMessageResponse,_,_)]); + } - clear { |action| - this.prSendMsg(\clear,[],action); - } + clear {|action| + this.prSendMsg(\clear,[], action); + } - free { |action| - serverCaches.remove(server,id); + free {|action| + serverCaches.remove(server, id); super.free; } - *freeAll { |server| - serverCaches.do(server,{|x|x.free;}); + *freeAll {|server| + serverCaches.do(server,{|x| x.free;}); } print { |action| action ?? {action = postit}; - this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]); + this.prSendMsg(\print,[], action, [string(FluidMessageResponse,_,_)]); } - } diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 8d8f125..8a400b2 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -59,7 +59,7 @@ METHOD:: free Destroy the object on the server. METHOD:: read -Read a data set from a JSON file on disk. +Read a data set from a JSON file on disk, replacing the current contents. ​ METHOD:: write Write the data set to disk as a JSON file. diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp index 77d812c..353729c 100644 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -9,21 +9,19 @@ FluidLabelSet is a server-side container of associations between labels (from a CLASSMETHODS:: -PRIVATE:: kr - METHOD:: new Make a new instance of a label set, uniquely identified by its name. Creating an instance with a name already in use will throw an exception. Use link::Classes/FluidLabelSet#*at:: or free the existing instance. ARGUMENT:: server -The link::Classes/Server:: on which to create the label set +The link::Classes/Server:: on which to create the label set. ARGUMENT:: name -symbol or string with the label set's name +symbol with the label set's name. METHOD:: at -Retreive a label set from the cache +Retrieve a label set from the cache. ARGUMENT:: server -The link::Classes/Server:: on which to create the label set +The link::Classes/Server:: on which to create the label set. ARGUMENT:: id -symbol or string with the label set's name +symbol or string with the label set's name. INSTANCEMETHODS:: @@ -31,68 +29,54 @@ INSTANCEMETHODS:: PRIVATE:: init, id METHOD:: addLabel -Add a label to the label set +Add a label to the label set. ARGUMENT:: id -symbol or string with the ID for this label +symbol or string with the ID for this label. ARGUMENT:: label -symbol or string with the label to add +symbol or string with the label to add. ARGUMENT:: action -function to run when the operation completes +function to run when the operation completes. METHOD:: updateLabel -Change a label in the label set -ARGUMENT:: id -symbol or string with the ID for this label -ARGUMENT:: label -symbol or string with the label to add -ARGUMENT:: action -function to run when the operation completes +Change a label in the label set. METHOD:: getLabel -Retreive the label associated with an ID. Will report an error if the ID isn't present in the set -ARGUMENT:: id -symbol or string with the ID to retreive. -ARGUMENT:: action -A function to run when the server responds, with the label as its argument +Retrieve the label associated with an ID. Will report an error if the ID isn't present in the set. METHOD:: deleteLabel -Remove a id-label pair from the label set -ARGUMENT:: id -symbol or string with the ID to remove -ARGUMENT:: action -A function to run when the label has been removed +Remove a id-label pair from the label set. METHOD:: clear -Empty the label set -ARGUMENT:: action -Function to run whrn the action completes +Empty the label set. METHOD:: size -Report the number of items in the label set -ARGUMENT:: action -A function to run when the server responds, taking the size as its argument +Report the number of items in the label set. METHOD:: cols -Returns the dimensionality of the link::Classes/FluidDataSet:: associated with this label set -ARGUMENT:: action -A function to run when the server responds, with the dimensionality as its argument +Report the number of labels per point (at the moment only 1 is supported). METHOD:: write Write this label set to disk as a JSON file. -ARGUMENT:: filename -Absolute path of file to write -ARGUMENT:: action -A function to run when the file is written METHOD:: read -Read a label set from a JSON file on disk -ARGUMENT:: filename -Absolute path of the file to read -ARGUMENT:: action -A function to run when the file is read +Read a label set from a JSON file on disk, replacing the current contents. + +METHOD:: dump +Get the contents of the label set as a Dictionary (note: uses a temporary file under the hood); + +METHOD:: load +Fill the label set with the contents of a dictionary, replacing its current contents (note: uses a temporary file under the hood). + +METHOD:: print +Post an abbreviated content of the label set in the window by default, but you can supply a custom action instead. EXAMPLES:: code:: -(some example code) -:: \ No newline at end of file +~ls = FluidLabelSet.new(s,\labelset_example); + +["one", "two", "three"].collect{|x,i| ~ls.addLabel(i, x);}; +~ls.print; + +~ls.free; +:: From 1d13f8cac06b911a6d1183f4fa57f0804a5382cf Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 17:01:31 +0100 Subject: [PATCH 182/550] refactor common methods to FluidManipulationClint --- release-packaging/Classes/FluidDataSet.sc | 18 ------------------ release-packaging/Classes/FluidKDTree.sc | 14 -------------- release-packaging/Classes/FluidKMeans.sc | 13 ------------- .../Classes/FluidKNNClassifier.sc | 9 +-------- release-packaging/Classes/FluidKNNRegressor.sc | 9 --------- release-packaging/Classes/FluidLabelSet.sc | 18 ------------------ release-packaging/Classes/FluidMDS.sc | 6 ++++++ .../Classes/FluidManipulationClient.sc | 18 ++++++++++++++++++ release-packaging/Classes/FluidNormalize.sc | 14 -------------- release-packaging/Classes/FluidPCA.sc | 16 ---------------- release-packaging/Classes/FluidStandardize.sc | 14 -------------- 11 files changed, 25 insertions(+), 124 deletions(-) diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 286f774..ec73b97 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -62,24 +62,6 @@ FluidDataSet : FluidManipulationClient { this.prSendMsg(\deletePoint,[label.asSymbol],action); } - cols {|action| - action ?? {action = postit}; - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } - - size { |action| - action ?? {action = postit}; - this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - clear { |action| this.prSendMsg(\clear,[],action); } diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 71306d7..2bc8b3e 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -24,18 +24,4 @@ FluidKDTree : FluidManipulationClient { this.prSendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,k,_)]); } - cols { |action| - - action ?? {action = postit}; - - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - - read{ |filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{ |filename,action| - this.prSendMsg(\write,[filename.asString],action); - } } diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index daf8d61..462f2f7 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -30,17 +30,4 @@ FluidKMeans : FluidManipulationClient { predictPoint { |buffer, action| this.prSendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); } - - cols { |action| - action ?? action = postit; - this.prSendMsg(\cols,[],action,[number(FluidMessageResponse,_,_)]); - } - - read{ |filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{ |filename,action| - this.prSendMsg(\write,[filename.asString],action); - } } diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index 3fb9e47..7db9271 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -26,12 +26,5 @@ FluidKNNClassifier : FluidManipulationClient { ); } - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } -} \ No newline at end of file +} diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index 46916b1..74c0023 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -26,13 +26,4 @@ FluidKNNRegressor : FluidManipulationClient { this.prSendMsg(\predictPoint, [buffer.asUGenInput, k,uniform], action, [number(FluidMessageResponse,_,_)]); } - - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } - } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index 4c0dc3a..a2c5ecd 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -60,24 +60,6 @@ FluidLabelSet : FluidManipulationClient { this.prSendMsg(\deleteLabel, [id.asString],action); } - cols {|action| - action ?? {action = postit}; - this.prSendMsg(\cols, [], action, [number(FluidMessageResponse,_,_)]); - } - - read{|filename,action| - this.prSendMsg(\read, [filename.asString], action); - } - - write{|filename,action| - this.prSendMsg(\write, [filename.asString], action); - } - - size {|action| - action ?? {action = postit}; - this.prSendMsg(\size,[], action, [number(FluidMessageResponse,_,_)]); - } - clear {|action| this.prSendMsg(\clear,[], action); } diff --git a/release-packaging/Classes/FluidMDS.sc b/release-packaging/Classes/FluidMDS.sc index 57139d3..1d5098f 100644 --- a/release-packaging/Classes/FluidMDS.sc +++ b/release-packaging/Classes/FluidMDS.sc @@ -19,5 +19,11 @@ FluidMDS : FluidManipulationClient { fitTransform{|sourceDataset, destDataset, k, dist, action| this.prSendMsg(\fitTransform,[sourceDataset.asSymbol, destDataset.asSymbol, k, dist],action); } + + // not implemented + cols {|action|} + read{|filename,action|} + write{|filename,action|} + size { |action|} } diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc index 70e7a45..541a87e 100644 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ b/release-packaging/Classes/FluidManipulationClient.sc @@ -83,6 +83,24 @@ FluidManipulationClient { ^nil } + cols {|action| + action ?? {action = postit}; + this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + + read{|filename, action| + this.prSendMsg(\read,[filename.asString],action); + } + + write{|filename, action| + this.prSendMsg(\write,[filename.asString],action); + } + + size {|action| + action ?? {action = postit}; + this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]); + } + prSendMsg { |msg, args, action,parser| if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); forkIfNeeded{ diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 674d481..3f66e49 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -24,18 +24,4 @@ FluidNormalize : FluidManipulationClient { transformPoint{|sourceBuffer, destBuffer, action| this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } - - cols {|action| - action ?? {action = postit}; - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } - } diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index 29c4be8..bd78d6e 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -26,20 +26,4 @@ FluidPCA : FluidManipulationClient { transformPoint{|sourceBuffer, destBuffer, action| this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } - - cols {|action| - - action ?? {action = postit}; - - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - - read{|filename,action| - this.prSendMsg(\read,[filename],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename],action); - } - } diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index 4be99cd..cd30407 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -24,18 +24,4 @@ FluidStandardize : FluidManipulationClient { transformPoint{|sourceBuffer, destBuffer, action| this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); } - - cols {|action| - action ?? {action = postit}; - this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]); - } - - read{|filename,action| - this.prSendMsg(\read,[filename.asString],action); - } - - write{|filename,action| - this.prSendMsg(\write,[filename.asString],action); - } - } From 7a5dabeb4531fcc8e63e30a843dc874eed264e45 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 11 Jun 2020 17:24:02 +0100 Subject: [PATCH 183/550] updated all of my example files. All of them working except the label in demo-dataset using the utilities. --- .../dataset/demo-dataset-maker-utilities.scd | 2 +- .../Examples/dataset/myfirstdataset.scd | 149 ------------------ .../dataset/super-simple-1D-example.scd | 50 +++--- .../dataset/super-simple-1D-example2.scd | 39 +++-- .../super-simple-classifier-example.scd | 8 +- ...-normalization-standardization-example.scd | 15 +- 6 files changed, 62 insertions(+), 201 deletions(-) delete mode 100644 release-packaging/Examples/dataset/myfirstdataset.scd diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 3e03c5e..16827b0 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -23,7 +23,7 @@ mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1); stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc)); flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(label,~flatbuf[voice],~ds,trig:Done.kr(flatten)) + writer = FluidDataSetWr.kr(~ds,label,buf: ~flatbuf[voice],trig:Done.kr(flatten)) }); ) diff --git a/release-packaging/Examples/dataset/myfirstdataset.scd b/release-packaging/Examples/dataset/myfirstdataset.scd deleted file mode 100644 index 0c821de..0000000 --- a/release-packaging/Examples/dataset/myfirstdataset.scd +++ /dev/null @@ -1,149 +0,0 @@ -// TB2 SC Playground V0 - -/* -Current stinkers: -1) Producing flat datapoints for FluidDataSet (i.e. flattening and cherry picking a multichannel buffer) takes bloody ages due to all the server syncing. I can't work out how to do it reliably outwith a Routine (which would certainly be quicker, to my mind) -2) Functions from the classes don't yet directly return things, and you have to access their return data through actions. This is partly because I don't know what the correct thing to do w/r/t blocking is, so I'm hoping GR will do it properly -*/ - -//STEP 0: start server -s.reboot; - -if(s.hasBooted.not){"Warning: server not running".postln}; - -//STEP 1: Get some files -Buffer.freeAll; -( -FileDialog.new(fileMode:2,okFunc:{|x| ~path = x[0]; - ~audioBuffers = SoundFile.collectIntoBuffers(~path+/+'*',s); - ~lookup = Dictionary(n:~audioBuffers.size); - ~audioBuffers.do{|b| ~lookup.add(b.path->b)}; -}); -) - -//STEP 2: Make a FluidDataSet -~dataset = FluidDataSet.new(s,"mfccs"); - -//STEP 3A: EITHER populate the dataset like so (and cry about how long the data point assembly takes) -( -Routine{ - var tmpMFCCs = Buffer.new(s); - var tmpStats = Buffer.new(s); - var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1);//12 dims * 4 stats * 2 derivatives - s.sync; - ~audioBuffers.do{|b| - ("Analyzing" + b.path).postln; - FluidBufMFCC.process(s,b,features: tmpMFCCs); - FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); - "stats".postln; - 12.do{|i| - //This takes ages becayse of server syncing :-( - FluidBufCompose.process(s,tmpStats,0,2, i+1,1, destination: tmpFlat, destStartFrame: (i*8)); - FluidBufCompose.process(s,tmpStats,4,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 2); - FluidBufCompose.process(s,tmpStats,6,3, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 3); - FluidBufCompose.process(s,tmpStats,11,1, i+1,1, destination: tmpFlat, destStartFrame: (i*8) + 6); - FluidBufCompose.process(s,tmpStats,13,1, i+1,1, destination:tmpFlat, destStartFrame: (i*8) + 7); - }; - ~dataset.addPoint(b.path,tmpFlat); - }; - s.sync; - "Done".postln; - tmpFlat.free; - tmpStats.free; - tmpMFCCs.free; -}.play -) - -//STEP 3B: OR populate the dataset with the flattening happening in langage side (much faster for now) -( -Routine{ - var tmpMFCCs = Buffer.new(s); - var tmpStats = Buffer.new(s); - var langStats; - var langFlat; - var tmpFlat = Buffer.alloc(s,12 * 4 * 2, 1); //12 dims * 4 stats * 2 derivatives - s.sync; - ~audioBuffers.do{|b| - ("Analyzing" + b.path).postln; - FluidBufMFCC.process(s,b,features: tmpMFCCs); - FluidBufStats.process(s,source:tmpMFCCs, stats: tmpStats,numDerivs:1); - tmpStats.getn(0,182,{|y| langStats = y;}); - s.sync; - "stats".postln; - langFlat = Array.new(); - //taking the mean, std, min and max, and the mean, std, min and max of the first derivative, of each MFCCs except coeff 0 to dismiss amplitude) - [0,1,4,6,7,8,11,13].do({|i| var j,k; j =((i*13)+1); k = j + 11;langFlat = langFlat ++ langStats[j..k]}); - tmpFlat.setn(0,langFlat); - s.sync; - ~dataset.addPoint(b.path,tmpFlat); - }; - s.sync; - "Done".postln; - tmpStats.free; - tmpMFCCs.free; - tmpFlat.free; -}.play -) - -//check -~dataset.size({|x| x.postln}) - -//save -( -FileDialog.new(fileMode: 0, acceptMode: 1, okFunc:{|x| var file = x[0]; - //if the file exists and is a json, delete it - if ((file.splitext[1] == "json") && (File.existsCaseSensitive(file)), {File.delete(file);"File Overwritten".postln;}); - //if not json, make it so - if (file.splitext[1] != "json", {file = file ++ ".json";}); - // then write - ~dataset.write(file); -}); -) - -//STEP 3C: OR load in one you rolled earlier -FileDialog.new(fileMode: 0, acceptMode: 0, okFunc:{|x| ~dataset.read(x[0])}); - -//peek -c = Buffer.new(s) -~dataset.getPoint(~audioBuffers[3].path,c, { c.getn(0,96,{|x| x.postln})}) - -/*************************************/ -//FluidKDTree -~kdtree = FluidKDTree.new(s) -~kdtree.fit(~dataset,action:{"fit".postln}) - -//match -~kdtree.kNearest(c,5,{|x| ~matches = x;}) -~kdtree.kNearestDist(c,5,{|x| x.postln}) - -~lookup[~matches[4]].postln - -/*************************************/ -//FluidKMeans -~kMeans= FluidKMeans.new(s) -~kMeans.fit(~dataset,k:5,action:{"fit".postln}) - - -// predicts in which cluster a point would be -~kMeans.predictPoint(c,{|x|x.postln}) - -// predicts which cluster each points of a dataset would be in, as a label -~labels = FluidLabelSet.new(s,"clusters") -~kMeans.predict(~dataset,~labels, {|x| x.postln}) - -~labels.getLabel(~audioBuffers[2].path,action:{|c| c.postln}) - -//query each item -( -Routine{ - ~labels.size({|x|x.do {|i| - ~audioBuffers[i].path.postln; - ~labels.getLabel(~audioBuffers[i].path,action:{|c| c.postln}); - s.sync; - } - }); -}.play -) - -//labelset can be written as json -~labels.write(~path+/+"labels.json") \ No newline at end of file diff --git a/release-packaging/Examples/dataset/super-simple-1D-example.scd b/release-packaging/Examples/dataset/super-simple-1D-example.scd index 010477e..98bd2d9 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example.scd @@ -3,13 +3,14 @@ s.reboot ~point = Buffer.alloc(s,1,1) ( Routine{ - 10.do{|i| - ~point.set(0,i); - s.sync; - ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) - } + 10.do{|i| + ~point.set(0,i); + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); + s.sync; + } }.play ) +~ds.print; /*** KDTREE ***/ ~tree = FluidKDTree.new(s) @@ -18,12 +19,11 @@ Routine{ k = 5; //play with this ( Routine{ - 10.do{|i| - ~point.set(0,i); - s.sync; - ("Neighbours for point" + i).postln; - ~tree.kNearest(~point, k, {|x| ("Labels:" + x).postln}) - } + 10.do{|i| + ~point.set(0,i); + ~tree.kNearest(~point, k, {|x| "Neighbours for a value of % are ".postf(i); x.postln}); + s.sync; + } }.play ) @@ -35,11 +35,11 @@ Routine{ ( Routine{ - 10.do{|i| + 10.do{|i| ~point.set(0,i); - s.sync; - ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + i ++ ":" + x).postln}) - } + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for a value of " + i ++ ":" + x).postln}); + s.sync; + } }.play ) @@ -48,12 +48,16 @@ Routine{ ~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) ( -Routine{ - var n; - ~labels.size({|x| - x.asInteger.do{|i| - ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); - s.sync; - };}); -}.play +~labels.size{|x| + Routine{x.asInteger.do{|i| //size does not return a value, but we can retrieve it via a function + ~labels.getLabel(i,action: {|l| + ("Label for entry " + i ++ ":" + l).postln; + }); + s.sync; + } + }.play; +}; ) + +// or simply print it +~labels.print \ No newline at end of file diff --git a/release-packaging/Examples/dataset/super-simple-1D-example2.scd b/release-packaging/Examples/dataset/super-simple-1D-example2.scd index f42cdcc..0347497 100644 --- a/release-packaging/Examples/dataset/super-simple-1D-example2.scd +++ b/release-packaging/Examples/dataset/super-simple-1D-example2.scd @@ -7,11 +7,12 @@ Routine{ var d; if(i<=4,{d=i},{d=i+5}); ~point.set(0,d); + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); s.sync; - ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}) } }.play ) +~ds.print; /*** KDTREE ***/ ~tree = FluidKDTree.new(s) @@ -20,16 +21,16 @@ Routine{ k = 5; //play with this ( Routine{ - 10.do{|i| - ~point.set(0,i*2); - s.sync; - ("Neighbours for point" + (i*2)).postln; - ~tree.kNearest(~point, k, {|x| ("Labels:" + x).postln}); - ~tree.kNearestDist(~point,k,{|x| ("Distances:" + x).postln}) - } + 15.do{|i| + ~point.set(0,i); + ~tree.kNearest(~point, k, {|x| "Neighbours for a value of % are ".postf(i); x.post;" with respective distances of ".post;}); + ~tree.kNearestDist(~point,k,{|x| x.postln}); + s.sync; + } }.play ) + /*** KMEANS ***/ ~kmeans = FluidKMeans.new(s) @@ -38,10 +39,10 @@ Routine{ ( Routine{ - 10.do{|i| + 15.do{|i| ~point.set(0,i); s.sync; - ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for point" + i ++ ":" + x).postln}) + ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for a value of " + i ++ ":" + x).postln}); } }.play ) @@ -51,12 +52,16 @@ Routine{ ~kmeans.predict(~ds,~labels, {|x| ("Size of each cluster" + x).postln}) ( -Routine{ - var n; - ~labels.size({|x| - x.asInteger.do{|i| - ~labels.getLabel(i.asString,action: {|l|("Label for" + i ++ ":" + l).postln}); +~labels.size{|x| + Routine{x.asInteger.do{|i| //size does not return a value, but we can retrieve it via a function + ~labels.getLabel(i,action: {|l| + ("Label for entry " + i ++ ":" + l).postln; + }); s.sync; - };}); -}.play + } + }.play; +}; ) + +// or simply print it +~labels.print \ No newline at end of file diff --git a/release-packaging/Examples/dataset/super-simple-classifier-example.scd b/release-packaging/Examples/dataset/super-simple-classifier-example.scd index 855035f..da60257 100644 --- a/release-packaging/Examples/dataset/super-simple-classifier-example.scd +++ b/release-packaging/Examples/dataset/super-simple-classifier-example.scd @@ -22,8 +22,8 @@ v.mouseDownAction = {|view, x, y|myx=x;myy=y;w.refresh; // myx.postln;myy.postln; Routine{ b.setn(0,[myx,myy]); - s.sync; ~knn.predictPoint(b, k, action: {|x|x.postln;}); + s.sync; }.play;}; //custom redraw function @@ -40,19 +40,19 @@ w.drawFunc = { ) ( -//populates a dataset with the same squares as the gui (their centres) +//populates a dataset with the same squares as the gui (their centres) (old method, iterating over buffers. A dictionary approach would be more efficient, see the example in this folder) Routine{ 50.do{|i| var x = i.div(10)*30+20; var y = i.mod(10)*30+20; b.setn(0,[x,y]); - s.sync; ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); ~simpleOutput.addLabel(i.asString,"White",{("Added Output" + i).postln}); - b.setn(0,[x+150,y]); s.sync; + b.setn(0,[x+150,y]); ~simpleInput.addPoint((i+50).asString,b,{("Added Input" + (i+50)).postln}); ~simpleOutput.addLabel((i+50).asString,"Red",{("Added Output" + (i+50)).postln}); + s.sync; } }.play; ) diff --git a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd index 9309ded..35417f4 100644 --- a/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd +++ b/release-packaging/Examples/dataset/super-simple-normalization-standardization-example.scd @@ -6,14 +6,15 @@ ( // fill up the dataset with 20 entries of 10 column/dimension/descriptor value each. The naming of the item's label is arbitrary as usual -20.do({ - arg i; - Buffer.loadCollection(s,Array.fill(~nb_of_dim,{rrand(0.0,100.0)}),action:{ - arg buf; - ~dataset.addPoint("point-"++i.asInteger.asString,buf); - buf.free; +Routine{ + var buf = Buffer.alloc(s,~nb_of_dim); + 20.do({ arg i; + buf.loadCollection(Array.fill(~nb_of_dim,{rrand(0.0,100.0)})); + ~dataset.addPoint("point-"++i.asInteger.asString, buf); + s.sync; }); -}); + buf.free; +}.play ) ~dataset.print; From e6600d073898a342c1162c112cb689718aaff1b8 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 17:43:00 +0100 Subject: [PATCH 184/550] refactor common methods to FluidManipulationClient in help files --- .../HelpSource/Classes/FluidDataSet.schelp | 18 ------------- .../HelpSource/Classes/FluidKDTree.schelp | 26 ------------------- .../HelpSource/Classes/FluidKMeans.schelp | 23 ---------------- .../HelpSource/Classes/FluidLabelSet.schelp | 19 +------------- .../Classes/FluidManipulationClient.schelp | 26 +++++++++++-------- .../HelpSource/Classes/FluidPCA.schelp | 19 -------------- 6 files changed, 16 insertions(+), 115 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index 8a400b2..aab6fc8 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -58,24 +58,6 @@ Empty the data set. METHOD:: free Destroy the object on the server. -METHOD:: read -Read a data set from a JSON file on disk, replacing the current contents. -​ -METHOD:: write -Write the data set to disk as a JSON file. - -METHOD:: size -Report the number of items currently in the data set. - -METHOD:: cols -Report the dimensionality of the data set. If action is nil, will default to posting result. - -METHOD:: dump -Get the contents of the dataset as a Dictionary (note: uses a temporary file under the hood); - -METHOD:: load -Fill the dataset with the contents of a dictionary, replacing its current contents (note: uses a temporary file under the hood). - METHOD:: print Post an abbreviated content of the dataset in the window by default, but you can supply a custom action instead. ​​ diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index bf3dd11..628c50b 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -52,32 +52,6 @@ The number of neighbours to search ARGUMENT:: action A function that will run when the query returns, whose argument is an array of distances -METHOD:: cols -Get the dimensionality of the data that the tree is indexed against - -ARGUMENT:: action -A function that runs when the query returns, whose argument is the dimensionality - - -METHOD:: read -Set the object's state from a JSON file - -ARGUMENT:: filename -The location of a JSON file on disk - -ARGUMENT:: action -function to run when the data is loaded - - -METHOD:: write -Write the index of the tree to disk. Currently this will not overwrite extant files. - -ARGUMENT:: filename -The path of a JSON file to write - -ARGUMENT:: action -A function to run when writing is complete - EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index bd2384e..83908b4 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -61,13 +61,6 @@ a link::Classes/Buffer:: containing a data point ARGUMENT:: action A function to run when the server responds, taking the ID of the cluser as its argument - - -METHOD:: cols -Retreive the dimentionality of the dataset this instance is trained on -ARGUMENT:: action -A function to run when the server responds, taking the dimensionality as its argument - METHOD:: predict Report cluster assignments for previously unseen data ARGUMENT:: dataset @@ -78,22 +71,6 @@ ARGUMENT:: action A function to run when complete, taking an array of the counts for each catgegory as its argument - -METHOD:: write -write learned clusters to disk as a JSON file. Will not overwrite existing files -ARGUMENT:: filename -Absolute path for file -ARGUMENT:: action -A function to run when the file is written - -METHOD:: read -Read a learned clustering of a data set from a JSON file -ARGUMENT:: filename -Absolute path of the JSON file -ARGUMENT:: action -Function to run when the file has been read - - EXAMPLES:: Server.default.options.outDevice = "Built-in Output" code:: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp index 353729c..8170ce1 100644 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -20,7 +20,7 @@ METHOD:: at Retrieve a label set from the cache. ARGUMENT:: server The link::Classes/Server:: on which to create the label set. -ARGUMENT:: id +ARGUMENT:: name symbol or string with the label set's name. @@ -49,23 +49,6 @@ Remove a id-label pair from the label set. METHOD:: clear Empty the label set. -METHOD:: size -Report the number of items in the label set. - -METHOD:: cols -Report the number of labels per point (at the moment only 1 is supported). - -METHOD:: write -Write this label set to disk as a JSON file. - -METHOD:: read -Read a label set from a JSON file on disk, replacing the current contents. - -METHOD:: dump -Get the contents of the label set as a Dictionary (note: uses a temporary file under the hood); - -METHOD:: load -Fill the label set with the contents of a dictionary, replacing its current contents (note: uses a temporary file under the hood). METHOD:: print Post an abbreviated content of the label set in the window by default, but you can supply a custom action instead. diff --git a/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp b/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp index 3e2d69a..5bb3dca 100644 --- a/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp +++ b/release-packaging/HelpSource/Classes/FluidManipulationClient.schelp @@ -11,13 +11,6 @@ CLASSMETHODS:: PRIVATE:: nonBlocking -METHOD:: kr -The kr of the underlying UGem, which will output progress readings once all messages can be asynchronous. - -If, for whatever reason, you create an instance of a client object using code::kr:: in your own synth, make sure to set the instance's link::Classes/FluidManipulationClient#synth:: and link::Classes/FluidManipulationClient#server::, or nothing will work. - -returns:: An instance - METHOD:: new Language-side constructor. Internally, this creates a new synth around an instance of the sub-class being constructed, and maintains a variable pointing to the synth, so that it can be communicated with. @@ -43,9 +36,20 @@ The link::Classes/Server:: that our instance's object is running on returns:: a link::Classes/Server:: +METHOD:: read +Read the object's data from a JSON file on disk, replacing the current contents. +​ +METHOD:: write +Write the object's data to disk as a JSON file. + +METHOD:: size +Report the number of items currently in the object's data. + +METHOD:: cols +Report the dimensionality of the object's data. If action is nil, will default to posting result. -EXAMPLES:: +METHOD:: dump +Get the contents of the object's data as a Dictionary (note: uses a temporary file under the hood); -code:: -(some example code) -:: \ No newline at end of file +METHOD:: load +Fill the object with the contents of a dictionary, replacing its current contents (note: uses a temporary file under the hood). diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index d21fe7f..987d76f 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -56,25 +56,6 @@ Output data ARGUMENT:: action Run when done -METHOD:: cols -Return the dimensionaliy of the data the model was trained on -ARGUMENT:: action -Run when done, taking the number of columns as an argument. If nil, defaults to posting to window - -METHOD:: read -Read a data set from a JSON file on disk -ARGUMENT:: filename -The absolute path of the JSON file to read -ARGUMENT:: action -A function to run when the file has been read -​ -METHOD:: write -Write the data set to disk as a JSON file. -ARGUMENT:: filename -Absolute path for the new file -ARGUMENT:: action -A function to run when the file has been written - EXAMPLES:: code:: From c3e90529aa446528fceb691ed24960d6e76f62ab Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 18:38:13 +0100 Subject: [PATCH 185/550] KDTreee help file review --- .../HelpSource/Classes/FluidKDTree.schelp | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index 628c50b..66877ef 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -56,60 +56,50 @@ A function that will run when the query returns, whose argument is an array of d EXAMPLES:: code:: -//Make some 2D points and place into a dataset -s.reboot; -( -~points = 100.collect{ [ 1.0.linrand,1.0.linrand] }; -~dataset= FluidDataSet(s,\kdtree_help_rand2d); -~tmpbuf = Buffer.alloc(s,2); -fork{ - s.bind{ - ~dataset.ready.wait; - ~points.do{|x,i| - (""++(i+1)++"/100").postln; - ~tmpbuf.setn(0,x); - ~dataset.addPoint(i,~tmpbuf); - s.sync - }; - "Data loaded".postln; - } -} -) -//Make a new tree, and fit it to the dataset + +// Make a dataset of random 23D points +s.boot; ( fork{ - ~tree = FluidKDTree(s); - ~tree.ready.wait; - s.sync; - ~tree.fit(~dataset); + ~ds = FluidDataSet.new(s,\kdtree_help_rand2d); + d = Dictionary.with( + *[\cols -> 2,\data -> Dictionary.newFrom( + 100.collect{|i| [i, [ 1.0.linrand,1.0.linrand]]}.flatten)]); + s.sync; + ~ds.load(d, {~ds.print}); } ) -//Dims of tree should match dataset -~tree.cols +// Make a new tree, and fit it to the dataset +~tree = FluidKDTree(s); -//Return labels of k nearest points to new data -( -~testpoint = [ 1.0.linrand,1.0.linrand ]; -("\n\nTest point:" + ~testpoint).postln; -~tmpbuf.setn(0,~testpoint); +//Fit it to the dataset +~tree.fit(~ds); + +// Should be 100 points, 2 columns +~tree.size; +~tree.cols; + + +//Return labels of k nearest points to a new point +~p = [ 1.0.linrand,1.0.linrand ]; +~tmpbuf = Buffer.loadCollection(s, ~p, 1, { + ~tree.kNearest(~tmpbuf,5, { |a|~nearest = a;}) +}); + + +// Labels of nearest points +~nearest.postln; + +// Values fork{ - ~tree.kNearest(~tmpbuf,5, { |a| - ("Labels of nearest points" + a).postln; - "Nearest points".postln; - a.do{|l| - ~dataset.getPoint(l,~tmpbuf,action:{ - ~tmpbuf.loadToFloatArray(action:{ |point| - point.postln; - }) - }); - s.sync; - } - }); +~nearest.do{|n| + ~ds.getPoint(n, ~tmpbuf, {~tmpbuf.getn(0, 2, {|x|x.postln})}); + s.sync; +} } -) -//or the distances -~tree.kNearestDist(~tmpbuf,5, { |a| a.postln }); +//Distances of the nearest points +~tree.kNearestDist(~tmpbuf, 5, { |a| a.postln }); :: From 21270d9f5c6543101147cf769c1441b28b864b61 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 18:39:45 +0100 Subject: [PATCH 186/550] removee FluidKNN --- release-packaging/Classes/FluidKNN.sc | 23 ------- .../HelpSource/Classes/FluidKNN.schelp | 68 ------------------- 2 files changed, 91 deletions(-) delete mode 100644 release-packaging/Classes/FluidKNN.sc delete mode 100644 release-packaging/HelpSource/Classes/FluidKNN.schelp diff --git a/release-packaging/Classes/FluidKNN.sc b/release-packaging/Classes/FluidKNN.sc deleted file mode 100644 index 1be993a..0000000 --- a/release-packaging/Classes/FluidKNN.sc +++ /dev/null @@ -1,23 +0,0 @@ -FluidKNN : FluidManipulationClient { - - *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } - - init {|uid| - id = uid; - } - - fit{|dataset, action| - this.prSendMsg(\fit,[dataset.asString],action); - } - - classifyPoint{ |buffer, labelset, k, action| - this.prSendMsg(\classify,[buffer.asUGenInput, labelset.asString, k],action,[string(FluidMessageResponse,_,_)]); - } - - regressPoint { |buffer,dataset, k, action| - this.prSendMsg(\regress,[buffer.asUGenInput, dataset.asString,k],action,[number(FluidMessageResponse,_,_)]); - } -} diff --git a/release-packaging/HelpSource/Classes/FluidKNN.schelp b/release-packaging/HelpSource/Classes/FluidKNN.schelp deleted file mode 100644 index 41abca3..0000000 --- a/release-packaging/HelpSource/Classes/FluidKNN.schelp +++ /dev/null @@ -1,68 +0,0 @@ -TITLE:: FluidKNN -summary:: K Nearest Neighbours machine learning -categories:: FluidManipulation -related:: Classes/FluidKDTree - -DESCRIPTION:: -Simple machine learning tasks (classification and regression) using K Nearest Neighbours. - -See -https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbors-classification - -https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbors-regression - - -CLASSMETHODS:: - -INSTANCEMETHODS:: - -METHOD:: fit -'Train' the KNN on a source link::Classes/FluidDataSet:: - -ARGUMENT:: dataset -source link::Classes/FluidDataSet:: - -ARGUMENT:: action -A function to run when fitting is complete - -METHOD:: regressPoint -Map a point between a source link::Classes/FluidDataSet:: used when link::Classes/FluidKNN#index::ing this KNN, and a target data set passed as an argument. - -For this to work, the target data set must have labels in common with the source data set. The code::k:: nearest neighbours to the supplied data point are retrrived from the source tree, and then a mapping is obtained through the average of the equivalently labelled points in the target data set. - -WARNING:: For now the target data set can only be 1D:: - -ARGUMENT:: buffer -The data point to map, in a link::Classes/Buffer:: - -ARGUMENT:: dataset -The target link::Classes/FluidDataSet:: - -ARGUMENT:: k -The number of neighbours to use in the estimation - -ARGUMENT:: action -A function to run when the server responds, taking the value of the regressed point as its argument - -METHOD:: classifyPoint -Classify a point, using categories from the supplied label set, which maps labels from the source data set to category IDs. This works by getting the labels of the code::k:: nearest points to the passed data point from the source data set, and looking up their IDs in the passed label set. The most frequently ocurring ID is designated as the class for the point. - -ARGUMENT:: buffer -The data point to classify, in a link::Classes/Buffer:: - -ARGUMENT:: labelset -A link::Classes/FluidLabelSet:: of categories mapped to labels in source data set - -ARGUMENT:: k -The number of neighbours to use in the classificaton - -ARGUMENT:: action -A function to run when the server responds, taking the assigned label as its argument - - - -EXAMPLES:: - -code:: -(some example code) -:: From 8381580885f4fe473bc32c80b6f4a963c3719fa9 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 19:25:10 +0100 Subject: [PATCH 187/550] KDTree further formatting review and typos --- release-packaging/Classes/FluidKDTree.sc | 40 ++++++++++--------- .../HelpSource/Classes/FluidKDTree.schelp | 14 +++---- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/release-packaging/Classes/FluidKDTree.sc b/release-packaging/Classes/FluidKDTree.sc index 2bc8b3e..4cf622f 100644 --- a/release-packaging/Classes/FluidKDTree.sc +++ b/release-packaging/Classes/FluidKDTree.sc @@ -1,27 +1,31 @@ FluidKDTree : FluidManipulationClient { - var id; + var id; - *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } - init {|uid| - id = uid; - } + init {|uid| + id = uid; + } - fit{|dataset,action| + fit{|dataset,action| dataset.asSymbol.postln; - this.prSendMsg(\fit,[dataset.asSymbol],action); - } + this.prSendMsg(\fit, [dataset.asSymbol], action); + } - kNearest{ |buffer, k,action| - this.prSendMsg(\kNearest,[buffer.asUGenInput,k],action,k.collect{string(FluidMessageResponse,_,_)}); - } - - kNearestDist { |buffer, k,action| - this.prSendMsg(\kNearestDist,[buffer.asUGenInput,k],action,[numbers(FluidMessageResponse,_,k,_)]); - } + kNearest{ |buffer, k,action| + this.prSendMsg(\kNearest, + [buffer.asUGenInput,k], action, + k.collect{string(FluidMessageResponse,_,_)} + ); + } + kNearestDist { |buffer, k,action| + this.prSendMsg(\kNearestDist, [buffer.asUGenInput,k], action, + [numbers(FluidMessageResponse,_,k,_)] + ); + } } diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index 66877ef..679934e 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -11,9 +11,9 @@ See https://scikit-learn.org/stable/modules/neighbors.html#nearest-neighbor-algo CLASSMETHODS:: METHOD:: new -Make a new KDTree model for the given server +Make a new KDTree model for the given server. ARGUMENT:: server -The server on which to make the model +The server on which to make the model. INSTANCEMETHODS:: @@ -24,24 +24,24 @@ ARGUMENT:: dataset The LINK::Classes/FluidDataSet:: of interest. This can either be a data set object itself, or the name of one. ARGUMENT:: action -A function to run when indexing is complete +A function to run when indexing is complete. METHOD:: kNearest -Returns the IDs of the CODE::k:: points nearest to the one passed +Returns the IDs of the CODE::k:: points nearest to the one passed. ARGUMENT:: buffer A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. ARGUMENT:: k -The number of neighbours to return +The number of neighbours to return. ARGUMENT:: action A function that will run when the query returns, whose argument is an array of point IDs from the tree's LINK::Classes/FluidDataSet:: METHOD:: kNearestDist -Get the distances of the K nearest neighbours to a point +Get the distances of the K nearest neighbours to a point. ARGUMENT:: buffer A LINK::Classes/Buffer:: containing a data point to match against. The number of frames in the buffer must match the dimensionality of the LINK::Classes/FluidDataSet:: the tree was fitted to. @@ -50,7 +50,7 @@ ARGUMENT:: k The number of neighbours to search ARGUMENT:: action -A function that will run when the query returns, whose argument is an array of distances +A function that will run when the query returns, whose argument is an array of distances. EXAMPLES:: From 48ee95f77c1be5f404f52eca0c164b1a6d218ef1 Mon Sep 17 00:00:00 2001 From: Gerard Date: Thu, 11 Jun 2020 19:53:20 +0100 Subject: [PATCH 188/550] KMeans review --- release-packaging/Classes/FluidKMeans.sc | 56 +++++---- .../HelpSource/Classes/FluidKMeans.schelp | 106 ++++++++---------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/release-packaging/Classes/FluidKMeans.sc b/release-packaging/Classes/FluidKMeans.sc index 462f2f7..5ebd7cb 100644 --- a/release-packaging/Classes/FluidKMeans.sc +++ b/release-packaging/Classes/FluidKMeans.sc @@ -1,33 +1,43 @@ FluidKMeans : FluidManipulationClient { - var <>k; + var <>k; - *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } + *new {|server| + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } - init {|uid| - id = uid; - } + init {|uid| + id = uid; + } - fit{|dataset,k, maxIter = 100, buffer, action| - buffer = buffer ? -1; - this.k = k; - // this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter, buffer.asUGenInput],action,[numbers(FluidMessageResponse,_,k,_)]); - this.prSendMsg(\fit,[dataset.asSymbol, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); + fit{|dataset,k, maxIter = 100, action| + this.k = k; + this.prSendMsg(\fit, + [dataset.asSymbol, k,maxIter], action, + [numbers(FluidMessageResponse,_,k,_)] + ); } - fitPredict{|dataset,labelset, k, maxIter = 100, action| - this.k = k; - this.prSendMsg(\fitPredict,[dataset.asSymbol,labelset.asSymbol, k,maxIter],action,[numbers(FluidMessageResponse,_,k,_)]); - } + fitPredict{|dataset, labelset, k, maxIter = 100, action| + this.k = k; + this.prSendMsg(\fitPredict, + [dataset.asSymbol,labelset.asSymbol, k,maxIter], + action,[numbers(FluidMessageResponse,_,k,_)] + ); + } - predict{ |dataset, labelset,action| - this.prSendMsg(\predict,[dataset.asSymbol, labelset.asSymbol],action,[numbers(FluidMessageResponse,_,this.k,_)]); - } + predict{ |dataset, labelset,action| + this.prSendMsg(\predict, + [dataset.asSymbol, labelset.asSymbol], action, + [numbers(FluidMessageResponse,_,this.k,_)] + ); + } - predictPoint { |buffer, action| - this.prSendMsg(\predictPoint,[buffer.asUGenInput],action,[number(FluidMessageResponse,_,_)]); - } + predictPoint { |buffer, action| + this.prSendMsg(\predictPoint, + [buffer.asUGenInput], action, + [number(FluidMessageResponse,_,_)] + ); + } } diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index 83908b4..6912197 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -1,7 +1,7 @@ TITLE:: FluidKMeans summary:: Cluster data points with K-Means categories:: FluidManipulation -related:: Classes/FluidDataSet, Classes/FluidLabelSet, Classes/FluidKNN +related:: Classes/FluidDataSet, Classes/FluidLabelSet, Classes/FluidKNNClassifier, Classes/FluidKNNRegressor DESCRIPTION:: Uses the K-Means algorithm to learn clusters from a link::Classes/FluidDataSet:: @@ -11,9 +11,9 @@ https://scikit-learn.org/stable/tutorial/statistical_inference/unsupervised_lear CLASSMETHODS:: METHOD:: new -Construct a new K Means model on the passed server +Construct a new K Means model on the passed server. ARGUMENT:: server -If nil will use Server.default +If nil will use Server.default. INSTANCEMETHODS:: @@ -22,118 +22,108 @@ PRIVATE::k METHOD:: fit Identify code::k:: clusters in a link::Classes/FluidDataSet:: ARGUMENT:: dataset -A link::Classes/FluidDataSet:: of data points +A link::Classes/FluidDataSet:: of data points. ARGUMENT:: k -The number of clusters to identify in the data set +The number of clusters to identify in the data set. ARGUMENT:: maxIter -Maximum number of iterations to use partitioning the data -ARGUMENT:: buffer -Seed centroids for clusters WARNING:: Not yet implemented :: +Maximum number of iterations. ARGUMENT:: action -A function to run when fitting is complete, taking as its argument an array with the number of data points for each cluster +A function to run when fitting is complete, taking as its argument an array with the number of data points for each cluster. METHOD:: predict Given a trained object, return the cluster ID for each data point in a dataset to a label set. ARGUMENT:: dataset -a link::Classes/FluidDataSet:: containing the data to predict +a link::Classes/FluidDataSet:: containing the data to predict. ARGUMENT:: labelset -a link::Classes/FluidLabelSet:: to reveive the predicted clusters +a link::Classes/FluidLabelSet:: to retrieve the predicted clusters. ARGUMENT:: action -A function to run when the server responds +A function to run when the server responds. METHOD:: fitPredict Run link::Classes/FluidKMeans#*fit:: and link::Classes/FluidKMeans#*predict:: in a single pass: i.e. train the model on the incoming link::Classes/FluidDataSet:: and then return the learned clustering to the passed link::Classes/FluidLabelSet:: ARGUMENT:: dataset -a link::Classes/FluidDataSet:: containing the data to fit and predict +a link::Classes/FluidDataSet:: containing the data to fit and predict. ARGUMENT:: labelset -a link::Classes/FluidLabelSet:: to reveive the predicted clusters +a link::Classes/FluidLabelSet:: to retrieve the predicted clusters. ARGUMENT:: k -The number of clusters to identify in the data set +The number of clusters. ARGUMENT:: maxIter -Maximum number of iterations to use partitioning the data +Maximum number of iterations. ARGUMENT:: action A function to run when the server responds METHOD:: predictPoint Given a trained object, return the cluster ID for a data point in a link::Classes/Buffer:: ARGUMENT:: buffer -a link::Classes/Buffer:: containing a data point +a link::Classes/Buffer:: containing a data point. ARGUMENT:: action -A function to run when the server responds, taking the ID of the cluser as its argument +A function to run when the server responds, taking the ID of the cluster as its argument. METHOD:: predict -Report cluster assignments for previously unseen data +Report cluster assignments for previously unseen data. ARGUMENT:: dataset -A link::Classes/FluidDataSet:: of data points +A link::Classes/FluidDataSet:: of data points. ARGUMENT:: labelset -A link::Classes/FluidLabelSet:: to contain assigments +A link::Classes/FluidLabelSet:: to contain assignments. ARGUMENT:: action -A function to run when complete, taking an array of the counts for each catgegory as its argument +A function to run when complete, taking an array of the counts for each category as its argument. EXAMPLES:: Server.default.options.outDevice = "Built-in Output" code:: -//A dataset for our points, a labelset for cluster labels ( -~dataset= FluidDataSet(s,\kdtree_help_rand2d); - -~clusters = FluidLabelSet(s,\kmeans_help_clusters); -) - //Make some clumped 2D points and place into a dataset -( -~points = (4.collect{64.collect{(1.sum3rand) + [1,-1].choose}.clump(2)}).flatten(1) * 0.5; -~dataset.clear; -~tmpbuf = Buffer.alloc(s,2); +~points = (4.collect{ + 64.collect{(1.sum3rand) + [1,-1].choose}.clump(2) + }).flatten(1) * 0.5; fork{ + ~dataset = FluidDataSet.new(s,\kmeans_help_rand2d); + d = Dictionary.with( + *[\cols -> 2,\data -> Dictionary.newFrom( + ~points.collect{|x, i| [i, x]}.flatten)]); s.sync; - ~points.do{|x,i| - (""++(i+1)++"/128").postln; - ~tmpbuf.setn(0,x); - ~dataset.addPoint(i,~tmpbuf); - s.sync - } + ~dataset.load(d, {~dataset.print}); } ) -//Make a new k means model, fit it to the dataset and return the discovered clusters to a labelset -( -fork{ - ~clusters.clear; - ~kmeans = FluidKMeans(s); - s.sync; - ~kmeans.fitPredict(~dataset,~clusters, 4,action: {|c| + +// Create a KMeans instance and a LabelSet for the cluster labels in the server +~clusters = FluidLabelSet(s,\kmeans_help_clusters); +~kmeans = FluidKMeans(s); + +// Fit into 4 clusters +~kmeans.fitPredict(~dataset,~clusters, 4, action: {|c| "Fitted.\n # Points in each cluster:".postln; c.do{|x,i| ("Cluster" + i + "->" + x.asInteger + "points").postln; } }); -} -) -//Dims of kmeans should match dataset -~kmeans.cols -//Return labels of clustered points +// Cols of kmeans should match dataset, size is the number of clusters +~kmeans.cols; +~kmeans.size; +~kmeans.dump; + +// Retrieve labels of clustered points ( ~assignments = Array.new(128); fork{ - 128.do{ |i| - ~clusters.getLabel(i,{|clusterID| - (i.asString+clusterID).postln; - ~assignments.add(clusterID) - }); - s.sync; - } + 128.do{ |i| + ~clusters.getLabel(i,{|clusterID| + (i.asString+clusterID).postln; + ~assignments.add(clusterID) + }); + s.sync; + } } ) //Visualise: we're hoping to see colours neatly mapped to quandrants... ( d = ((~points + 1) * 0.5).flatten(1).unlace; -// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; w = Window("scatter", Rect(128, 64, 200, 200)); ~colours = [Color.blue,Color.red,Color.green,Color.magenta]; w.drawFunc = { From c64f8eba3fad9b4f705033c3f935f168a883590e Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Thu, 11 Jun 2020 20:05:43 +0100 Subject: [PATCH 189/550] fixing helper when no slices are return, and amended demo-dataset accordingly --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 ++ .../Examples/dataset/demo-dataset-maker-utilities.scd | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 0c6bf3b..b6efec9 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -98,6 +98,8 @@ FluidSliceCorpus { }{ var dict = IdentityDictionary(); dict.putAll(v); + dict[\prefix] = k; + dict[\index] = 1; index.add((k ++ "-1").asSymbol->dict); }; if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 16827b0..159483b 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -17,13 +17,14 @@ // here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers. ~extractor = FluidProcessSlices({|src,start,num,data| - var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice; - label = data.key; + var mfcc, stats, writer, flatten, label, index, voice; + label = data.value[\prefix]; + index = data.value[\index]; voice = data.value[\voice]; mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1); stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc)); flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(~ds,label,buf: ~flatbuf[voice],trig:Done.kr(flatten)) + writer = FluidDataSetWr.kr(~ds,label, index, ~flatbuf[voice], Done.kr(flatten)) }); ) From 1a01e7600131bfa523d9b5c640f589735176b470 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 11 Jun 2020 21:37:21 +0100 Subject: [PATCH 190/550] Guard against scsynth triggering u_cmd before constructor called --- include/FluidSCWrapper.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index f480e4b..711b5fa 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1213,6 +1213,12 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase using IndexList = typename Client::MessageSetType::template MessageDescriptorAt< N>::IndexList; + if(!x->init) + { + std::cout << "ERROR: Synth constructor not yet called" << std::endl; + return; + } + launchMessageImpl(x, args, IndexList()); } @@ -1421,6 +1427,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase << std::endl; } + bool init{false}; public: using Client = C; @@ -1432,6 +1439,7 @@ public: { client().setParams(params()); //<-IMPORTANT: client's ref to params is by address, and this has just changed impl::FluidSCWrapperBase::init(); + init = true; } ~FluidSCWrapper() From f93b2562cbb5a1cd69186a35ab05fe6cd6347f6b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 11:43:54 +0100 Subject: [PATCH 191/550] Add bypass for numerical index to FluidDataSetWr, update help --- include/clients/rt/FluidDataSetWr.hpp | 6 +++++- .../HelpSource/Classes/FluidDataSetWr.schelp | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index 7058289..1fbb006 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -39,7 +39,11 @@ public: if (auto datasetPtr = dataset.lock()) { std::stringstream ss; - ss << get<1>() << get<2>() + (mCounter++); + ss << get<1>(); + + index labelOffset = get<2>(); + if(labelOffset >= 0) ss << labelOffset + (mCounter++); + auto buf = get<3>(); return datasetPtr->addPoint(ss.str(), buf); } diff --git a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp index e39eb1d..3605a23 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetWr.schelp @@ -4,7 +4,17 @@ categories:: FluidManipulation related:: Classes/FLuidDataSet DESCRIPTION:: -A UGen that writes to a link::Classes/FluidDataSet:: +A UGen that adds labelled points to a link::Classes/FluidDataSet:: + +By default the object generates a numerical index that gets used for the point labels. This index starts counting from link::#offset#labelOffset:: and increments each time the Ugen is retriggered with a zero to non-zero transition. The label is then concatenated with the code::labelPrefix:: symbol, which is fixed at instantiation. In this way, one can make custom, incrementing labels, e.g. + +code:: +FluidDataSetWr.kr(~somedataset,"my_data",0,~somebuffer,trig) +:: + +would add points like code::my_data0, mydata1, mydata2...:: if successively retriggered. + +Alternatively, for one shot use you may not want a numerical suffix at all. Setting code::indexLabel:: < 0 will bypass this and use only the labelPrefix. As such, any retriggering will result in a complaint from the link::Classes/FluidDataSet:: that the requested label is already present. CLASSMETHODS:: @@ -20,12 +30,14 @@ ARGUMENT:: labelPrefix A string or symbol with a prefix for generated labels ARGUMENT:: labelOffset -An integer with the offset to start labeling from. If the UGen is run in a server-side loop (i.e. repeatedly retriggered), the generated labels will count upwards from this offset. +ANCHOR::offset:: +An integer with the offset to start labeling from. If the UGen is run in a server-side loop (i.e. repeatedly retriggered), the generated labels will count upwards from this offset. If < 0, then no numerical index will be applied to the generated label (i.e. only the labelPrefix is used). ARGUMENT:: buf The link::Classes/Buffer:: containing the data point. ARGUMENT:: trig + A kr trigger signal EXAMPLES:: From 16111521e5e26e415be385587dd0726ea2a58f6d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 11:44:46 +0100 Subject: [PATCH 192/550] Revert "fixing helper when no slices are return, and amended demo-dataset accordingly" This reverts commit c64f8eba3fad9b4f705033c3f935f168a883590e. --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 -- .../Examples/dataset/demo-dataset-maker-utilities.scd | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index b6efec9..0c6bf3b 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -98,8 +98,6 @@ FluidSliceCorpus { }{ var dict = IdentityDictionary(); dict.putAll(v); - dict[\prefix] = k; - dict[\index] = 1; index.add((k ++ "-1").asSymbol->dict); }; if(jobs.size > 0){perf.value(tmpIndices)}{ tmpIndices.free }; diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 159483b..16827b0 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -17,14 +17,13 @@ // here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers. ~extractor = FluidProcessSlices({|src,start,num,data| - var mfcc, stats, writer, flatten, label, index, voice; - label = data.value[\prefix]; - index = data.value[\index]; + var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice; + label = data.key; voice = data.value[\voice]; mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1); stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc)); flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(~ds,label, index, ~flatbuf[voice], Done.kr(flatten)) + writer = FluidDataSetWr.kr(~ds,label,buf: ~flatbuf[voice],trig:Done.kr(flatten)) }); ) From 7a1bdc5a0294724c9814ba6119c9f0fc10d9f62c Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 11:53:08 +0100 Subject: [PATCH 193/550] Revert changes to Corpus Builders and help files now that we have bypass in FluidDataWr --- include/clients/rt/FluidDataSetWr.hpp | 17 +++++++---------- .../Classes/FluidCorpusBuilders.sc | 5 ++--- .../Classes/FluidProcessSlices.schelp | 7 +++---- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index 1fbb006..20fcf0f 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -23,19 +23,18 @@ namespace client { class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut { public: - FLUID_DECLARE_PARAMS( - DataSetClientRef::makeParam("dataSet", "DataSet Name"), - StringParam("labelPrefix","Label Prefix"), - LongParam("labelOffset", "Label Counter Offset", 0), - BufferParam("buf", "Data Buffer") - ); + FLUID_DECLARE_PARAMS(StringParam("label", "Label"), + BufferParam("buf", "Data Buffer"), + DataSetClientRef::makeParam("dataSet", "DataSet Name")); DataSetWriterClient(ParamSetViewType& p) : mParams(p) {} template Result process(FluidContext&) { - auto dataset = get<0>().get(); + auto& idx = get<0>(); + auto buf = get<1>(); + auto dataset = get<2>().get(); if (auto datasetPtr = dataset.lock()) { std::stringstream ss; @@ -43,6 +42,7 @@ public: index labelOffset = get<2>(); if(labelOffset >= 0) ss << labelOffset + (mCounter++); + auto buf = get<3>(); return datasetPtr->addPoint(ss.str(), buf); @@ -50,9 +50,6 @@ public: else return {Result::Status::kError, "No dataset"}; } - - private: - index mCounter{0}; }; using NRTThreadedDataSetWriter = diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 0c6bf3b..af7d58f 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -83,14 +83,13 @@ FluidSliceCorpus { var rawPoints = Array.newFrom(a).asInteger; if(rawPoints[0] != [v[\bounds][0]]){rawPoints = [v[\bounds][0]] ++ rawPoints}; if(rawPoints.last != [v[\bounds][1]]){rawPoints=rawPoints ++ [v[\bounds][1]]}; + rawPoints.doAdjacentPairs{|a,b| var dict; if ((b - a) >= 1){ dict = IdentityDictionary(); dict.putAll(v); dict[\bounds] = [a,b]; - dict[\prefix] = k; - dict[\index] = sliceindex; index.add(((k ++ "-" ++sliceindex).asSymbol)->dict); sliceindex = sliceindex + 1; } @@ -141,7 +140,7 @@ FluidProcessSlices{ counter = counter + 1; ("Processing" + counter ++ "/" ++ total).postln; idx = counter; - v[\allindex] = counter; + v[\index] = counter; v[\voice] = jobID; OSCFunc({ completed = completed + 1; diff --git a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp index 727a30a..4fcee45 100644 --- a/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp +++ b/release-packaging/HelpSource/Classes/FluidProcessSlices.schelp @@ -107,13 +107,12 @@ s.reboot; //write pitch statistics into a dataset ( ~extractor = FluidProcessSlices({|src,start,num,data| - var pitch, stats, labelPrefix, labelIndex, i; + var pitch, stats, label,i; i = data.value[\voice]; - labelPrefix = data.value[\prefix]; - labelIndex = data.value[\index]; + label = data.key; pitch = FluidBufPitch.kr(src,start,num,features:~pitchbufs[i]); stats = FluidBufStats.kr(~pitchbufs[i],stats:~statsbufs[i],trig:Done.kr(pitch)); - FluidDataSetWr.kr(~pitchdata,labelPrefix,labelIndex,buf:~statsbufs[i],trig:Done.kr(stats)) + FluidDataSetWr.kr(~pitchdata,label,-1,buf:~statsbufs[i],trig:Done.kr(stats)) }); ) From 5bd0832981c36fdbb259988ceb6307fa7017123b Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 12:13:45 +0100 Subject: [PATCH 194/550] Restore FluidDataWr C++ atfer overzealous revert --- include/clients/rt/FluidDataSetWr.hpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index 20fcf0f..1fbb006 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -23,18 +23,19 @@ namespace client { class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut { public: - FLUID_DECLARE_PARAMS(StringParam("label", "Label"), - BufferParam("buf", "Data Buffer"), - DataSetClientRef::makeParam("dataSet", "DataSet Name")); + FLUID_DECLARE_PARAMS( + DataSetClientRef::makeParam("dataSet", "DataSet Name"), + StringParam("labelPrefix","Label Prefix"), + LongParam("labelOffset", "Label Counter Offset", 0), + BufferParam("buf", "Data Buffer") + ); DataSetWriterClient(ParamSetViewType& p) : mParams(p) {} template Result process(FluidContext&) { - auto& idx = get<0>(); - auto buf = get<1>(); - auto dataset = get<2>().get(); + auto dataset = get<0>().get(); if (auto datasetPtr = dataset.lock()) { std::stringstream ss; @@ -42,7 +43,6 @@ public: index labelOffset = get<2>(); if(labelOffset >= 0) ss << labelOffset + (mCounter++); - auto buf = get<3>(); return datasetPtr->addPoint(ss.str(), buf); @@ -50,6 +50,9 @@ public: else return {Result::Status::kError, "No dataset"}; } + + private: + index mCounter{0}; }; using NRTThreadedDataSetWriter = From 0630929bd0f3cd5056aa1a3ad7e03499c3fe2b8c Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 12 Jun 2020 12:30:47 +0100 Subject: [PATCH 195/550] mod the example of dataset building with the new sexy -1 bypasser --- .../Examples/dataset/demo-dataset-maker-utilities.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd index 16827b0..4d0e313 100644 --- a/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/demo-dataset-maker-utilities.scd @@ -23,7 +23,7 @@ mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1); stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc)); flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats)); - writer = FluidDataSetWr.kr(~ds,label,buf: ~flatbuf[voice],trig:Done.kr(flatten)) + writer = FluidDataSetWr.kr(~ds,label, -1, ~flatbuf[voice], Done.kr(flatten)) }); ) From 05c5b3da3cf95a8eb60371fe12d5d72a2faa6beb Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 13:30:56 +0100 Subject: [PATCH 196/550] Make FluidBufAudioTransport.sc conform to general NRT class structure --- .../Classes/FluidBufAudioTransport.sc | 67 +++++++------------ 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 52069e6..8085c6f 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -1,59 +1,40 @@ FluidBufAudioTransport : UGen{ - - var <>synth, <>server; - - *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + *new1 { |rate, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking=0| var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var blocking = 0; - source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; + source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; source1 = source1.asUGenInput; - source2 = source2.asUGenInput; + source2 = source2.asUGenInput; + + destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + destination = destination.asUGenInput; - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; - destination = destination.asUGenInput; //NB For wrapped versions of NRT classes, we set the params for maxima to //whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value) + ^super.new1(rate,source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize, trig,blocking) - ^this.multiNew(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize, trig,blocking); } - - *process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action| - var synth, instance; - - - source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw}; - source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw}; - - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; + *kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1| + ^this.new1(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, trig); + } - source1 = source1.asUGenInput; - source2 = source2.asUGenInput; - destination = destination.asUGenInput; - - server = server ? Server.default; - server.ifNotRunning({ - "WARNING: Server not running".postln; - ^nil; - }); - synth = { instance = FluidBufAudioTransport.kr(source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, trig:1)}.play(server); - forkIfNeeded{ - synth.waitForFree; - server.sync; - if (destination != -1) {destination = server.cachedBufferAt(destination); destination.updateInfo; server.sync;} {destination = nil}; - action.value(destination); - }; - instance.synth = synth; - instance.server = server; - ^instance; + *process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action| + ^FluidNRTProcess.new( + server, this, action, [destination] + ).process( + source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize + ) } - cancel{ - if(this.server.notNil) - {this.server.sendMsg("/u_cmd", this.synth.nodeID, this.synthIndex, "cancel")}; - } + *processBlocking { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action| + ^FluidNRTProcess.new( + server, this, action, [destination],blocking:1 + ).process( + source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize + ) + } } + From 80eca48f41a994de9fb6c76eecad02d702705178 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 12 Jun 2020 14:43:07 +0100 Subject: [PATCH 197/550] sort small typos and clarify examples for alpha03 --- .../HelpSource/Classes/FluidAudioTransport.schelp | 2 +- .../HelpSource/Classes/FluidBufAudioTransport.schelp | 2 +- .../HelpSource/Classes/FluidBufThreadDemo.schelp | 4 ++-- release-packaging/HelpSource/Classes/FluidMFCC.schelp | 5 +++-- release-packaging/HelpSource/Classes/FluidNMFFilter.schelp | 6 +++--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp index d973cd8..b8099e6 100644 --- a/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidAudioTransport.schelp @@ -44,5 +44,5 @@ EXAMPLES:: code:: //the mouse X axis interpolates between the two sinewaves -{FluidAudioTransport.ar(SinOsc.ar(220),SinOsc.ar(440),MouseX.kr())}.play; +{FluidAudioTransport.ar(SinOsc.ar(220,mul: 0.1),SinOsc.ar(440,mul: 0.02),MouseX.kr())}.play; :: diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index eea567a..6177727 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -81,7 +81,7 @@ code:: //Make 2 sources to be interpolated ( b = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi).sin * 0.1})); -c = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi / 2).sin * 0.1})); +c = Buffer.loadCollection(s, FloatArray.fill(44100, {|a|(a / pi / 2).sin * 0.02})); d = Buffer.new ) diff --git a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp index 99427b3..ea798a9 100644 --- a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp @@ -60,8 +60,8 @@ FluidBufThreadDemo.process(s, b, 1000, {|x|x.get(0,{|y|y.postln});}); // as the 'process' returns its parent UGen, we can cancel the process easily c = FluidBufThreadDemo.process(s, b, 100000, {|x|x.get(0,{|y|y.postln});}); -c.cancel +c.cancel //it stops silently for now but check the synth count going down by 1. -// if a simple call to the UGen is used, the progress can be monitored +// if a simple call to the UGen is used, the progress can be monitored. The usual cmd. will cancel the job by freeing the synth. {c = FluidBufThreadDemo.kr(b,10000, Done.freeSelf); Poll.kr(Impulse.kr(2),c);}.scope; :: diff --git a/release-packaging/HelpSource/Classes/FluidMFCC.schelp b/release-packaging/HelpSource/Classes/FluidMFCC.schelp index 4fb62cf..b799bbb 100644 --- a/release-packaging/HelpSource/Classes/FluidMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidMFCC.schelp @@ -61,7 +61,7 @@ a.reference_(Array.fill(13,{0.5})); //make a center line to show 0 //run the window updating routine. ( -~winRange = 500; +~winRange = 200; r = Routine { { @@ -89,7 +89,7 @@ x = {arg type = 0; // change the wave types, observe the amplitude invariance of the descriptors, apart from the leftmost coefficient x.set(\type, 1) -~winRange = 100; //adjust the range above and below 0 to zoom in or out on the MFCC +~winRange = 50; //adjust the range above and below 0 to zoom in or out on the MFCC x.set(\type, 2) x.set(\type, 0) // free this source @@ -106,6 +106,7 @@ x = {arg bands = 40, low = 20, high = 20000; source.dup; }.play; ) +~winRange = 10; //adjust the range above and below 0 to zoom in or out on the MFCC // observe the number of bands. The unused ones at the top are not updated x.set(\bands,20) diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index a5efa31..fbe8375 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -89,11 +89,11 @@ Routine { e.plot //listen how the filter isolates each component and places them in each channel separately. -{FluidNMFFilter.ar(SinOsc.ar(500),e,2)}.play +{FluidNMFFilter.ar(SinOsc.ar(500, mul: 0.1),e,2)}.play -{FluidNMFFilter.ar(SinOsc.ar(5000),e,2)}.play +{FluidNMFFilter.ar(SinOsc.ar(5000, mul: 0.1),e,2)}.play -{FluidNMFFilter.ar(SinOsc.ar([500,5000]).sum,e,2)}.play +{FluidNMFFilter.ar(SinOsc.ar([500,5000], mul: 0.1).sum,e,2)}.play :: STRONG::A guitar processor:: From 81f11f7a41676105f3f1837173046a286a9f0dc3 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 12 Jun 2020 14:48:38 +0100 Subject: [PATCH 198/550] bring back improvements on fluidbufpitch demo --- release-packaging/HelpSource/Classes/FluidBufPitch.schelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index f202e83..4caac6a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -156,7 +156,7 @@ d.postln e = Array.new; d.do({ arg val, i; - if ((val[0] > 500) && (val[1] > 0.666)) {e = e.add(i)}; // if pitch is greater than 500Hz and confidence higher than 0.666, keep the index + if ((val[0] > 500) && (val[1] > 0.98)) {e = e.add(i)}; // if pitch is greater than 500Hz and confidence higher than 0.98, keep the index }); ) e.postln; From 5eecb02ab8c1a761c11cdf7b09b2c238b300bd2d Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 15:18:01 +0100 Subject: [PATCH 199/550] Update NRT params on retrigger --- include/FluidSCWrapper.hpp | 73 +++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 711b5fa..53a2363 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -53,6 +53,39 @@ struct WrapperState Result mResult{}; }; +/// Named, shared clients already have a lookup table in their adaptor class +template +struct IsPersistent +{ + using type = std::false_type; +}; + +//TODO: make less tied to current implementation +template +struct IsPersistent>> +{ + using type = std::true_type; +}; + +template +using IsPersistent_t = typename IsPersistent::type; + +/// Models don't, but still need to survive CMD-. +template +struct IsModel +{ + using type = std::false_type; +}; + +template +struct IsModel>> +{ + using type = typename ClientWrapper::isModelObject; +}; + +template +using IsModel_t = typename IsModel::type; + namespace impl { template @@ -301,7 +334,7 @@ class NonRealTime : public SCUnit using SharedState = std::shared_ptr>; public: - static index ControlOffset(Unit*) { return 0; } + static index ControlOffset(Unit*) { return IsModel_t::value ? 1 : 0; } static index ControlSize(Unit* unit) { return index(unit->mNumInputs) - unit->mSpecialIndex - 2; } static void setup(InterfaceTable* ft, const char* name) @@ -383,6 +416,11 @@ public: mWrapper->mDone = sharedState->mJobDone; if(trigger) { + mWrapper->mControlsIterator.reset(mInBuf + ControlOffset(this)); + Wrapper::setParams(mWrapper, + mWrapper->params(), mWrapper->mControlsIterator); // forward on inputs N + audio inputs as params + mWrapper->params().constrainParameterValues(); + SharedState* statePtr = static_cast(mWorld->ft->fRTAlloc(mWorld, sizeof(SharedState))); statePtr = new (statePtr) SharedState(sharedState); mFifoMsg.Set(mWorld, initNRTJob, nullptr, statePtr); @@ -591,38 +629,7 @@ class NonRealTimeAndRealTime : public RealTime, //Discovery for clients that need persistent storage (Dataset and friends) -/// Named, shared clients already have a lookup table in their adaptor class -template -struct IsPersistent -{ - using type = std::false_type; -}; -//TODO: make less tied to current implementation -template -struct IsPersistent>> -{ - using type = std::true_type; -}; - -template -using IsPersistent_t = typename IsPersistent::type; - -/// Models don't, but still need to survive CMD-. -template -struct IsModel -{ - using type = std::false_type; -}; - -template -struct IsModel>> -{ - using type = typename ClientWrapper::isModelObject; -}; - -template -using IsModel_t = typename IsModel::type; template struct LifetimePolicy; @@ -668,7 +675,7 @@ struct LifetimePolicy static void constructClass(Unit* unit) { index uid = static_cast(unit->mInBuf[Wrapper::ControlOffset(unit)][0]); - FloatControlsIter controlsReader{unit->mInBuf + 1 + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; + FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; auto& entry = mRegistry[uid]; auto& client = entry.client; auto& params = entry.params; From c129ad78b5b6db49d891149d20f3a70a466844f6 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 15:42:15 +0100 Subject: [PATCH 200/550] Normalize / Standardize review --- release-packaging/Classes/FluidNormalize.sc | 30 ++++++++------ release-packaging/Classes/FluidStandardize.sc | 30 ++++++++------ .../HelpSource/Classes/FluidNormalize.schelp | 40 ++++++------------- .../Classes/FluidStandardize.schelp | 26 ++---------- 4 files changed, 52 insertions(+), 74 deletions(-) diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 3f66e49..07b77a1 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -9,19 +9,25 @@ FluidNormalize : FluidManipulationClient { id = uid; } - fit{|dataset, action| - this.prSendMsg(\fit,[dataset.asSymbol],action); - } + fit{|dataset, action| + this.prSendMsg(\fit,[dataset.asSymbol], action); + } - fitTransform{|dataset, action| - this.prSendMsg(\fit,[dataset.asSymbol],action); - } + transform{|sourceDataset, destDataset, action| + this.prSendMsg(\transform, + [sourceDataset.asSymbol, destDataset.asSymbol], action + ); + } - transform{|sourceDataset, destDataset, action| - this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); - } + fitTransform{|sourceDataset, destDataset, action| + this.prSendMsg(\fitTransform, + [sourceDataset.asSymbol, destDataset.asSymbol], action + ); + } - transformPoint{|sourceBuffer, destBuffer, action| - this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); - } + transformPoint{|sourceBuffer, destBuffer, action| + this.prSendMsg(\transformPoint, + [sourceBuffer.asUGenInput, destBuffer.asUGenInput], action + ); + } } diff --git a/release-packaging/Classes/FluidStandardize.sc b/release-packaging/Classes/FluidStandardize.sc index cd30407..e792918 100644 --- a/release-packaging/Classes/FluidStandardize.sc +++ b/release-packaging/Classes/FluidStandardize.sc @@ -9,19 +9,25 @@ FluidStandardize : FluidManipulationClient { id = uid; } - fit{|dataset, action| - this.prSendMsg(\fit,[dataset.asSymbol],action); - } + fit{|dataset, action| + this.prSendMsg(\fit, [dataset.asSymbol], action); + } - transform{|sourceDataset, destDataset, action| - this.prSendMsg(\transform,[sourceDataset.asSymbol, destDataset.asSymbol],action); - } + transform{|sourceDataset, destDataset, action| + this.prSendMsg(\transform, + [sourceDataset.asSymbol, destDataset.asSymbol], action + ); + } - fitTransform{|dataset, action| - this.prSendMsg(\fitTransform,[dataset.asSymbol],action); - } + fitTransform{|sourceDataset, destDataset, action| + this.prSendMsg(\fitTransform, + [sourceDataset.asSymbol, destDataset.asSymbol], action + ); + } - transformPoint{|sourceBuffer, destBuffer, action| - this.prSendMsg(\transformPoint,[sourceBuffer.asUGenInput, destBuffer.asUGenInput],action); - } + transformPoint{|sourceBuffer, destBuffer, action| + this.prSendMsg(\transformPoint, + [sourceBuffer.asUGenInput, destBuffer.asUGenInput], action + ); + } } diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index a416063..8fd2694 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -4,7 +4,7 @@ categories:: FluidManipulation related:: Classes/FluidStandardize, Classes/FluidDataSet DESCRIPTION:: -Normalize the entries of a link::Classes/FluidDataSet::, or normalize a data point according to the learned bounrds of a data set. On the server. +Normalize the entries of a link::Classes/FluidDataSet::, or normalize a data point according to the learned bounds of a data set. On the server. See http://www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html @@ -25,21 +25,24 @@ Maximum output value, default 1 INSTANCEMETHODS:: METHOD:: fit -Normalize a link::Classes/FluidDataSet:: strong::in-place:: +Compute the normalization factors from a link::Classes/FluidDataSet:: for later. ARGUMENT:: dataset The link::Classes/FluidDataSet:: to normalize ARGUMENT:: action A function to run when processing is complete -METHOD:: fitTransform -Normalize a link::Classes/FluidDataSet:: strong::in-place:: -ARGUMENT:: dataset +METHOD:: transform +Normalize a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet::, using the learned extrema from a previous call to link::Classes/FluidNormalize#fit:: +ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to normalize +ARGUMENT:: destDataset +The link::Classes/FluidDataSet:: to populate with normalized data ARGUMENT:: action A function to run when processing is complete -METHOD:: transform -Normalize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: + +METHOD:: fitTransform +Normalize a link::Classes/FluidDataSet:: ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to normalize ARGUMENT:: destDataset @@ -47,8 +50,9 @@ The link::Classes/FluidDataSet:: to populate with normalized data ARGUMENT:: action A function to run when processing is complete + METHOD:: transformPoint -Normalize a new data point, using the learned extrema from a previous link::Classes/FluidNormalize#fit::ting +Normalize a new data point, using the learned extrema from a previous call to link::Classes/FluidNormalize#fit:: ARGUMENT:: sourceBuffer A link::Classes/Buffer:: with the new data point ARGUMENT:: destBuffer @@ -56,26 +60,6 @@ A link::Classes/Buffer:: to contain the normalized value ARGUMENT:: action A function to run when processing is complete -METHOD:: cols -Retreive the dimensionality of the data set we have fitted on -ARGUMENT:: action -A function to run when the server responds, taking the dimensions as its argument - -METHOD:: read -Load internal state (dimensionality, mins, maxes) from a JSON file -ARGUMENT:: filename -Absolute path to the JSON file -ARGUMENT:: action -A function to run when file is loaded - -METHOD:: write -Store the internal state of object on disk as a JSON file. Will not overwrite existing files -ARGUMENT:: filename -Absolute path of file to write -ARGUMENT:: action -A function to run when file is written - - EXAMPLES:: code:: s.boot; diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index 217799b..a538a07 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -25,7 +25,7 @@ ARGUMENT:: action A function to run when processing is complete METHOD:: transform -Standrdize a link::Classes/FluidDataSet:: non-destructively into another link::Classes/FluidDataSet:: +Standardize a link::Classes/FluidDataSet::, using the learned statistics from a previous call to link::Classes/FluidStandardize#fit:: ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to standardize ARGUMENT:: destDataset @@ -34,14 +34,14 @@ ARGUMENT:: action A function to run when processing is complete METHOD:: fitTransform -Standrdize a link::Classes/FluidDataSet:: fit and scale a link::Classes/FluidDataSet:: in-place +Standardize a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet:: ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to standardize ARGUMENT:: action A function to run when processing is complete METHOD:: transformPoint -Standardize a new data point, using the learned statistics from a previous link::Classes/FluidStandardize#fit::ting +Standardize a new data point, using the learned statistics from a previous call to link::Classes/FluidStandardize#fit:: ARGUMENT:: sourceBuffer A link::Classes/Buffer:: with the new data point ARGUMENT:: destBuffer @@ -49,24 +49,6 @@ A link::Classes/Buffer:: to contain the standardize value ARGUMENT:: action A function to run when processing is complete -METHOD:: cols -Retreive the dimensionality of the data set we have fitted on -ARGUMENT:: action -A function to run when the server responds, taking the dimensions as its argument. Will print to post window by default - -METHOD:: read -Load internal state (dimensionality, means, deviations) from a JSON file -ARGUMENT:: filename -Absolute path to the JSON file -ARGUMENT:: action -A function to run when file is loaded - -METHOD:: write -Store the internal state of object on disk as a JSON file. -ARGUMENT:: filename -Absolute path of file to write -ARGUMENT:: action -A function to run when file is written EXAMPLES:: code:: @@ -138,4 +120,4 @@ fork{ ~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; ) -:: \ No newline at end of file +:: From b2338ed9d9414345de1e57447decc52ad010e432 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 17:57:52 +0100 Subject: [PATCH 201/550] FluidNormalize: review example --- .../HelpSource/Classes/FluidNormalize.schelp | 79 +++++++++---------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index 8fd2694..a28d6c2 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -68,66 +68,59 @@ s.boot; ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; ~raw = FluidDataSet(s,\norm_help_raw); ~norm = FluidDataSet(s,\norm_help_normd); -~audio = Buffer.read(s,~audiofile); ~pitch_feature = Buffer.new(s); -~stats = Buffer.new(s); -~datapoint = Buffer.alloc(s,2); +~stats = Buffer.alloc(s, 7, 2); ~normalizer = FluidNormalize(s); ) -// Do a pitch analysis on the audio, which gives us pitch and pitch confidence (so a 2D datum) +// Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) +( +~audio = Buffer.read(s,~audiofile); +FluidBufPitch.process(s,~audio, features: ~pitch_feature); +) + // Divide the time series in to 10, and take the mean of each segment and add this as a point to // the 'raw' FluidDataSet ( -~raw.clear; -~norm.clear; -FluidBufPitch.process(s,~audio,features:~pitch_feature,action:{ - "Pitch analysis.complete. Doing stats".postln; - fork{ - var chunkLen = (~pitch_feature.numFrames / 10).asInteger; - 10.do{ |i| - s.sync; FluidBufStats.process(s,~pitch_feature,startFrame:i*chunkLen,numFrames:chunkLen,stats:~stats, action:{ - ~stats.loadToFloatArray(action:{ |statsdata| - [statsdata[0],statsdata[1]].postln; - ~datapoint.setn(0,[statsdata[0],statsdata[1]]); - s.sync; - ("Adding point" ++ i).postln; - ~raw.addPoint(i,~datapoint); - }) - }); - if(i == 9) {"Analysis done, dataset ready".postln} - } - } -}); +{ + var trig = LocalIn.kr(1, 1); + var buf = LocalBuf(2, 1); + var count = PulseCount.kr(trig) - 1; + var chunkLen = (~pitch_feature.numFrames / 10).asInteger; + var stats = FluidBufStats.kr( + source: ~pitch_feature, startFrame: count * chunkLen, + numFrames: chunkLen, stats: ~stats, trig: trig + ); + var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence + var wr1 = BufWr.kr(rd[0], buf, DC.kr(0)); + var wr2 = BufWr.kr(rd[1], buf, DC.kr(1)); + var dsWr = FluidDataSetWr.kr(\norm_help_raw, buf: buf, trig: Done.kr(stats)); + LocalOut.kr( Done.kr(dsWr)); + FreeSelf.kr(count - 9); +}.play; ) -//Fit the FluidNormalizer to the raw data, and then apply the scaling out of place into -//our second FluidDataSet, so we can compare. -//Download the dataset contents into arrays for plotting + +// Normalize and load to language-side array ( -~normalizer.fit(~raw); -~normalizer.transform(~raw,~norm); ~rawarray = Array.new(10); ~normedarray= Array.new(10); -fork{ - 10.do{|i| - ~raw.getPoint(i,~datapoint,{ - ~datapoint.loadToFloatArray(action:{|a| ~rawarray.add(Array.newFrom(a))}) - }); - s.sync; - ~norm.getPoint(i,~datapoint,{ - - ~datapoint.loadToFloatArray(action:{|a| ~normedarray.add(Array.newFrom(a))}) - }); - s.sync; - if(i==9){"Data downloaded".postln}; - } -} +~normalizer.fitTransform(~raw,~norm, { + ~raw.dump{|x| 10.do{|i| + ~rawarray.add(x["data"][i.asString]) + }}; + ~norm.dump{|x| 10.do{|i| + ~normedarray.add(x["data"][i.asString]) + }}; +}); ) + //Plot side by side. Before normalization the two dimensions have radically different scales //which can be unhelpful in many cases + ( ~rawarray.flatten(1).unlace.plot("Unnormalized",Rect(0,0,400,400),minval:0,maxval:[5000,1]).plotMode=\bars; ~plot2 = ~normedarray.flatten(1).unlace.plot("Normalized",Rect(410,0,400,400)).plotMode=\bars; ) + :: From d96dbb8791410ebec5c78aa8f7ed8d89517d1cf0 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 18:04:11 +0100 Subject: [PATCH 202/550] KNNClassifier / Regressor review --- .../Classes/FluidKNNClassifier.sc | 31 +++++++++---------- .../Classes/FluidKNNRegressor.sc | 30 +++++++++--------- .../Classes/FluidKNNClassifier.schelp | 8 +++-- .../Classes/FluidKNNRegressor.schelp | 9 ++++-- 4 files changed, 41 insertions(+), 37 deletions(-) diff --git a/release-packaging/Classes/FluidKNNClassifier.sc b/release-packaging/Classes/FluidKNNClassifier.sc index 7db9271..5f57e6e 100644 --- a/release-packaging/Classes/FluidKNNClassifier.sc +++ b/release-packaging/Classes/FluidKNNClassifier.sc @@ -1,30 +1,29 @@ FluidKNNClassifier : FluidManipulationClient { *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } - init {|uid| - id = uid; - } + init {|uid| + id = uid; + } - fit{|dataset, labelset, action| - this.prSendMsg(\fit,[dataset.asSymbol, labelset.asSymbol], action); - } + fit{|dataset, labelset, action| + this.prSendMsg(\fit,[dataset.asSymbol, labelset.asSymbol], action); + } - predict{ |dataset, labelset, k, uniform = 0, action| - this.prSendMsg(\predict, + predict{|dataset, labelset, k, uniform = 0, action| + this.prSendMsg(\predict, [dataset.asSymbol, labelset.asSymbol, k, uniform], action); - } + } - predictPoint { |buffer, k, uniform = 0, action| - this.prSendMsg(\predictPoint, - [buffer.asUGenInput, k,uniform], action, + predictPoint {|buffer, k, uniform = 0, action| + this.prSendMsg(\predictPoint, + [buffer.asUGenInput, k, uniform], action, [string(FluidMessageResponse,_,_)] ); } - } diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index 74c0023..e7519db 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -1,29 +1,29 @@ FluidKNNRegressor : FluidManipulationClient { *new {|server| - var uid = UniqueID.next; - ^super.new(server,uid)!?{|inst|inst.init(uid);inst} - } + var uid = UniqueID.next; + ^super.new(server,uid)!?{|inst|inst.init(uid);inst} + } - init {|uid| - id = uid; - } + init {|uid| + id = uid; + } - fit{|sourceDataset, targetDataset, action| - this.prSendMsg(\fit, + fit{|sourceDataset, targetDataset, action| + this.prSendMsg(\fit, [sourceDataset.asSymbol, targetDataset.asSymbol], action ); - } + } - predict{ |sourceDataset, targetDataset, k, uniform = 0, action| - this.prSendMsg(\predict, + predict{ |sourceDataset, targetDataset, k, uniform = 0, action| + this.prSendMsg(\predict, [sourceDataset.asSymbol, targetDataset.asSymbol, k, uniform], action); - } + } - predictPoint { |buffer, k, uniform = 0, action| - this.prSendMsg(\predictPoint, [buffer.asUGenInput, k,uniform], action, + predictPoint { |buffer, k, uniform = 0, action| + this.prSendMsg(\predictPoint, [buffer.asUGenInput, k, uniform], action, [number(FluidMessageResponse,_,_)]); - } + } } diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp index d76b446..f9aea85 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -4,6 +4,8 @@ categories:: Classification, KNN related:: Classes/FluidKNNRegressor, Classes/FluidDataSet, Classes/FluidLabelSet DESCRIPTION:: +A nearest-neighbor classifier using link::Classes/FluidKDTree:: . Each point is assigned the class that is most common among its nearest neighbors. +https://scikit-learn.org/stable/modules/neighbors.html#classification CLASSMETHODS:: @@ -15,7 +17,7 @@ The server to make the model on INSTANCEMETHODS:: METHOD:: fit -Fit the model to a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet:: . These need to be the sime size +Fit the model to a source link::Classes/FluidDataSet:: and a target link::Classes/FluidLabelSet::. These need to be the same size ARGUMENT:: dataset Source data ARGUMENT:: labelset @@ -32,7 +34,7 @@ place to write labels ARGUMENT:: k the number of neighours to consider ARGUMENT:: uniform -true / false: whether the neighbours shold be weighted by distance +true / false: whether the neighbours should be weighted by distance ARGUMENT:: action Run when done @@ -43,7 +45,7 @@ A data point ARGUMENT:: k Number of neighbours to consider ARGUMENT:: uniform -true / false: whether the neighbours shold be weighted by distance +true / false: whether the neighbours should be weighted by distance (default) or uniformly ARGUMENT:: action Run when done, passes predicted label as argument diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp index e5d576c..6336470 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -4,6 +4,9 @@ categories:: Regression related:: Classes/FluidKNNClassifier, Classes/FluidDataSet DESCRIPTION:: +A nearest-neighbor regressor. A continuous value is predicted for each point as the (weighted) average value of its nearest neighbors. + +https://scikit-learn.org/stable/modules/neighbors.html#regression CLASSMETHODS:: @@ -15,7 +18,7 @@ The server to run this model on. INSTANCEMETHODS:: METHOD:: fit -Map a source link::Classes/FluidDataSet:: to a target; they must be the same size, but can have different dimensionality +Map a source link::Classes/FluidDataSet:: to a one-dimensional target; both datasets need to have the same number of points. ARGUMENT:: sourceDataset Source data ARGUMENT:: targetDataset @@ -44,7 +47,7 @@ data point ARGUMENT:: k number of neigbours to consider in mapping, min 1 ARGUMENT:: uniform -Whether to weight neighbours by distance when producing new point +Whether the neighbours should be weighted by distance (default) or uniformly ARGUMENT:: action Run when done @@ -105,4 +108,4 @@ fork{ ) //We should see a single cycle of a chirp ~outputdata.plot; -:: \ No newline at end of file +:: From ac3a524ffdf8b26b788b42383b7b5e7c998ec34c Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 18:04:47 +0100 Subject: [PATCH 203/550] PCA / MDS help file review --- release-packaging/Classes/FluidMDS.sc | 5 +++-- release-packaging/HelpSource/Classes/FluidMDS.schelp | 2 ++ release-packaging/HelpSource/Classes/FluidPCA.schelp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidMDS.sc b/release-packaging/Classes/FluidMDS.sc index 1d5098f..9b10024 100644 --- a/release-packaging/Classes/FluidMDS.sc +++ b/release-packaging/Classes/FluidMDS.sc @@ -17,9 +17,10 @@ FluidMDS : FluidManipulationClient { } fitTransform{|sourceDataset, destDataset, k, dist, action| - this.prSendMsg(\fitTransform,[sourceDataset.asSymbol, destDataset.asSymbol, k, dist],action); + this.prSendMsg(\fitTransform, + [sourceDataset.asSymbol, destDataset.asSymbol, k, dist], action); } - + // not implemented cols {|action|} read{|filename,action|} diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp index d69d282..9ac12f3 100644 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -5,6 +5,8 @@ related:: Classes/FluidMDS, Classes/FluidDataSet DESCRIPTION:: +Multidimensional scaling of a link::Classes/FluidDataSet:: + https://scikit-learn.org/stable/modules/manifold.html#multi-dimensional-scaling-mds diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index 987d76f..60af69e 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -5,6 +5,8 @@ related:: Classes/FluidMDS, Classes/FluidDataSet DESCRIPTION:: +Principal Components Analysis of a link::Classes/FluidDataSet:: + https://scikit-learn.org/stable/modules/decomposition.html#principal-component-analysis-pca CLASSMETHODS:: From 64e3fc49b7e0b16d0082009f53c41563c9d783b5 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 18:22:28 +0100 Subject: [PATCH 204/550] FluidStasndardize example update --- .../Classes/FluidStandardize.schelp | 83 +++++++++---------- 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index a538a07..7e23d0c 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -37,6 +37,8 @@ METHOD:: fitTransform Standardize a link::Classes/FluidDataSet:: into another link::Classes/FluidDataSet:: ARGUMENT:: sourceDataset The link::Classes/FluidDataSet:: to standardize +ARGUMENT:: destDataset +The link::Classes/FluidDataSet:: to populate with standardized data ARGUMENT:: action A function to run when processing is complete @@ -60,64 +62,57 @@ s.boot; ~stand = FluidDataSet(s,\stand_help_standd); ~audio = Buffer.read(s,~audiofile); ~pitch_feature = Buffer.new(s); -~stats = Buffer.new(s); -~datapoint = Buffer.alloc(s,2); +~stats = Buffer.alloc(s, 7, 2); ~standardizer = FluidStandardize(s); ) -// Do a pitch analysis on the audio, which gives us pitch and pitch confidence (so a 2D datum) + +// Load audio and run a pitch analysis, which gives us pitch and pitch confidence (so a 2D datum) +( +~audio = Buffer.read(s,~audiofile); +FluidBufPitch.process(s,~audio, features: ~pitch_feature); +) + // Divide the time series in to 10, and take the mean of each segment and add this as a point to // the 'raw' FluidDataSet ( -~raw.clear; -~stand.clear; -FluidBufPitch.process(s,~audio,features:~pitch_feature,action:{ - "Pitch analysis.complete. Doing stats".postln; - fork{ - var chunkLen = (~pitch_feature.numFrames / 10).asInteger; - 10.do{ |i| - s.sync; FluidBufStats.process(s,~pitch_feature,startFrame:i*chunkLen,numFrames:chunkLen,stats:~stats, action:{ - ~stats.loadToFloatArray(action:{ |statsdata| - [statsdata[0],statsdata[1]].postln; - ~datapoint.setn(0,[statsdata[0],statsdata[1]]); - s.sync; - ("Adding point" ++ i).postln; - ~raw.addPoint(i,~datapoint); - }) - }); - if(i == 9) {"Analysis done, dataset ready".postln} - } - } -}); +{ + var trig = LocalIn.kr(1, 1); + var buf = LocalBuf(2, 1); + var count = PulseCount.kr(trig) - 1; + var chunkLen = (~pitch_feature.numFrames / 10).asInteger; + var stats = FluidBufStats.kr( + source: ~pitch_feature, startFrame: count * chunkLen, + numFrames: chunkLen, stats: ~stats, trig: trig + ); + var rd = BufRd.kr(2, ~stats, DC.kr(0), 0, 1);// pick only mean pitch and confidence + var wr1 = BufWr.kr(rd[0], buf, DC.kr(0)); + var wr2 = BufWr.kr(rd[1], buf, DC.kr(1)); + var dsWr = FluidDataSetWr.kr(\stand_help_raw, buf: buf, trig: Done.kr(stats)); + LocalOut.kr( Done.kr(dsWr)); + FreeSelf.kr(count - 9); +}.play; ) -//Fit the FluidStandardizer to the raw data, and then apply the scaling out of place into -//our second FluidDataSet, so we can compare. -//Download the dataset contents into arrays for plotting + +// Standardize and load to language-side array ( -~standardizer.fit(~raw); -~standardizer.transform(~raw,~stand); ~rawarray = Array.new(10); ~stdarray= Array.new(10); -fork{ - 10.do{|i| - ~raw.getPoint(i,~datapoint,{ - ~datapoint.loadToFloatArray(action:{|a| ~rawarray.add(Array.newFrom(a))}) - }); - s.sync; - ~stand.getPoint(i,~datapoint,{ - - ~datapoint.loadToFloatArray(action:{|a| ~stdarray.add(Array.newFrom(a))}) - }); - s.sync; - if(i==9){"Data downloaded".postln}; - } -} +~standardizer.fitTransform(~raw,~stand, { + ~raw.dump{|x| 10.do{|i| + ~rawarray.add(x["data"][i.asString]) + }}; + ~stand.dump{|x| 10.do{|i| + ~stdarray.add(x["data"][i.asString]) + }}; +}); ) -//Plot side by side. Before standardization the two dimensions have radically different scales -//which can be unhelpful in many cases. Now they are zero-centered, and comparable + ( ~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; ) + + :: From a6c3757e4a70085df3da729a4e05643ee8426f6e Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 18:43:18 +0100 Subject: [PATCH 205/550] PCA example update --- .../HelpSource/Classes/FluidPCA.schelp | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index 60af69e..b6f0a6f 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -61,72 +61,70 @@ Run when done EXAMPLES:: code:: - s.boot; //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidPCA ( ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; ~raw = FluidDataSet(s,\pca_help_12D); +~standardized = FluidDataSet(s,\pca_help_12Ds); ~reduced = FluidDataSet(s,\pca_help_2D); ~audio = Buffer.read(s,~audiofile); ~mfcc_feature = Buffer.new(s); -~stats = Buffer.new(s); -~datapoint = Buffer.alloc(s,12); +~stats = Buffer.alloc(s, 7, 12); +~datapoint = Buffer.alloc(s, 12); ~standardizer = FluidStandardize(s); ~pca = FluidPCA(s); ) -// Do a mfcc analysis on the audio, which gives us 13 points, and we'll throw the 0th away -// Divide the time series in to 100, and take the mean of each segment and add this as a point to + +// Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) +( +~audio = Buffer.read(s,~audiofile); +FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); +) + +// Divide the time series in 100, and take the mean of each segment and add this as a point to // the 'raw' FluidDataSet ( -~raw.clear; -~norm.clear; -FluidBufMFCC.process(s,~audio,features:~mfcc_feature,action:{ - "MFCC analysis.complete. Doing stats".postln; - fork{ - var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; - 100.do{ |i| - s.sync; FluidBufStats.process(s,~mfcc_feature,startFrame:i*chunkLen,numFrames:chunkLen,startChan:1, stats:~stats, action:{ - ~stats.loadToFloatArray(action:{ |statsdata| - [statsdata[0],statsdata[1]].postln; - ~datapoint.setn(0,[statsdata[0],statsdata[1]]); - s.sync; - ("Adding point" ++ i).postln; - ~raw.addPoint(i,~datapoint); - }) - }); - if(i == 99) {"Analysis done, dataset ready".postln} - } - } -}); +{ + var trig = LocalIn.kr(1, 1); + var buf = LocalBuf(12, 1); + var count = PulseCount.kr(trig) - 1; + var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; + var stats = FluidBufStats.kr( + source: ~mfcc_feature, startFrame: count * chunkLen, + startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig + ); + var rd = BufRd.kr(12, ~stats, DC.kr(0), 0, 1); + var bufWr, dsWr; + 12.do{|i| + bufWr = BufWr.kr(rd[i], buf, DC.kr(i)); + }; + dsWr = FluidDataSetWr.kr(\pca_help_12D, buf: buf, trig: Done.kr(stats)); + LocalOut.kr( Done.kr(dsWr)); + FreeSelf.kr(count - 98); +}.play; ) + //First standardize our dataset, so that the MFCC dimensions are on comensurate scales //Then apply the PCA in-place on the standardized data //Download the dataset contents into an array for plotting ( -~standardizer.fit(~raw); -~standardizer.transform(~raw, ~reduced); -~pca.fitTransform(~raw,~reduced,2); -~reducedarray= Array.new(100); -fork{ - 100.do{|i| - ~reduced.getPoint(i,~datapoint,{ - - ~datapoint.loadToFloatArray(action:{|a| ~reducedarray.add(Array.newFrom(a))}) - }); - s.sync; - if(i==99){"Data downloaded".postln}; - } -} +~reducedarray = Array.new(100); +~standardizer.fitTransform(~raw, ~standardized); +~pca.fitTransform(~standardized, ~reduced, 2, action:{ + ~reduced.dump{|x| 100.do{|i| + ~reducedarray.add(x["data"][i.asString]) + }}; +}); + ) //Visualise the 2D projection of our original 12D data ( d = ~reducedarray.flatten(1).unlace.deepCollect(1, { |x| x.normalize}); -// d = [20.collect{1.0.rand}, 20.collect{1.0.rand}]; w = Window("scatter", Rect(128, 64, 200, 200)); w.drawFunc = { Pen.use { @@ -142,5 +140,4 @@ w.drawFunc = { w.refresh; w.front; ) - :: From 54f36f77365772e8515b590222b0992e7cab9161 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 19:01:04 +0100 Subject: [PATCH 206/550] MDS example update --- .../HelpSource/Classes/FluidMDS.schelp | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp index 9ac12f3..a4a8008 100644 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -59,63 +59,62 @@ Run when done EXAMPLES:: code:: + //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers, a FluidStandardize and a FluidMDS ( ~audiofile = File.realpath(FluidBufPitch.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"; ~raw = FluidDataSet(s,\mds_help_12D); +~standardized = FluidDataSet(s,\mds_help_12Ds); ~reduced = FluidDataSet(s,\mds_help_2D); ~audio = Buffer.read(s,~audiofile); ~mfcc_feature = Buffer.new(s); -~stats = Buffer.new(s); -~datapoint = Buffer.alloc(s,12); +~stats = Buffer.alloc(s, 7, 12); ~standardizer = FluidStandardize(s); ~mds = FluidMDS(s); ) -// Do a mfcc analysis on the audio, which gives us 13 points, and we'll throw the 0th away -// Divide the time series in to 100, and take the mean of each segment and add this as a point to + +// Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) +( +~audio = Buffer.read(s,~audiofile); +FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); +) + +// Divide the time series in 100, and take the mean of each segment and add this as a point to // the 'raw' FluidDataSet ( -~raw.clear; -~norm.clear; -FluidBufMFCC.process(s,~audio,features:~mfcc_feature,action:{ - "MFCC analysis.complete. Doing stats".postln; - fork{ - var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; - 100.do{ |i| - s.sync; FluidBufStats.process(s,~mfcc_feature,startFrame:i*chunkLen,numFrames:chunkLen,startChan:1, stats:~stats, action:{ - ~stats.loadToFloatArray(action:{ |statsdata| - [statsdata[0],statsdata[1]].postln; - ~datapoint.setn(0,[statsdata[0],statsdata[1]]); - s.sync; - ("Adding point" ++ i).postln; - ~raw.addPoint(i,~datapoint); - }) - }); - if(i == 99) {"Analysis done, dataset ready".postln} - } - } -}); +{ + var trig = LocalIn.kr(1, 1); + var buf = LocalBuf(12, 1); + var count = PulseCount.kr(trig) - 1; + var chunkLen = (~mfcc_feature.numFrames / 100).asInteger; + var stats = FluidBufStats.kr( + source: ~mfcc_feature, startFrame: count * chunkLen, + startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig + ); + var rd = BufRd.kr(12, ~stats, DC.kr(0), 0, 1); + var bufWr, dsWr; + 12.do{|i| + bufWr = BufWr.kr(rd[i], buf, DC.kr(i)); + }; + dsWr = FluidDataSetWr.kr(\mds_help_12D, buf: buf, trig: Done.kr(stats)); + LocalOut.kr( Done.kr(dsWr)); + FreeSelf.kr(count - 99); +}.play; ) + + //First standardize our dataset, so that the MFCC dimensions are on comensurate scales //Then apply the MDS in-place on the standardized data to get 2 dimensions, using a Euclidean distance metric //Download the dataset contents into an array for plotting ( -~standardizer.fit(~raw); -~standardizer.transform(~raw, ~reduced); -~mds.fitTransform(~raw,~reduced,2, FluidMDS.euclidean); -~reducedarray= Array.new(100); -fork{ - 100.do{|i| - ~reduced.getPoint(i,~datapoint,{ - - ~datapoint.loadToFloatArray(action:{|a| ~reducedarray.add(Array.newFrom(a))}) - }); - s.sync; - if(i==99){"Data downloaded".postln}; - } -} +~reducedarray = Array.new(100); +~standardizer.fitTransform(~raw, ~standardized); +~mds.fitTransform(~standardized, ~reduced, 2, FluidMDS.euclidean, action:{ + ~reduced.dump{|x| 100.do{|i| + ~reducedarray.add(x["data"][i.asString]) + }}}); ) //Visualise the 2D projection of our original 12D data From 716d60912e398c7ccfc0638657c40db86245a560 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 19:23:42 +0100 Subject: [PATCH 207/550] Recover cancelled notification resolves #58 --- include/FluidSCWrapper.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 53a2363..f0bf2e2 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -51,6 +51,15 @@ struct WrapperState std::atomic mInNRT{false}; std::atomic mNodeAlive{true}; Result mResult{}; + + ~WrapperState() + { + if(!mJobDone && !mSynchronous) + { + std::cout << "Processing Cancelled" << std::endl; + } + } + }; /// Named, shared clients already have a lookup table in their adaptor class From fb89c0d9a01a8767e4115acfe80cb340c750e883 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 19:25:59 +0100 Subject: [PATCH 208/550] KNNClassifier example update --- .../Classes/FluidKNNClassifier.schelp | 105 ++++++++---------- 1 file changed, 47 insertions(+), 58 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp index f9aea85..edc5a02 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -52,10 +52,15 @@ Run when done, passes predicted label as argument EXAMPLES:: code:: -//A dataset of example points, and a label set of corresponding labels -//+ -//A dataset of test data and a labelset for predicted labels + + +// Make: +// - A KNN Classifier +// - A dataset of example points, and a label set of corresponding labels +// - A dataset of test data and a labelset for predicted labels + ( +~classifier = FluidKNNClassifier(s); ~source= FluidDataSet(s,\knnclassify_help_examples); ~labels = FluidLabelSet(s,\knnclassify_help_labels); ~test = FluidDataSet(s,\knnclassify_help_test); @@ -66,59 +71,44 @@ code:: ( ~examplepoints = [[0.5,0.5],[-0.5,0.5],[0.5,-0.5],[-0.5,-0.5]]; ~examplelabels = [\red,\orange,\green,\blue]; -~source.clear; -~labels.clear; -~tmpbuf = Buffer.alloc(s,2); -fork{ - s.sync; - ~examplepoints.do{|x,i| - (""++(i+1)++"/4").postln; - ~tmpbuf.setn(0,x); - ~source.addPoint(i,~tmpbuf); - ~labels.addLabel(i,~examplelabels[i]); - s.sync - } -} +d = Dictionary.new; +d.add(\cols -> 2); +d.add(\data -> Dictionary.newFrom(~examplepoints.collect{|x, i|[i.asString, x]}.flatten)); +~source.load(d); +~examplelabels.collect{|x,i| ~labels.addLabel(i, x);}; ) //Make some random, but clustered test points ( -~testpoints = (4.collect{64.collect{(1.sum3rand) + [1,-1].choose}.clump(2)}).flatten(1) * 0.5; -~test.clear; -fork { - s.sync; - ~testpoints.do{|x,i| - ~tmpbuf.setn(0,x); - ~test.addPoint(i,~tmpbuf); - s.sync; - if(i==(~testpoints.size - 1)){"Generated test data".postln;} - } -} +~testpoints = (4.collect{ + 64.collect{(1.sum3rand) + [1,-1].choose}.clump(2) + }).flatten(1) * 0.5; +d = Dictionary.with( + *[\cols -> 2,\data -> Dictionary.newFrom( + ~testpoints.collect{|x, i| [i, x]}.flatten)]); + ~test.load(d); + ) -//Make a new KNN classifier model, fit it to the example dataset and labels, and then run preduction on the test data into our mapping label set + +//Fit the classifier to the example dataset and labels, and then run prediction on the test data into our mapping label set ( -fork{ - ~classifier = FluidKNNClassifier(s); - s.sync; - ~classifier.fit(~source,~labels); - ~classifier.predict(~test, ~mapping, 1); - s.sync; -} +~classifier.fit(~source,~labels); +~classifier.predict(~test, ~mapping, 1); ) //Return labels of clustered points ( ~assignments = Array.new(~testpoints.size); fork{ - ~testpoints.do{|x,i| - ~mapping.getLabel(i,action:{|l| - ~assignments.add(l); - }); - s.sync; - if(i==(~testpoints.size - 1)){"Got assignments".postln;} - }; - ~assignments.postln; + ~testpoints.do{|x,i| + ~mapping.getLabel(i, action:{|l| + ~assignments.add(l); + }); + s.sync; + if(i==(~testpoints.size - 1)){"Got assignments".postln;} + }; + ~assignments.postln; } ) @@ -137,24 +127,23 @@ d = ((~testpoints + 1) * 0.5).flatten(1).unlace; w = Window("scatter", Rect(128, 64, 200, 200)); ~colours = [Color.blue,Color.red,Color.green,Color.magenta]; w.drawFunc = { - Pen.use { - e[0].size.do{|i| - var r = Rect(e[0][i],e[1][i],10,10); - Pen.fillColor = c[~examplelabels[i]]; - Pen.fillOval(r); - }; - d[0].size.do{|i| - var x = (d[0][i]*200); - var y = (d[1][i]*200); - var r = Rect(x,y,5,5); - Pen.fillColor = c[~assignments[i].asSymbol].alpha_(0.3); - Pen.fillOval(r); - } - } + Pen.use { + e[0].size.do{|i| + var r = Rect(e[0][i],e[1][i],10,10); + Pen.fillColor = c[~examplelabels[i]]; + Pen.fillOval(r); + }; + d[0].size.do{|i| + var x = (d[0][i]*200); + var y = (d[1][i]*200); + var r = Rect(x,y,5,5); + Pen.fillColor = c[~assignments[i].asSymbol].alpha_(0.3); + Pen.fillOval(r); + } + } }; w.refresh; w.front; ) - :: From 4b25eeba18d274b3cfcd0194f5b3f1f6dcbe642d Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 12 Jun 2020 19:45:27 +0100 Subject: [PATCH 209/550] KNNRgressor example update --- .../Classes/FluidKNNRegressor.schelp | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp index 6336470..b51f0b4 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -62,50 +62,56 @@ code:: ~test = FluidDataSet(s,\knn_regress_test); ~output = FluidDataSet(s,\knn_regress_out); ~tmpbuf = Buffer.alloc(s,1); +~regressor = FluidKNNRegressor(s); ) //Make source, target and test data ( ~sourcedata = 128.collect{|i|i/128}; ~targetdata = 128.collect{|i| sin(2*pi*i/128) }; -fork{ - 128.do{ |i| - ((i + 1).asString ++ "/128").postln; - ~tmpbuf.setn(0,i/128); - ~source.addPoint(i,~tmpbuf); - s.sync; - ~tmpbuf.setn(0,sin(2*pi*i/128)); - ~target.addPoint(i,~tmpbuf); - s.sync; - ~tmpbuf.setn(0,(i/128)**2); - ~test.addPoint(i,~tmpbuf); - s.sync; - if(i==127){"Source, target and test generated".postln}; - } -} +~testdata = 128.collect{|i|(i/128)**2}; + +~source.load( + Dictionary.with( + *[\cols -> 1,\data -> Dictionary.newFrom( + ~sourcedata.collect{|x, i| [i.asString, [x]]}.flatten)]) +); + +~target.load( +d = Dictionary.with( + *[\cols -> 1,\data -> Dictionary.newFrom( + ~targetdata.collect{|x, i| [i.asString, [x]]}.flatten)]); +); + +~test.load( + Dictionary.with( + *[\cols -> 1,\data -> Dictionary.newFrom( + ~testdata.collect{|x, i| [i.asString, [x]]}.flatten)]) +); + + +~source.print; +~target.print; +~test.print; + ) + + // 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 ( ~outputdata = Array(128); -fork{ - ~regressor = FluidKNNRegressor(s); - s.sync; - ~regressor.fit(~source,~target); - ~regressor.predict(~test,~output,1); - s.sync; - 128.do{|i| - ~output.getPoint(i,~tmpbuf,{ - ~tmpbuf.loadToFloatArray(action:{|x| - ~outputdata.addAll(x) - }) - }); - s.sync; - if(i==127){"Model fitted, output generated".postln}; - } -} +~regressor.fit(~source, ~target); +~regressor.predict(~test, ~output, 1, action:{ + ~output.dump{|x| 128.do{|i| + ~outputdata.add(x["data"][i.asString][0]) + }}; +}); ) + + //We should see a single cycle of a chirp ~outputdata.plot; + :: From 441d1d0cf9ba81650e4289d42025ef97d397b4ef Mon Sep 17 00:00:00 2001 From: Owen Green Date: Fri, 12 Jun 2020 20:05:23 +0100 Subject: [PATCH 210/550] Things that haven't run, can't be cancelled. Hope this fixes #58 --- include/FluidSCWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index f0bf2e2..ab91c68 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -54,7 +54,7 @@ struct WrapperState ~WrapperState() { - if(!mJobDone && !mSynchronous) + if(!mJobDone && !mSynchronous && mHasTriggered) { std::cout << "Processing Cancelled" << std::endl; } From ef88ef48f7ab078349dfba8858ac1bd0e8cd18dc Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Fri, 12 Jun 2020 21:56:04 +0100 Subject: [PATCH 211/550] typo in dataset helpfile (plus faster write :) --- release-packaging/HelpSource/Classes/FluidDataSet.schelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index aab6fc8..a36084f 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -101,13 +101,13 @@ fork{ ~ds.load(d); s.sync; ~ds.dump; s.sync; ~ds.free; } -); +) // Using synth ( ~ds = FluidDataSet.new(s,\simple1d_3); { - var trig = Impulse.kr(10); + var trig = Impulse.kr(20); var count = PulseCount.kr(trig) - 1; var buf = LocalBuf(1); BufWr.kr(count, buf); From a3ee1868f14c5f7a9ab9127b7e097fbf43e854a0 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 23 Jun 2020 17:37:54 +0100 Subject: [PATCH 212/550] Supply missing lambda capture, because MSVC was sad --- include/FluidSCWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index ab91c68..2f6c719 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1294,7 +1294,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto& report = std::cout; report << "ERROR: " << msg->name << " type signature incorrect.\nExpect: ("; size_t i{0}; - ForEach(args, [&i](auto& x){ + ForEach(args, [&i,&expectedArgCount](auto& x){ report << ParamReader::argTypeToString(x); if(i < ( expectedArgCount - 1 ) ) { From eb6aeb0304176de26033549fd3f9b430a8a5e483 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 23 Jun 2020 18:07:41 +0100 Subject: [PATCH 213/550] Also capture report --- include/FluidSCWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 2f6c719..6d16c28 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1294,7 +1294,7 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto& report = std::cout; report << "ERROR: " << msg->name << " type signature incorrect.\nExpect: ("; size_t i{0}; - ForEach(args, [&i,&expectedArgCount](auto& x){ + ForEach(args, [&i,&expectedArgCount,&report](auto& x){ report << ParamReader::argTypeToString(x); if(i < ( expectedArgCount - 1 ) ) { From b687b5649da174f411fc2cebdff076a54b207fce Mon Sep 17 00:00:00 2001 From: Owen Green Date: Thu, 25 Jun 2020 16:38:55 +0100 Subject: [PATCH 214/550] Add KMeansClient RT support --- include/FluidSCWrapper.hpp | 8 ++ release-packaging/Classes/FluidKMeans.sc | 11 +-- .../Classes/FluidManipulationClient.sc | 84 +++++++++++++++++-- .../HelpSource/Classes/FluidKMeans.schelp | 33 +++++++- src/FluidManipulation/FluidManipulation.cpp | 2 +- 5 files changed, 121 insertions(+), 17 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6d16c28..02c053b 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 ("FluidDataSetQuery",ft); makeSCWrapper("FluidLabelSet",ft); makeSCWrapper("FluidKDTree",ft); - makeSCWrapper("FluidKMeans",ft); + makeSCWrapper("FluidKMeans",ft); makeSCWrapper("FluidKNNClassifier",ft); makeSCWrapper("FluidKNNRegressor",ft); makeSCWrapper("FluidNormalize",ft); From b2a295d8b33e62abcaf29011f18204e1cdf91b05 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 1 Jul 2020 11:39:53 +0100 Subject: [PATCH 215/550] Add support for LongArrayT params --- include/FluidSCWrapper.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 6d16c28..af3c649 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -987,6 +987,24 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ft->fRTFree(w,chunk); return res; } + + static auto fromArgs(Unit*, FloatControlsIter& args,typename LongArrayT::type&, int) + { + //first is array size, then items + + using Container = typename LongArrayT::type; + using Value = typename Container::type; + + index size = static_cast(args.next()); + + Container res(size); + + for (index i = 0; i < size; ++i) + res[i] = static_cast(args.next()); + + return res; + } + template static std::enable_if_t::value, T> From 2b71644d7bcfa11e0cd3cd37af4df5782526a809 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Wed, 1 Jul 2020 22:02:42 +0100 Subject: [PATCH 216/550] Refresh parameters before message invocation --- include/FluidSCWrapper.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index af3c649..eb42cee 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -1343,6 +1343,8 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase ForEach(args,[x,&inArgs](auto& arg){ arg = ParamReader::fromArgs(x, inArgs,arg,0); }); + + x->client().setParams(x->params()); ft->fDoAsynchronousCommand( x->mWorld, nullptr, getName(), msg, From 7c271fa525b85f7a1ec77783d29530d2d1720c89 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 13:28:36 +0100 Subject: [PATCH 217/550] Assign server properly in FluidLoadFolder, resolves #59 --- release-packaging/Classes/FluidCorpusBuilders.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index af7d58f..8a16df1 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -10,7 +10,7 @@ FluidLoadFolder { play { |server, action| var sizes,channels,maxChan, startEnd,counter; - server ?? server = Server.default; + server ?? {server = Server.default}; files = SoundFile.collect(path +/+ '*'); sizes = files.collect{|f|f.numFrames()}; channels = files.collect{|f| f.numChannels()}; From 7f6453be1854f406d99a9e7d3f4cba38cbccd3cc Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 13:29:28 +0100 Subject: [PATCH 218/550] Enable .kr for FluidBufCompose, resolves #60 --- release-packaging/Classes/FluidBufCompose.sc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index 47f323d..a9326aa 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -11,10 +11,9 @@ FluidBufCompose : UGen { ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); } -/* *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1| - - ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking:1); - }*/ + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1| + ^this.new1('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, 1); + } *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action| From 36f0fca45348deea9090d549612b3a1cade865c0 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 13:31:04 +0100 Subject: [PATCH 219/550] Make number of parallel tasks settable in FluidSliceCorpus, and manage small sets, resolves #61 --- release-packaging/Classes/FluidCorpusBuilders.sc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index 8a16df1..e629743 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -56,7 +56,7 @@ FluidSliceCorpus { ^super.newCopyArgs(sliceFunc,labelFunc); } - play{ |server,sourceBuffer,bufIdx, action| + play{ |server,sourceBuffer,bufIdx, action, tasks = 4| var counter, tmpIndices,perf,jobs,total,uid, completed, pointstotal; uid = UniqueID.next; sourceBuffer ?? {"No buffer to slice".error; ^nil}; @@ -111,7 +111,8 @@ FluidSliceCorpus { FreeSelfWhenDone.kr(onsets); }.play; }; - 4.do{perf.value(Buffer.new)}; + tasks ?? {tasks = 4}; + tasks.asInteger.min(jobs.size).do{perf.value(Buffer.new)}; } } From f480d476b216ff6c9abf57204208113233a9de59 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 13:49:19 +0100 Subject: [PATCH 220/550] Correct reading of UID from controls for ModelObjects, resolves #62 --- include/FluidSCWrapper.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index eb42cee..6c76610 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -343,8 +343,8 @@ class NonRealTime : public SCUnit using SharedState = std::shared_ptr>; public: - static index ControlOffset(Unit*) { return IsModel_t::value ? 1 : 0; } - static index ControlSize(Unit* unit) { return index(unit->mNumInputs) - unit->mSpecialIndex - 2; } + static index ControlOffset(Unit*) { return 0; } + static index ControlSize(Unit* unit) { return index(unit->mNumInputs) - unit->mSpecialIndex - 2 - (IsModel_t::value ? 1 : 0); } static void setup(InterfaceTable* ft, const char* name) { @@ -683,7 +683,8 @@ struct LifetimePolicy static void constructClass(Unit* unit) { - index uid = static_cast(unit->mInBuf[Wrapper::ControlOffset(unit)][0]); + index uid = static_cast(unit->mInBuf[Wrapper::ControlOffset(unit)+Wrapper::ControlSize(unit)][0]); + FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; auto& entry = mRegistry[uid]; auto& client = entry.client; From 1923ba397745ff3527a767b30e8adc16ae5e6e4e Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 13:50:41 +0100 Subject: [PATCH 221/550] Update order of args for FluidNormalize --- release-packaging/Classes/FluidNormalize.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 07b77a1..c8013e4 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -2,7 +2,7 @@ FluidNormalize : FluidManipulationClient { *new {|server, min = 0, max = 1| var uid = UniqueID.next; - ^super.new(server,uid,min,max)!?{|inst|inst.init(uid);inst} + ^super.new(server,min,max,uid)!?{|inst|inst.init(uid);inst} } init {|uid| From 2d265783a0dacee657880dfe9d1f396fea468e8f Mon Sep 17 00:00:00 2001 From: Pierre Alexandre Tremblay Date: Mon, 6 Jul 2020 14:57:17 +0100 Subject: [PATCH 222/550] working audio example of KR kmeans --- .../HelpSource/Classes/FluidKMeans.schelp | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index f635910..ef970a4 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -94,12 +94,14 @@ fork{ ~kmeans = FluidKMeans(s); // Fit into 4 clusters +( ~kmeans.fitPredict(~dataset,~clusters, 4, action: {|c| "Fitted.\n # Points in each cluster:".postln; c.do{|x,i| ("Cluster" + i + "->" + x.asInteger + "points").postln; } }); +) // Cols of kmeans should match dataset, size is the number of clusters ~kmeans.cols; @@ -120,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; @@ -150,13 +155,13 @@ w.front; ~ob = Bus.control(s); //output bus can be kr ~inpPoint = Buffer.alloc(s,2); ~outPoint = Buffer.alloc(s,1); -//Set properties on FluidKMeans: -~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); ) //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); @@ -170,6 +175,27 @@ w.front; { 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); ) :: From b7f1f6e2130e1156295543542298265b8f5b6e16 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Mon, 6 Jul 2020 18:33:38 +0100 Subject: [PATCH 223/550] Add FluidMLPRegressor --- .../Classes/FluidMLPRegressor.sc | 35 +++++++++++++++++++ src/FluidManipulation/FluidManipulation.cpp | 2 ++ 2 files changed, 37 insertions(+) create mode 100644 release-packaging/Classes/FluidMLPRegressor.sc diff --git a/release-packaging/Classes/FluidMLPRegressor.sc b/release-packaging/Classes/FluidMLPRegressor.sc new file mode 100644 index 0000000..33443aa --- /dev/null +++ b/release-packaging/Classes/FluidMLPRegressor.sc @@ -0,0 +1,35 @@ +FluidMLPRegressor : FluidManipulationClient { + + classvar