diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index cc6c8eb..c5bbae0 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -11,1534 +11,42 @@ under the European Union’s Horizon 2020 research and innovation programme #pragma once #include "SCBufferAdaptor.hpp" -#include -#include -#include -#include -#include -#include -#include +#include "wrapper/DeriveBaseClass.hpp" +#include "wrapper/NonRealtime.hpp" +#include "wrapper/Realtime.hpp" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fluid { -namespace client { - -template -class FluidSCWrapper; - - -template -struct WrapperState -{ - typename Client::ParamSetType params; - Client client; - Node* mNode; - 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{}; - - ~WrapperState() - { - if(!mJobDone && !mSynchronous && mHasTriggered) - { - std::cout << "Processing Cancelled" << std::endl; - } - } -}; - - -template -using SharedState = std::shared_ptr>; - -/// 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 -struct IsModel> -{ - using type = typename ClientWrapper::isModelObject; -}; - - -template -using IsModel_t = typename IsModel::type; - -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 -{ - FloatControlsIter(float** vals, index N) : mValues(vals), mSize(N) {} - - float next() { return mCount >= mSize ? 0 : *mValues[mCount++]; } - - void reset(float** vals) - { - mValues = vals; - mCount = 0; - } - - index size() const noexcept { return mSize; } - index remain() { return mSize - mCount; } - -private: - float** mValues; - index mSize; - index mCount{0}; -}; - -//////////////////////////////////////////////////////////////////////////////// - -// Real Time Processor - -template -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(mWrapper->mControlsIterator), - std::forward(countScan)); - return countScan; - } - -public: - - static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; } - static index ControlSize(Unit* unit) { return static_cast(unit->mNumInputs) - unit->mSpecialIndex - 1 -(IsModel_t::value ? 1 : 0); } - - static void setup(InterfaceTable* ft, const char* name) - { - ft->fDefineUnitCmd(name, "latency", doLatency); - } - - static void doLatency(Unit* unit, sc_msg_iter*) - { - float l[]{ - static_cast(static_cast(unit)->client().latency()) - }; - auto ft = Wrapper::getInterfaceTable(); - - std::stringstream ss; - ss << '/' << Wrapper::getName() << "_latency"; - std::cout << ss.str() << std::endl; - ft->fSendNodeReply(&unit->mParent->mNode, -1, ss.str().c_str(), 1, l); - } - - RealTime() - {} - - void init() - { - - auto& client =static_cast(this)->client(); - assert( - !(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) && - "Client can't have both audio and control outputs"); - - Result r; - if(!(r = expectedSize(mWrapper->mControlsIterator)).ok()) - { - mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; - std::cout - << "ERROR: " << Wrapper::getName() - << " wrong number of arguments." - << r.message() - << std::endl; - return; - } - - mWrapper->mControlsIterator.reset(mInBuf + mSpecialIndex + 1); - - client.sampleRate(fullSampleRate()); - mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); - mOutputConnections.reserve(asUnsigned(client.audioChannelsOut())); - mAudioInputs.reserve(asUnsigned(client.audioChannelsIn())); - mOutputs.reserve(asUnsigned( - std::max(client.audioChannelsOut(), client.controlChannelsOut()))); - - 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 < client.audioChannelsOut(); ++i) - { - mOutputConnections.emplace_back(true); - mOutputs.emplace_back(nullptr, 0, 0); - } - - for (index i = 0; i < client.controlChannelsOut(); ++i) - { mOutputs.emplace_back(nullptr, 0, 0); } - - mCalcFunc = make_calc_function(); - Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); - } - - void next(int) - { - - auto& client = mWrapper->client(); - auto& params = mWrapper->params(); - const Unit* unit = this; - bool trig = IsModel_t::value ? !mPrevTrig && in0(0) > 0 : false; - bool shouldProcess = IsModel_t::value ? trig : true; - mPrevTrig = trig; - - if(shouldProcess) - { - mWrapper->mControlsIterator.reset(mInBuf + mSpecialIndex + - 1); // mClient.audioChannelsIn()); - Wrapper::setParams(mWrapper, - params, mWrapper->mControlsIterator); // forward on inputs N + audio inputs as params - params.constrainParameterValues(); - } - 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 < 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 < client.controlChannelsOut(); ++i) - { - assert(i <= std::numeric_limits::max()); - mOutputs[asUnsigned(i)].reset(out(static_cast(i)), 0, 1); - } - client.process(mAudioInputs, mOutputs, mContext); -// } - } -private: - std::vector mInputConnections; - std::vector mOutputConnections; - std::vector mAudioInputs; - std::vector mOutputs; - FluidContext mContext; - Wrapper* mWrapper{static_cast(this)}; - bool mPrevTrig; -}; - -//////////////////////////////////////////////////////////////////////////////// - -/// Non Real Time Processor -/// This is also a UGen, but the main action is delegated off to a worker -/// thread, via the NRT thread. The RT bit is there to allow us (a) to poll our -/// thread and (b) emit a kr progress update -template -class NonRealTime : public SCUnit -{ - using ParamSetType = typename Client::ParamSetType; -// using SharedState = std::shared_ptr>; -public: - - 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) - { - 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->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 - /// params, so we skip them in the controlsIterator - NonRealTime() - : mSynchronous{mNumInputs > 2 ? (in0(int(mNumInputs) - 1) > 0) : false} - {} - - ~NonRealTime() - { - auto state = mWrapper->client().state(); - if (state == ProcessState::kProcessing) - { - std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; - Wrapper::getInterfaceTable()->fSendNodeReply(&mParent->mNode, 1, "/done", - 0, nullptr); - } - // processing will be cancelled in ~NRTThreadAdaptor() - } - - - /// No option of not using a worker thread for now - /// init() sets up the NRT process via the SC NRT thread, and then sets our - /// UGen calc function going - void init() - { - mWrapper->state()->mSynchronous = mSynchronous; - - mFifoMsg.Set(mWorld, initNRTJob, nullptr, this); - - // 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, - /// launches tidy up when complete - 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; - - - - auto& sharedState = mWrapper->state(); - mWrapper->mDone = sharedState->mJobDone; - if(trigger && !sharedState->mInNRT) - { - 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->mInNRT = true; -// SharedState* statePtr = static_cast*>(mWorld->ft->fRTAlloc(mWorld, sizeof(SharedState))); - SharedState* statePtr = new SharedState(sharedState); -// statePtr = new (statePtr) SharedState(sharedState); - mFifoMsg.Set(mWorld, initNRTJob, [](FifoMsg* m){ - delete static_cast*>(m->mData); - }, statePtr); - mWorld->ft->fSendMsgFromRT(mWorld, mFifoMsg); - return; - } - - if (0 == pollCounter++ && !sharedState->mInNRT && sharedState->mHasTriggered) - { - sharedState->mInNRT = true; - -// SharedState* statePtr = static_cast*>(mWorld->ft->fRTAlloc(mWorld, sizeof(SharedState))); - SharedState* statePtr = new SharedState(sharedState); -// statePtr = new (statePtr) SharedState(sharedState); - mWorld->ft->fDoAsynchronousCommand(mWorld, nullptr, Wrapper::getName(), - statePtr, postProcess, exchangeBuffers, - tidyUp, destroy, 0, nullptr); - } - pollCounter %= checkThreadInterval; - } - - - /// To be called on NRT thread. Validate parameters and commence processing in - /// new thread - static void initNRTJob(FifoMsg* f) - { - if(!f->mData) return; - auto w = static_cast*>(f->mData); - SharedState& s = *w; - Result result = validateParameters(s->params); - - if (!result.ok()) - { - std::cout << "ERROR: " << Wrapper::getName() << ": " - << result.message().c_str() << std::endl; - } - else - { - s->client.setSynchronous(s->mSynchronous); - result = s->client.enqueue(s->params); - if (!result.ok()) - { - std::cout << "ERROR: " << Wrapper::getName() << ": " - << result.message().c_str() << std::endl; - } - else - { - s->mJobDone = false; - s->mCancelled = false; - s->mResult = s->client.process(); - s->mHasTriggered = true; - } - } - s->mInNRT = false; -// using namespace std; -// w->~shared_ptr>(); -// f->mWorld->ft->fRTFree(f->mWorld,w); -// delete w; - } - - /// Check result and report if bad - static bool postProcess(World*, void* data) - { - - if(!data) return false; - - auto& w = *static_cast*>(data); - Result r; - assert(w->mNode != nullptr) ; - w->mInNRT = true; - ProcessState s = w->client.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 - // when mSynchrous = true, this call - // will always be behind process() on - // the command FIFO, so we can assume - // that if the state is kNoProcess, it - // has run (vs never having run) - { - // Given that cancellation from the language now always happens by freeing - // the synth, this block isn't reached normally. HOwever, if someone - // cancels using u_cmd, this is what will fire - if (r.status() == Result::Status::kCancelled) - { - std::cout << Wrapper::getName() << ": Processing cancelled" - << std::endl; - w->mCancelled = true; - w->mHasTriggered = false; - w->mJobDone = true; - return false; - } - - w->client.checkProgress(r); - if(w->mSynchronous) r = w->mResult; - if (!r.ok()) - { - if(!w->mJobDone) Wrapper::printResult(w,r); - if(r.status() == Result::Status::kError) - { - w->mJobDone = true; - w->mHasTriggered = false; - return false; - } - } - w->mJobDone = true; - w->mHasTriggered = false; - return true; - } - return false; - } - - /// swap NRT buffers back to RT-land - 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 - // 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* , void* data) - { - if(!data) return false; - SharedState& s = *(static_cast*>(data)); - s->params.template forEachParamType(); - return true; - } - - static void destroy(World* /*world*/, void* data) - { - if(!data) return; - auto& s = *static_cast*>(data); - s->mInNRT = false; -// using namespace std; -// s.~shared_ptr>(); -// world->ft->fRTFree(world,data); - delete static_cast*>(data); - } - - static void doCancel(Unit* unit, sc_msg_iter*) - { - static_cast(unit)->client().cancel(); - } - -private: - static Result validateParameters(ParamSetType& p) - { - auto results = p.constrainParameterValues(); - for (auto& r : results) - { - if (!r.ok()) return r; - } - return {}; - } - - 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(); - } - }; - - FifoMsg mFifoMsg; - char* mCompletionMessage = nullptr; - void* mReplyAddr = nullptr; - const char* mName = nullptr; - index checkThreadInterval; - index pollCounter{0}; - index mPreviousTrigger{0}; - - bool mSynchronous{true}; - Wrapper* mWrapper{static_cast(this)}; - Result mResult; -}; - -//////////////////////////////////////////////////////////////////////////////// - -/// An impossible monstrosty -template -class NonRealTimeAndRealTime : public RealTime, - public NonRealTime -{ - static void setup(InterfaceTable* ft, const char* name) - { - RealTime::setup(ft, name); - NonRealTime::setup(ft, name); - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -//Discovery for clients that need persistent storage (Dataset and friends) - - - -template -struct LifetimePolicy; - -//template -//struct LifetimePolicy -//{ -//// static_assert(false,"Shared Objecthood and Model Objecthood are not compatible"); -//}; - -/// Default policy -template -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,true,false); - 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)+Wrapper::ControlSize(unit)][0]); - - FloatControlsIter controlsReader{unit->mInBuf + 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,true,false); - 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) - { - 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: - static std::unordered_map mRegistry; -}; - -template -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; - using ParamType = typename Client::ParamSetType; - - static void constructClass(Unit* unit) - { - - FloatControlsIter controlsReader{unit->mInBuf + Wrapper::ControlOffset(unit),Wrapper::ControlSize(unit)}; - - auto params = typename Client::ParamSetType{Client::getParameterDescriptors()}; - Wrapper::setParams(unit, params,controlsReader,true,false); - - auto& name = params.template get<0>(); - - mParamsRegistry.emplace(name, ParamType{params}); - mClientRegistry.emplace(name, Client{params}); - - auto 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* ft, const char* name) - { - - 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()); - mClientRegistry.erase(objectName); - mParamsRegistry.erase(objectName); - }, &mClientRegistry); - - } -private: - static ClientPointer getClientPointer(Wrapper* wrapper) - { - auto& params = wrapper->params(); - auto name = params.template get<0>(); - return SharedType::lookup(name); - } - - static std::unordered_map mClientRegistry; - static std::unordered_map mParamsRegistry; -}; - -template -std::unordered_map - LifetimePolicy::mClientRegistry{}; - -template -std::unordered_map::ParamType> - LifetimePolicy::mParamsRegistry{}; -//// Template Specialisations for NRT/RT - -template -class FluidSCWrapperImpl; - -template -class FluidSCWrapperImpl - : public NonRealTime, - public LifetimePolicy, IsPersistent_t> -{ -public: - void init(){ - NonRealTime::init(); - } - static void setup(InterfaceTable* ft, const char* name) - { - NonRealTime::setup(ft,name); - LifetimePolicy, IsPersistent_t>::setup(ft,name); - } -}; - -template -class FluidSCWrapperImpl - : public RealTime, - public LifetimePolicy, IsPersistent_t> -{ -public: - void init(){ - RealTime::init(); - } - static void setup(InterfaceTable* ft, const char* name) - { - RealTime::setup(ft,name); - LifetimePolicy, IsPersistent_t>::setup(ft,name); - } -}; - -//////////////////////////////////////////////////////////////////////////////// - -// Make base class(es), full of CRTP mixin goodness -template -using FluidSCWrapperBase = FluidSCWrapperImpl, - typename Client::isNonRealTime, - typename Client::isRealTime>; - -} // namespace impl - -//////////////////////////////////////////////////////////////////////////////// +namespace fluid { +namespace client { /// The main wrapper template 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; - template - 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(Unit*, sc_msg_iter* args, std::string, int) - { - const char* recv = args->gets(""); - - return std::string(recv ? recv : ""); - } - - static auto fromArgs(Unit* /*x*/, FloatControlsIter& args, std::string, int) - { - // first is string size, then chars - index size = static_cast(args.next()); - -// auto ft = FluidSCWrapper::getInterfaceTable(); -// auto w = x->mWorld; -// char* chunk = -// static_cast(ft->fRTAlloc(w, asUnsigned(size + 1))); - std::string res; - res.resize(asUnsigned(size)); - -// if (!chunk) -// { -// std::cout << "ERROR: " << FluidSCWrapper::getName() -// << ": RT memory allocation failed\n"; -// return std::string{""}; -// } - - for (index i = 0; i < size; ++i) - res[asUnsigned(i)] = static_cast(args.next()); - -// res[size] = 0; // terminate string -// auto res = std::string{chunk}; -// 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> - fromArgs(Unit*, FloatControlsIter& args, T, int) - { - return static_cast(args.next()); - } - - template - static std::enable_if_t::value, T> - fromArgs(Unit*, FloatControlsIter& args, T, int) - { - return args.next(); - } - - template - static std::enable_if_t::value, T> - fromArgs(Unit*, sc_msg_iter* args, T, int defVal) - { - return args->geti(defVal); - } - - template - static std::enable_if_t::value, T> - fromArgs(Unit*, sc_msg_iter* args, T, int) - { - return args->getf(); - } - - static SCBufferAdaptor* fetchBuffer(Unit* x, index bufnum) - { - if(bufnum >= x->mWorld->mNumSndBufs) - { - index localBufNum = bufnum - x->mWorld->mNumSndBufs; - - Graph* parent = x->mParent; - - return localBufNum <= parent->localMaxBufNum ? - new SCBufferAdaptor(parent->mLocalSndBufs + localBufNum,x->mWorld,true) - : nullptr; - } - else - return bufnum >= 0 ? new SCBufferAdaptor(bufnum, x->mWorld) : nullptr; - } - - static auto fromArgs(Unit* x, ArgType args, BufferT::type&, int) - { - typename LongT::type bufnum = static_cast( - ParamReader::fromArgs(x, args, typename LongT::type(), -1)); - return BufferT::type(fetchBuffer(x, bufnum)); - } - - static auto fromArgs(Unit* x, ArgType args, InputBufferT::type&, int) - { - typename LongT::type bufnum = - static_cast(fromArgs(x, args, LongT::type(), -1)); - return InputBufferT::type(fetchBuffer(x, bufnum)); - } - - template - static std::enable_if_t::value, P> - fromArgs(Unit* x, ArgType args, P&, int) - { - return {fromArgs(x, 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()(Unit* x, 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; - - for (index i = 0; i < argSize; i++) - a[i] = static_cast( - ParamReader::fromArgs(x, 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 - - struct ToFloatArray - { - 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); - return count; - } - - template - static index allocSize(FluidTensor s) - { - return s.size(); - } - - template - static std::tuple, index> - allocSize(std::tuple&& t) - { - return allocSizeImpl(std::forward(t), - std::index_sequence_for()); - }; - - 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 - } - 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); - } - - 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)...}; - } - }; - - - // 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; - SharedState state; - ArgTuple args; - Ret result; - std::string name; - }; - - // Sets up a single /u_cmd - template - struct SetupMessage - { - void operator()(const T& message) - { - auto ft = getInterfaceTable(); - ft->fDefineUnitCmd(getName(), message.name, launchMessage); - } - }; - - template - static void launchMessage(Unit* u, sc_msg_iter* args) - { - FluidSCWrapper* x = static_cast(u); - 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()); - } - - template - static void launchMessageImpl(FluidSCWrapper* x, sc_msg_iter* inArgs, - std::index_sequence) - { - x->mControlsIterator.reset(x->mInBuf + FluidSCWrapper::ControlOffset(x)); - setParams(x, x->params(),x->mControlsIterator, true); - - 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; - MessageData* msg = new MessageData(); - msg->name = '/' + Client::getMessageDescriptors().template name(); - msg->state = x->state(); - ArgTuple& args = msg->args; - // type check OSC message - - 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; - - 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& arg){ - if(tagsIter == tagsEnd) - { - typesMatch = false; - return; - } - char t = *(tagsIter++); - typesMatch = typesMatch && ParamReader::argTypeOK(arg,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){ - std::cout << ParamReader::argTypeToString(x); - if(i < (std::tuple_size::value - 1 ) ) - { - std::cout << " ,"; - } - i++; - }); - report << ")\nReceived: ("; - i = 0; - for(auto t: tags) - { - report << ParamReader::oscTagToString(t); - if( i < ( tags.size() - 1 ) ) - { - report << ", "; - } - i++; - } - report << ")\n"; - } - - if(!willContinue) - { -// msg->~MessageData(); -// ft->fRTFree(x->mWorld, msgptr); - delete msg; - return; - } - 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, - [](World*, void* data) // NRT thread: invocation - { - MessageData* m = static_cast(data); - m->result = - ReturnType{invokeImpl(m->state, m->args, IndexList{})}; - - if (!m->result.ok()) - printResult(m->state, m->result); - - return true; - }, - [](World* world, void* data) // RT thread: response - { - MessageData* m = static_cast(data); - MessageDescriptor::template forEachArg(m->args, - world); - - if(m->result.status() != Result::Status::kError) - messageOutput(m->state, m->name, m->result); - else - { - auto ft = getInterfaceTable(); - if(m->state->mNodeAlive) - ft->fSendNodeReply(m->state->mNode, - -1, m->name.c_str(),0, nullptr); - } - return true; - }, - nullptr, // NRT Thread: No-op - [](World* /*w*/, void* data) // RT thread: clean up - { - MessageData* m = static_cast(data); -// m->~MessageData(); -// getInterfaceTable()->fRTFree(w, data); - delete m; - }, - 0, nullptr); - } - - template // Call from NRT - static decltype(auto) invokeImpl(SharedState& x, ArgsTuple& args, - std::index_sequence) - { - return x->client.template invoke(x->client, std::get(args)...); - } - - template // call from RT - static void messageOutput(SharedState& x, const std::string& s, - MessageResult& result) - { - auto ft = getInterfaceTable(); - if(!x->mNodeAlive) return; - - // allocate return values - index 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))); - - float* values = new float[asUnsigned(numArgs)]; - - // copy return data - ToFloatArray::convert(values, static_cast(result)); - - ft->fSendNodeReply(x->mNode, -1, s.c_str(), - static_cast(numArgs), values); - -// ft->fRTFree(x->mNode->mWorld,values); - delete[] values; - } - - static void messageOutput(SharedState& x, const std::string& s, - MessageResult&) - { - auto ft = getInterfaceTable(); - if(!x->mNodeAlive) return; - ft->fSendNodeReply(x->mNode, -1, s.c_str(), 0, nullptr); - } - - template - 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)); - - 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))); - - float* values = new float[asUnsigned(numArgs)]; - ToFloatArray::convert(values, std::tuple(result), offsets, - std::index_sequence_for()); - - ft->fSendNodeReply(x->mNode, -1, s.c_str(), - static_cast(numArgs), values); - -// ft->fRTFree(x->mNode->mWorld,values); - delete[] values; - } - - static void doVersion(Unit*, sc_msg_iter*) { std::cout << "Fluid Corpus Manipualtion Toolkit version " << fluidVersion() << std::endl; } - bool init{false}; + bool mInit{false}; public: - using Client = C; - using ParamSetType = typename C::ParamSetType; - - FluidSCWrapper(FloatControlsIter&& i, Client&& c, ParamSetType&& p): - mControlsIterator{std::move(i)}, - mState{new WrapperState{std::move(p),std::move(c),&SCUnit::mParent->mNode}} - { - client().setParams(params()); //<-IMPORTANT: client's ref to params is by address, and this has just changed - impl::FluidSCWrapperBase::init(); - init = true; - } - - ~FluidSCWrapper() - { - mState->mNodeAlive = false; - } + template + using ArgumentSetter = typename ClientParams::template Setter; - SharedState& state() { return mState; } + template + using ControlSetter = typename ClientParams::template Setter; + using Client = C; + using ParamSetType = typename C::ParamSetType; static const char* getName(const char* setName = nullptr) { @@ -1556,10 +64,16 @@ public: { getName(name); getInterfaceTable(ft); - registerUnit(ft, name); impl::FluidSCWrapperBase::setup(ft, name); - Client::getMessageDescriptors().template iterate(); ft->fDefineUnitCmd(name, "version", doVersion); + + std::string commandName("/"); + commandName += getName(); + commandName += "/version"; + ft->fDefinePlugInCmd(commandName.c_str(), + [](World*, void*, sc_msg_iter*, void*){ doVersion(nullptr,nullptr); }, + nullptr); + } static auto& setParams(Unit* x, ParamSetType& p, @@ -1577,26 +91,27 @@ public: { for(auto& r:*reportage) { - if(!r.ok()) printResult(&x->mParent->mNode, r); + if(!r.ok()) printResult(x->mParent->mNode.mWorld, r); } } if(!initialized) delete reportage; return p; } - static void printResult(SharedState& x, Result& r) - { - if (!x.get() || !x->mNodeAlive) return; - FluidSCWrapper::printResult(x->mNode, r); - } +// static void printResult(SharedState& x, Result& r) +// { +// if (!x.get() || !x->mNodeAlive) return; +// FluidSCWrapper::printResult(x->mNode->mWorld, r); +// } - static void printResult(Node* x, Result& r) + static void printResult(World* w,Result& r) { + switch (r.status()) { case Result::Status::kWarning: { - if (x->mWorld->mVerbosity > 0) + if (!w || w->mVerbosity > 0) std::cout << "WARNING: " << r.message().c_str() << '\n'; break; } @@ -1615,20 +130,9 @@ public: } } } - - auto& client() { return mState->client; } - auto& params() { return mState->params; } 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); - } std::array mReportage; - FloatControlsIter mControlsIterator; - std::shared_ptr> mState; }; template diff --git a/include/SCBufferAdaptor.hpp b/include/SCBufferAdaptor.hpp index c1aebdf..5c212b5 100644 --- a/include/SCBufferAdaptor.hpp +++ b/include/SCBufferAdaptor.hpp @@ -25,29 +25,7 @@ under the European Union’s Horizon 2020 research and innovation programme namespace fluid { namespace client { -/** - A descendent of SndBuf that will populate itself - from the NRT mirror buffers given a world and a bufnum - **/ -struct NRTBuf -{ - NRTBuf(SndBuf* b) : mBuffer(b) {} - NRTBuf(World* world, index bufnum, bool rt = false) - : NRTBuf(rt ? World_GetBuf(world, static_cast(bufnum)) - : World_GetNRTBuf(world, static_cast(bufnum))) - { - if (mBuffer && !static_cast(mBuffer->samplerate)) - mBuffer->samplerate = world->mFullRate.mSampleRate; - } - -protected: - SndBuf* mBuffer; -}; - -/** - A combination of SndBuf and client::BufferAdaptor (which, in turn, exposes - FluidTensorView), for simple transfer of data - +/* Given a World* and a buffer number, this will populate its SndBuf stuff from the NRT mirror buffers, and create a FluidTensorView wrapper of appropriate dimensions. @@ -60,24 +38,30 @@ protected: nSamps = rows nChans = columns **/ -class SCBufferAdaptor : public NRTBuf, public client::BufferAdaptor +class SCBufferAdaptor; +std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b); + +class SCBufferAdaptor :public client::BufferAdaptor { public: - // SCBufferAdaptor() = delete; + SCBufferAdaptor(const SCBufferAdaptor&) = delete; SCBufferAdaptor& operator=(const SCBufferAdaptor&) = delete; SCBufferAdaptor(SCBufferAdaptor&&) = default; SCBufferAdaptor(SndBuf* buf, World* world, bool local) - : NRTBuf{buf}, mWorld{world}, mLocal{local} + : mBuffer{buf}, mWorld{world}, mLocal{local} {} - SCBufferAdaptor(index bufnum, World* world, bool rt = false) - : NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world) - {} + SCBufferAdaptor(index bufnum, World* world) + : mBuffer{World_GetNRTBuf(world, static_cast(bufnum))}, mBufnum(bufnum), mWorld(world) + { + if (mBuffer && !static_cast(mBuffer->samplerate)) + mBuffer->samplerate = world->mFullRate.mSampleRate; + } - ~SCBufferAdaptor() { cleanUp(); } +// ~SCBufferAdaptor() { cleanUp(); } void assignToRT(World* rtWorld) { @@ -90,11 +74,8 @@ public: void cleanUp() { - if (mOldData) - { - boost::alignment::aligned_free(mOldData); - mOldData = nullptr; - } + boost::alignment::aligned_free(mOldData); + mOldData = nullptr; } // No locks in (vanilla) SC, so no-ops for these @@ -178,24 +159,24 @@ public: std::string asString() const override { return std::to_string(bufnum()); } 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) + if(frames > mBuffer->frames || channels > mBuffer->channels) { return {Result::Status::kError, "Local buffer must be presized adequetly, need ", - frames, "frames, ", channels, " channels." }; + frames, " frames, ", channels, " channels." }; } else return {}; } - mOldData = thisThing->data; + mOldData = mBuffer->data; int allocResult = mWorld->ft->fBufAlloc(mBuffer, static_cast(channels), static_cast(frames), sampleRate); - + + if(mBuffer->data == mOldData) mOldData = nullptr; + Result r; if (allocResult != kSCErr_None) @@ -210,8 +191,9 @@ public: void realTime(bool rt) { mRealTime = rt; } protected: + SndBuf* mBuffer; bool mRealTime{false}; - float* mOldData{0}; + float* mOldData{nullptr}; index mBufnum; World* mWorld; bool mLocal{false}; diff --git a/include/clients/rt/FluidDataSetWr.hpp b/include/clients/rt/FluidDataSetWr.hpp index 1fbb006..ebdc0a4 100644 --- a/include/clients/rt/FluidDataSetWr.hpp +++ b/include/clients/rt/FluidDataSetWr.hpp @@ -24,7 +24,7 @@ class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut { public: FLUID_DECLARE_PARAMS( - DataSetClientRef::makeParam("dataSet", "DataSet Name"), + DataSetClientRef::makeParam("dataSet", "DataSet ID"), StringParam("labelPrefix","Label Prefix"), LongParam("labelOffset", "Label Counter Offset", 0), BufferParam("buf", "Data Buffer") diff --git a/include/wrapper/ArgsFromClient.hpp b/include/wrapper/ArgsFromClient.hpp new file mode 100644 index 0000000..88ae458 --- /dev/null +++ b/include/wrapper/ArgsFromClient.hpp @@ -0,0 +1,337 @@ +#pragma once + +#include "Meta.hpp" + +namespace fluid { +namespace client { + +namespace impl { + // Iterate over kr/ir inputs via callbacks from params object + struct FloatControlsIter + { + FloatControlsIter(float** vals, index N) : mValues(vals), mSize(N) {} + + float next() { return mCount >= mSize ? 0 : *mValues[mCount++]; } + + void reset(float** vals) + { + mValues = vals; + mCount = 0; + } + + index size() const noexcept { return mSize; } + index remain() { return mSize - mCount; } + + private: + float** mValues; + index mSize; + index mCount{0}; + }; +} //impl + +//Specializations of param reader for RT and NRT cases (data encoded differently, buffer semantics differ cause of local bufs) +template struct ParamReader; + +// RT case: we're decoding data from float**, there will be a Unit, we can have LocalBufs +// TODO: All the allocations should be using SC RT allocator, but this won't work reliably until it propagates down through the param set +template<> +struct ParamReader +{ + + using Controls = impl::FloatControlsIter; + + static auto fromArgs(Unit* /*x*/, Controls& args, std::string, int) + { + // first is string size, then chars + index size = static_cast(args.next()); + std::string res; + res.resize(asUnsigned(size)); + for (index i = 0; i < size; ++i) + res[asUnsigned(i)] = static_cast(args.next()); + return res; + } + + static auto fromArgs(Unit*, Controls& 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> + fromArgs(Unit*, Controls& args, T, int) + { + return static_cast(args.next()); + } + + template + static std::enable_if_t::value, T> + fromArgs(Unit*, Controls& args, T, int) + { + return args.next(); + } + + static SCBufferAdaptor* fetchBuffer(Unit* x, index bufnum) + { + if(bufnum >= x->mWorld->mNumSndBufs) + { + index localBufNum = bufnum - x->mWorld->mNumSndBufs; + + Graph* parent = x->mParent; + + return localBufNum <= parent->localMaxBufNum ? + new SCBufferAdaptor(parent->mLocalSndBufs + localBufNum,x->mWorld,true) + : nullptr; + } + else + return bufnum >= 0 ? new SCBufferAdaptor(bufnum, x->mWorld) : nullptr; + } + + static auto fromArgs(Unit* x, Controls& args, BufferT::type&, int) + { + typename LongT::type bufnum = static_cast( + ParamReader::fromArgs(x, args, typename LongT::type(), -1)); + return BufferT::type(fetchBuffer(x, bufnum)); + } + + static auto fromArgs(Unit* x, Controls& args, InputBufferT::type&, int) + { + typename LongT::type bufnum = + static_cast(fromArgs(x, args, LongT::type(), -1)); + return InputBufferT::type(fetchBuffer(x, bufnum)); + } + + template + static std::enable_if_t::value, P> + fromArgs(Unit* x, Controls& args, P&, int) + { + auto id = fromArgs(x, args, index{}, 0); + return {id >= 0 ? std::to_string(id).c_str() : "" }; + } +}; + +// NRT case: we're decoding data from sc_msg_iter*, there will be a World*, we can't have LocalBufs +// TODO: All the allocations should be using SC RT allocator (I guess: this will probably always run on the RT thread), but this won't work reliably until it propagates down through the param set +template<> +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 == 'i'; + } + + static auto fromArgs(World*, sc_msg_iter& args, std::string, int) + { + const char* recv = args.gets(""); + + return std::string(recv ? recv : ""); + } + + 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 SCBufferAdaptor* fetchBuffer(World* x, index bufnum) + { + if(bufnum >= x->mNumSndBufs) + { + std::cout << "ERROR: bufnum " << bufnum << " is invalid for global buffers\n"; + return nullptr; + } + else + return bufnum >= 0 ? new SCBufferAdaptor(bufnum, x) : nullptr; + } + + static auto fromArgs(World* x, sc_msg_iter& args, BufferT::type&, int) + { + typename LongT::type bufnum = static_cast( + ParamReader::fromArgs(x, args, typename LongT::type(), -1)); + return BufferT::type(fetchBuffer(x, bufnum)); + } + + static auto fromArgs(World* x, sc_msg_iter& args, InputBufferT::type&, int) + { + typename LongT::type bufnum = + static_cast(fromArgs(x, args, LongT::type(), -1)); + return InputBufferT::type(fetchBuffer(x, bufnum)); + } + + template + static std::enable_if_t::value, P> + fromArgs(World* x, sc_msg_iter& args, P&, int) + { + auto id = fromArgs(x, args, index{}, 0); + return {id >= 0 ? std::to_string(id).c_str() : ""}; + } + + static auto fromArgs(World*, sc_msg_iter& 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.geti()); + Container res(size); + for (index i = 0; i < size; ++i) + res[i] = static_cast(args.geti()); + return res; + } +}; + + +template +struct ClientParams{ +// Iterate over arguments via callbacks from params object + template + struct Setter + { + static constexpr index argSize = + Wrapper::Client::getParameterDescriptors().template get().fixedSize; + + + /// Grizzly enable_if hackage coming up. Need to brute force an int from incoming data into a string param for FluidDataSet / FluidLabelSet. + /// This will go away one day + + template + std::enable_if_t || Number!=0, typename T::type> + operator()(Context* x, ArgType& args) + { + // Just return default if there's nothing left to grab + if (args.remain() == 0) + { + std::cout << "WARNING: " << Wrapper::getName() + << " received fewer parameters than expected\n"; + return Wrapper::Client::getParameterDescriptors().template makeValue(); + } + + ParamLiteralConvertor a; + using LiteralType = + typename ParamLiteralConvertor::LiteralType; + + for (index i = 0; i < argSize; i++) + a[i] = static_cast( + ParamReader::fromArgs(x, args, a[0], 0)); + + return a.value(); + } + + template + std::enable_if_t && Number==0, typename T::type> + operator()(Context* x, ArgType& args) + { + // Just return default if there's nothing left to grab + if (args.remain() == 0) + { + std::cout << "WARNING: " << Wrapper::getName() + << " received fewer parameters than expected\n"; + return Wrapper::Client::getParameterDescriptors().template makeValue(); + } + + index id = ParamReader::fromArgs(x,args,index{},0); + return std::to_string(id); + } + }; + + template + struct Getter + { + static constexpr index argSize = + Wrapper::Client::getParameterDescriptors().template get().fixedSize; + + }; + + +}; + +} +} diff --git a/include/wrapper/ArgsToClient.hpp b/include/wrapper/ArgsToClient.hpp new file mode 100644 index 0000000..bed811d --- /dev/null +++ b/include/wrapper/ArgsToClient.hpp @@ -0,0 +1,201 @@ +#pragma once + +namespace fluid { +namespace client { + + struct ToFloatArray + { + 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); + return count; + } + + template + static index allocSize(FluidTensor s) + { + return s.size(); + } + + template + static std::tuple, index> + allocSize(std::tuple&& t) + { + return allocSizeImpl(std::forward(t), + std::index_sequence_for()); + }; + + 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 + } + 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); + } + + 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)...}; + } + }; + + template + struct ToOSCTypes + { + + static index numTags(typename BufferT::type) { return 1; } + + template + static std::enable_if_t< + std::is_integral::value || std::is_floating_point::value, index> + numTags(T) + { + return 1; + } + + static index numTags(std::string) + { + return 1;; + } + + template + static index numTags(FluidTensor s) + { + return s.size(); + } + + template + static index numTags(std::tuple&&) + { + return std::tuple_size>::value; + }; + + + static void getTag(Packet& p, typename BufferT::type) { p.addtag('i'); } + + template + static std::enable_if_t>::value> + getTag(Packet& p, T&&) { p.addtag('i'); } + + template + static std::enable_if_t>::value> + getTag(Packet& p, T&&) { p.addtag('f'); } + + static void getTag (Packet& p, std::string) { p.addtag('s'); } + + template + static void getTag(Packet& p, FluidTensor x) + { + T dummy{}; + for (int i = 0; i < x.rows(); i++) + getTag(p, dummy); + } + + template + static void getTag(Packet& p, std::tuple&& t) + { + ForEach(t,[&p](auto& x){getTag(p,x);}); + } + + + static void convert(Packet& p, typename BufferT::type buf) + { + p.addi(static_cast(buf.get())->bufnum()); + } + + template + static std::enable_if_t::value> + convert(Packet& p, T x) + { + p.addi(x); + } + + template + static std::enable_if_t::value> + convert(Packet& p, T x) + { + p.addf(x); + } + + static void convert(Packet& p, std::string s) + { + p.adds(s.c_str()); + } + + template + static void convert(Packet& p, FluidTensor s) + { + for(auto& x: s) convert(p,x); + } + + template + static void convert(Packet& p, std::tuple&& t) + { + ForEach(t,[&p](auto& x){ convert(p,x);}); + } + }; + + +} +} diff --git a/include/wrapper/BufferFuncs.hpp b/include/wrapper/BufferFuncs.hpp new file mode 100644 index 0000000..303e96f --- /dev/null +++ b/include/wrapper/BufferFuncs.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "SCBufferAdaptor.hpp" + +namespace fluid { +namespace client { +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(); + } + }; + +} +} +} diff --git a/include/wrapper/CopyReplyAddress.hpp b/include/wrapper/CopyReplyAddress.hpp new file mode 100644 index 0000000..6506779 --- /dev/null +++ b/include/wrapper/CopyReplyAddress.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace fluid{ +namespace client{ + +void* copyReplyAddress(InterfaceTable* ft, World* inWorld, void* inreply) +{ + + if(! inreply) return nullptr; + + ReplyAddress* reply = (ReplyAddress*)ft->fRTAlloc(inWorld, sizeof(ReplyAddress)); + + *reply = *(static_cast(inreply)); + + return reply; +} + +void deleteReplyAddress(InterfaceTable* ft, World* inWorld, void* inreply) +{ + if(! inreply) return; + ft->fRTFree(inWorld,(ReplyAddress*)inreply); +} + +void* copyReplyAddress(void* inreply) +{ + + if(! inreply) return nullptr; + + ReplyAddress* reply = new ReplyAddress(); + + *reply = *(static_cast(inreply)); + + return reply; +} + +void deleteReplyAddress(void* inreply) +{ + if(! inreply) return; + delete (ReplyAddress*)inreply; +} + + + +} +} diff --git a/include/wrapper/DeriveBaseClass.hpp b/include/wrapper/DeriveBaseClass.hpp new file mode 100644 index 0000000..9f59348 --- /dev/null +++ b/include/wrapper/DeriveBaseClass.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "NonRealtime.hpp" +#include "Realtime.hpp" + +namespace fluid { +namespace client { + +template class FluidSCWrapper; + +namespace impl { + +template +struct BaseChooser +{ + templatestruct Choose + { + using type = NonRealTime; + }; + + template<> + struct Choose + { + using type = RealTime; + }; + + using RT = typename Client::isRealTime; + + static constexpr bool UseRealTime = RT::value && !IsModel_t::value; + + using type = typename Choose::type; +}; + +template +using BaseChooser_t = typename BaseChooser::type; + + +template +using FluidSCWrapperBase = BaseChooser_t>; +} +} +} diff --git a/include/wrapper/Messaging.hpp b/include/wrapper/Messaging.hpp new file mode 100644 index 0000000..40cb50f --- /dev/null +++ b/include/wrapper/Messaging.hpp @@ -0,0 +1,268 @@ +#pragma once + +#include "ArgsFromClient.hpp" +#include "ArgsToClient.hpp" +#include + +namespace fluid { +namespace client { + +template +struct FluidSCMessaging{ + + static auto getInterfaceTable(){ return FluidSCWrapper::getInterfaceTable(); } + static auto getName(){ return FluidSCWrapper::getName(); } + + + template + struct MessageDispatchCmd + { + using Descriptor = typename Client::MessageSetType::template MessageDescriptorAt; + using ArgTuple = typename Descriptor::ArgumentTypes; + using ReturnType = typename Descriptor::ReturnType; + using IndexList = typename Descriptor::IndexList; + + static constexpr size_t Message = N; + index id; + ArgTuple args; + ReturnType result; + std::string name; + IndexList argIndices; + void* replyAddr{nullptr}; + }; + + + template + struct SetupMessageCmd + { + + void operator()(const T& message) + { + static std::string messageName = std::string{getName()} + '/' + message.name; + auto ft = getInterfaceTable(); + ft->fDefinePlugInCmd(messageName.c_str(), doMessage,(void*)messageName.c_str()); + } + }; + + + template + static bool validateMessageArgs(Message* msg, sc_msg_iter* inArgs) + { + using ArgTuple = decltype(msg->args); + + 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; + + auto& args = msg->args; + + constexpr size_t expectedArgCount = std::tuple_size::value; + + /// TODO this squawks if we have a completion message, so maybe we can check if extra arg is a 'b' and squawk if not? +// 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& arg){ + if(tagsIter == tagsEnd) + { + typesMatch = false; + return; + } + char t = *(tagsIter++); + typesMatch = typesMatch && ParamReader::argTypeOK(arg,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){ + std::cout << ParamReader::argTypeToString(x); + if(i < (std::tuple_size::value - 1 ) ) + { + std::cout << " ,"; + } + i++; + }); + report << ")\nReceived: ("; + i = 0; + for(auto t: tags) + { + report << ParamReader::oscTagToString(t); + if( i < ( tags.size() - 1 ) ) + { + report << ", "; + } + i++; + } + report << ")\n"; + } + + return willContinue; + } + + + template + static void doMessage(World* inWorld, void* inUserData, struct sc_msg_iter* args, void* replyAddr) + { + using MessageData = MessageDispatchCmd; + + auto msg = new MessageData(); + + msg->id = args->geti(); + msg->replyAddr = copyReplyAddress(replyAddr); + ///TODO make this step contingent on verbosity or something, in the name of effieciency + bool willContinue = validateMessageArgs(msg, args); + + if(!willContinue) + { + delete msg; + return; + } + + + msg->name = std::string{'/'} + (const char*)(inUserData); + + ForEach(msg-> args,[inWorld,&args](auto& thisarg) + { + thisarg = ParamReader::fromArgs(inWorld, *args,thisarg,0); + }); + + size_t completionMsgSize{args ? args->getbsize() : 0}; + assert(completionMsgSize <= std::numeric_limits::max()); + char* completionMsgData = nullptr; + + if (completionMsgSize) { + completionMsgData = (char*)getInterfaceTable()->fRTAlloc(inWorld, completionMsgSize); + args->getb(completionMsgData, completionMsgSize); + } + + + getInterfaceTable()->fDoAsynchronousCommand(inWorld, replyAddr, getName(), msg, + [](World* world, void* data) // NRT thread: invocation + { + MessageData* m = static_cast(data); + using ReturnType = typename MessageData::ReturnType; + + if(auto ptr = FluidSCWrapper::get(m->id).lock()) + { + m->result = + ReturnType{invokeImpl(ptr->mClient, m->args,m->argIndices)}; + + if (!m->result.ok()) + FluidSCWrapper::printResult(world, m->result); + } else FluidSCWrapper::printNotFound(m->id); + + return true; + }, + [](World* world, void* data) // RT thread: buffer swap (and possible completion messages) + { + MessageData* m = static_cast(data); + MessageData::Descriptor::template forEachArg(m->args, + world); + return true; + }, + [](World*, void* data) // NRT Thread: Send reply + { + MessageData* m = static_cast(data); + if(m->result.status() != Result::Status::kError) + messageOutput(m->name, m->id, m->result, m->replyAddr); + return false; + }, + [](World*, void* data) // RT thread: clean up + { + MessageData* m = static_cast(data); + delete m; + }, + static_cast(completionMsgSize), completionMsgData); + } + + template // Call from NRT + static decltype(auto) invokeImpl(Client& x, ArgsTuple& args, + std::index_sequence) + { + return x.template invoke(x, std::get(args)...); + } + + template // call from RT + static void messageOutput(const std::string& s, index id, MessageResult& result, void* replyAddr) + { + index numTags = ToOSCTypes::numTags(static_cast(result)); + if(numTags > 2048) + { + std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl; + return; + } + + small_scpacket packet; + packet.adds(s.c_str()); + packet.maketags(static_cast(numTags) + 2); + packet.addtag(','); + packet.addtag('i'); + ToOSCTypes::getTag(packet, static_cast(result)); + + packet.addi(static_cast(id)); + ToOSCTypes::convert(packet, static_cast(result)); + + if(replyAddr) + ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); + } + + static void messageOutput(const std::string& s,index id, MessageResult&, void* replyAddr) + { + small_scpacket packet; + packet.adds(s.c_str()); + packet.maketags(2); + packet.addtag(','); + packet.addtag('i'); + packet.addi(static_cast(id)); + + if(replyAddr) + ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); + } + + template + static void messageOutput(const std::string& s, index id, MessageResult>& result, void* replyAddr) + { + using T = std::tuple; + + index numTags = ToOSCTypes::numTags(static_cast(result)); + if(numTags > 2048) + { + std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl; + return; + } + + small_scpacket packet; + packet.adds(s.c_str()); + packet.maketags(static_cast(numTags + 3)); + packet.addtag(','); + packet.addtag('i'); + ToOSCTypes::getTag(packet,static_cast(result)); + + packet.addi(static_cast(id)); + ToOSCTypes::convert(packet, static_cast(result)); + + if(replyAddr) + ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); + + } +}; +} +} diff --git a/include/wrapper/Meta.hpp b/include/wrapper/Meta.hpp new file mode 100644 index 0000000..8b375cf --- /dev/null +++ b/include/wrapper/Meta.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +namespace fluid { +namespace client { +namespace impl { + /// Named, shared clients already have a lookup table in their adaptor class + template + struct IsNamedShared + { + using type = std::false_type; + }; + + //TODO: make less tied to current implementation + template + struct IsNamedShared>> + { + using type = std::true_type; + }; + + template + using IsNamedShared_t = typename IsNamedShared::type; + + template + constexpr bool IsNamedShared_v = IsNamedShared_t::value; + + /// 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 + struct IsModel> + { + using type = typename ClientWrapper::isModelObject; + }; + + template + using IsModel_t = typename IsModel::type; + + template + constexpr bool IsModel_v = IsModel_t::value; + + + +} +} +} diff --git a/include/wrapper/NonRealtime.hpp b/include/wrapper/NonRealtime.hpp new file mode 100644 index 0000000..adc74d8 --- /dev/null +++ b/include/wrapper/NonRealtime.hpp @@ -0,0 +1,957 @@ +#pragma once + +#include "BufferFuncs.hpp" +#include "Meta.hpp" +#include "SCBufferAdaptor.hpp" +#include "CopyReplyAddress.hpp" +#include "Messaging.hpp" +#include "RealTimeBase.hpp" + +#include +#include +#include +#include +#include + +namespace fluid { +namespace client { +namespace impl { + + /// Non Real Time Processor + + template + class NonRealTime : public SCUnit + { + using Params = typename Client::ParamSetType; + + template + static T* rtalloc(World* world,Args&&...args) + { + void* space = getInterfaceTable()->fRTAlloc(world, sizeof(T)); + return new (space) T{std::forward(args)...}; + } + + /// Instance cache + struct CacheEntry + { + + CacheEntry(const Params& p):mParams{p},mClient{mParams} + {} + + Params mParams; + Client mClient; + bool mDone{false}; + }; + + using CacheEntryPointer = std::shared_ptr; + using WeakCacheEntryPointer = std::weak_ptr; //could use weak_type in 17 + using Cache = std::map; + + static bool isNull(WeakCacheEntryPointer const& weak) { + return !weak.owner_before(WeakCacheEntryPointer{}) && !WeakCacheEntryPointer{}.owner_before(weak); + } + + static Cache mCache; + + public: + static WeakCacheEntryPointer get(index id) + { + auto lookup = mCache.find(id); + return lookup == mCache.end() ? WeakCacheEntryPointer() : lookup->second; + } + + static WeakCacheEntryPointer add(index id, const Params& params) + { + if(isNull(get(id))) + { + auto result = mCache.emplace(id, + std::make_shared(params)); + + return result.second ? (result.first)->second : WeakCacheEntryPointer(); //sob + } + else //client has screwed up + { + std::cout << "ERROR: " << Wrapper::getName() << " ID " << id << " already in use\n"; + return {}; + } + } + + static void remove(index id) + { + mCache.erase(id); + } + + static void printNotFound(index id) + { + std::cout << "ERROR: " << Wrapper::getName() << " no instance with ID " << id << std::endl; + } + + private: + static InterfaceTable* getInterfaceTable() { return Wrapper::getInterfaceTable() ;} + + template + using ParamsFromOSC = typename ClientParams::template Setter; + + template + using ParamsFromSynth = typename ClientParams::template Setter; + + + struct NRTCommand + { + NRTCommand(World*, sc_msg_iter* args, void* replyAddr, bool consumeID = true) + { + auto count = args->count; + auto pos = args->rdpos; + + mID = args->geti(); + + if(!consumeID) + { + args->count = count; + args->rdpos = pos; + } + + if(replyAddr) + mReplyAddress = copyReplyAddress(replyAddr); + } + + ~NRTCommand() + { + if(mReplyAddress) deleteReplyAddress(mReplyAddress); + } + + NRTCommand(){} + + explicit NRTCommand(index id):mID{id}{} + + bool stage2(World*) { return true; } //nrt + bool stage3(World*) { return true; } //rt + bool stage4(World*) { return false; } //nrt + void cleanup(World*) {} //rt + + void sendReply(const char* name,bool success) + { + if(mReplyAddress) + { + std::string slash{"/"}; + small_scpacket packet; + packet.adds((slash+name).c_str()); + packet.maketags(3); + packet.addtag(','); + packet.addtag('i'); + packet.addtag('i'); + packet.addi(success); + packet.addi(static_cast(mID)); + + ::SendReply(static_cast(mReplyAddress),packet.data(), static_cast(packet.size())); + } + } +// protected: + index mID; + void* mReplyAddress{nullptr}; + }; + + struct CommandNew : public NRTCommand + { + CommandNew(World* world, sc_msg_iter* args,void* replyAddr) + : NRTCommand{world,args, replyAddr, !IsNamedShared_v}, + mParams{Client::getParameterDescriptors()} + { + mParams.template setParameterValuesRT(nullptr, world, *args); + } + + CommandNew(index id, World*, FloatControlsIter& args, Unit* x) + :NRTCommand{id}, + mParams{Client::getParameterDescriptors()} + { + mParams.template setParameterValuesRT(nullptr, x, args); + } + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/new"; + return cmd.c_str(); + } + + bool stage2(World*) + { +// auto entry = ; + mResult = (!isNull(add(NRTCommand::mID, mParams))); + + //Sigh. The cache entry above has both the client instance and main params instance. + // The client is linked to the params by reference; I've not got the in-place constrction + // working properly so that params are in their final resting place by the time we make the client + // so (for) now we need to manually repoint the client to the correct place. Or badness. + if(mResult) + { + auto ptr = get(NRTCommand::mID).lock(); + ptr->mClient.setParams(ptr->mParams); + } + + NRTCommand::sendReply(name(),mResult); + + return mResult; + } + + private: + bool mResult; + Params mParams; + }; + + struct CommandFree: public NRTCommand + { + using NRTCommand::NRTCommand; + + + template + struct CancelCheck{ + void operator()(index id) + { + if(auto ptr = get(id).lock()) + { + auto& client = ptr->mClient; + if(!client.synchronous() && client.state() == ProcessState::kProcessing) + std::cout << Wrapper::getName() + << ": Processing cancelled" + << std::endl; + } + } + }; + + template<> + struct CancelCheck{ + void operator()(index) + {} + }; + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/free"; + return cmd.c_str(); + } + + bool stage2(World*) + { + CancelCheck()(NRTCommand::mID); + remove(NRTCommand::mID); + NRTCommand::sendReply(name(), true); + return true; + } + + }; + + + /// Not registered as a PlugInCmd. Triggered by worker thread callback + struct CommandAsyncComplete: public NRTCommand + { + CommandAsyncComplete(World*, index id, void* replyAddress) + { + NRTCommand::mID = id; + NRTCommand::mReplyAddress = replyAddress; + } + + static const char* name() { return CommandProcess::name(); } + + bool stage2(World* world) + { + + // std::cout << "In Async completion\n"; + if(auto ptr = get(NRTCommand::mID).lock()) + { + Result r; + auto& client = ptr->mClient; + ProcessState s = client.checkProgress(r); + if (s == ProcessState::kDone || s == ProcessState::kDoneStillProcessing) + { + if (r.status() == Result::Status::kCancelled) + { + std::cout << Wrapper::getName() + << ": Processing cancelled" + << std::endl; + ptr->mDone = true; + return false; + } + + client.checkProgress(r); + mSuccess = !(r.status() == Result::Status::kError); + if (!r.ok()) + { + Wrapper::printResult(world,r); + if(r.status() == Result::Status::kError) + { + ptr->mDone = true; + return false; + } + } + + return true; + } + } + return false; + } + + bool stage3(World* world) + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + auto& params = ptr->mParams; + params.template forEachParamType(world); + return true; + } + return false; + } + + bool stage4(World*) //nrt + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + ptr->mParams.template forEachParamType(); + + if(NRTCommand::mID >= 0 && NRTCommand::mReplyAddress) + { + NRTCommand::sendReply(name(),mSuccess); + } + ptr->mDone = true; + return true; + } + return false; + } + + bool mSuccess; + }; + + + static void doProcessCallback(World* world, index id,size_t completionMsgSize,char* completionMessage,void* replyAddress) + { + auto ft = getInterfaceTable(); + struct Context{ + World* mWorld; + index mID; + size_t mCompletionMsgSize; + char* mCompletionMessage; + void* mReplyAddress; + }; + + Context* c = new Context{world,id,completionMsgSize,completionMessage,replyAddress}; + + auto launchCompletionFromNRT = [](FifoMsg* inmsg) + { + auto runCompletion = [](FifoMsg* msg){ + // std::cout << "In FIFOMsg\n"; + Context* c = static_cast(msg->mData); + World* world = c->mWorld; + index id = c->mID; + auto ft = getInterfaceTable(); + void* space = ft->fRTAlloc(world,sizeof(CommandAsyncComplete)); + CommandAsyncComplete* cmd = new (space) CommandAsyncComplete(world, id,c->mReplyAddress); + runAsyncCommand(world, cmd, c->mReplyAddress, c->mCompletionMsgSize, c->mCompletionMessage); + }; + + auto tidyup = [](FifoMsg* msg) + { + Context* c = static_cast(msg->mData); + delete c; + }; + + auto ft = getInterfaceTable(); + FifoMsg fwd = *inmsg; + fwd.Set(inmsg->mWorld, runCompletion, tidyup, inmsg->mData); + if(inmsg->mWorld->mRunning) + ft->fSendMsgToRT(inmsg->mWorld,fwd); + }; + + FifoMsg msg; + msg.Set(world, launchCompletionFromNRT, nullptr, c); + + if(world->mRunning) ft->fSendMsgFromRT(world,msg); + } + + struct CommandProcess: public NRTCommand + { + CommandProcess(World* world, sc_msg_iter* args, void* replyAddr): NRTCommand{world, args, replyAddr} + { + auto& ar = *args; + + if(auto ptr = get(NRTCommand::mID).lock()) + { + ptr->mDone = false; + ptr->mParams.template setParameterValuesRT(nullptr, world, ar); + mSynchronous = static_cast(ar.geti()); + } //if this fails, we'll hear about it in stage2 anyway + } + + explicit CommandProcess(index id,bool synchronous):NRTCommand{id},mSynchronous(synchronous) + {} + + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/process"; + return cmd.c_str(); + } + + bool stage2(World* world) + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + auto& params = ptr->mParams; + auto& client = ptr->mClient; + +// if(mOSCData) +// { +// params.template setParameterValuesRT(nullptr, world, *mOSCData); +// mSynchronous = static_cast(mOSCData->geti()); +// } + + Result result = validateParameters(params); + Wrapper::printResult(world, result); + if (result.status() != Result::Status::kError) + { +// client.done() + client.setSynchronous(mSynchronous); + index id = NRTCommand::mID; + size_t completionMsgSize = mCompletionMsgSize; + char* completionMessage = mCompletionMessage; + void* replyAddress = copyReplyAddress(NRTCommand::mReplyAddress); + + auto callback = [world,id,completionMsgSize,completionMessage,replyAddress](){ + doProcessCallback(world,id,completionMsgSize,completionMessage,replyAddress); + }; + + result = mSynchronous ? client.enqueue(params) : client.enqueue(params,callback); + Wrapper::printResult(world, result); + + if(result.ok()) + { + ptr->mDone = false; + mResult = client.process(); + Wrapper::printResult(world,mResult); + + bool error =mResult.status() == Result::Status::kError; + + if(error) ptr->mDone = true; + return mSynchronous && !error; + } + } + } + else + { + mResult = Result{Result::Status::kError, "No ", Wrapper::getName(), " with ID ", NRTCommand::mID}; + Wrapper::printResult(world,mResult); + } + return false; + } + + //Only for blocking execution + bool stage3(World* world) //rt + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + ptr->mParams.template forEachParamType(world); +// NRTCommand::sendReply(world, name(), mResult.ok()); + return true; + } + // std::cout << "Ohno\n"; + return false; + } + + //Only for blocking execution + bool stage4(World*) //nrt + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + ptr->mParams.template forEachParamType(); + + if(NRTCommand::mID >= 0 && mSynchronous) + NRTCommand::sendReply(name(), mResult.ok()); + ptr->mDone = true; + return true; + } + return false; + } + + + bool synchronous() + { + return mSynchronous; + } + + void addCompletionMessage(size_t size, char* message)//, void* addr) + { + mCompletionMsgSize = size; + mCompletionMessage = message; + } + +// private: + Result mResult; + bool mSynchronous; + size_t mCompletionMsgSize{0}; + char* mCompletionMessage{nullptr}; + }; + + struct CommandProcessNew: public NRTCommand + { + CommandProcessNew(World* world, sc_msg_iter* args,void* replyAddr) + : mNew{world, args, replyAddr}, + mProcess{mNew.mID,false} + { + mProcess.mSynchronous = args->geti(); + mProcess.mReplyAddress = mNew.mReplyAddress; + } + + CommandProcessNew(index id, World* world, FloatControlsIter& args, Unit* x) + : mNew{id, world, args, x}, + mProcess{id} + {} + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/processNew"; + return cmd.c_str(); + } + + bool stage2(World* world) + { + return mNew.stage2(world) ? mProcess.stage2(world) : false; + } + + bool stage3(World* world) //rt + { + return mProcess.stage3(world); + } + + bool stage4(World* world) //nrt + { + return mProcess.stage4(world); + } + + void cleanup(World* world) + { + mProcess.mReplyAddress = nullptr; + mProcess.cleanup(world); + } + + bool synchronous() + { + return mProcess.synchronous(); + } + + void addCompletionMessage(size_t size, char* message) + { + mProcess.addCompletionMessage(size, message); + } + + private: + CommandNew mNew; + CommandProcess mProcess; + }; + + + struct CommandCancel: public NRTCommand + { + CommandCancel(World* world, sc_msg_iter* args, void* replyAddr) + : NRTCommand{world, args, replyAddr} + {} + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/cancel"; + return cmd.c_str(); + } + + bool stage2(World*) + { + if(auto ptr = get(NRTCommand::mID).lock()) + { + auto& client = ptr->mClient; + if(!client.synchronous()) + { + client.cancel(); + return true; + } + } + return false; + } + }; + + struct CommandSetParams: public NRTCommand + { + CommandSetParams(World* world, sc_msg_iter* args, void* replyAddr) + : NRTCommand{world, args, replyAddr} + { + auto& ar = *args; + if(auto ptr = get(NRTCommand::mID).lock()) + { + ptr->mParams.template setParameterValuesRT(nullptr, world, ar); + Result result = validateParameters(ptr->mParams); + ptr->mClient.setParams(ptr->mParams); + } else printNotFound(NRTCommand::mID); + } + + static const char* name() + { + static std::string cmd = std::string(Wrapper::getName()) + "/setParams"; + return cmd.c_str(); + } + }; + + + template + static auto runAsyncCommand(World* world, Command* cmd, void* replyAddr, + size_t completionMsgSize, char* completionMsgData) + { + auto ft = getInterfaceTable(); + + return ft->fDoAsynchronousCommand(world, replyAddr,Command::name(),cmd, + [](World* w, void* d) { return static_cast(d)->stage2(w); }, + [](World* w, void* d) { return static_cast(d)->stage3(w); }, + [](World* w, void* d) { return static_cast(d)->stage4(w); }, + [](World* w, void* d) + { + auto cmd = static_cast(d); + cmd->cleanup(w); + cmd->~Command(); + getInterfaceTable()->fRTFree(w,d); + }, + static_cast(completionMsgSize), completionMsgData); + } + + + static auto runAsyncCommand(World* world, CommandProcess* cmd, void* replyAddr, + size_t completionMsgSize, char* completionMsgData) + { + if(!cmd->synchronous()) + { + cmd->addCompletionMessage(completionMsgSize,completionMsgData); + return runAsyncCommand(world, cmd, replyAddr, 0, nullptr); + } + else return runAsyncCommand(world, cmd, replyAddr, completionMsgSize, completionMsgData); + } + + static auto runAsyncCommand(World* world, CommandProcessNew* cmd, void* replyAddr, + size_t completionMsgSize, char* completionMsgData) + { + if(!cmd->synchronous()) + { + cmd->addCompletionMessage(completionMsgSize,completionMsgData); + return runAsyncCommand(world, cmd, replyAddr, 0, nullptr); + } + else return runAsyncCommand(world, cmd, replyAddr, completionMsgSize, completionMsgData); + } + + + template + static void defineNRTCommand() + { + auto ft = getInterfaceTable(); + auto commandRunner = [](World* world, void*, struct sc_msg_iter* args, void* replyAddr) + { + + auto ft = getInterfaceTable(); + void* space = ft->fRTAlloc(world,sizeof(Command)); + Command* cmd = new (space) Command(world, args, replyAddr); + //This is brittle, but can't think of something better offhand + //This is the only place we can check for a completion message at the end of the OSC packet + //beause it has to be passed on to DoAsynhronousCommand at this point. However, detecting correctly + //relies on the Command type having fully consumed arguments from the args iterator in the constructor for cmd + size_t completionMsgSize{args ? args->getbsize() : 0}; + assert(completionMsgSize <= std::numeric_limits::max()); + char* completionMsgData = nullptr; + + if (completionMsgSize) { + completionMsgData = (char*)ft->fRTAlloc(world, completionMsgSize); + args->getb(completionMsgData, completionMsgSize); + } + runAsyncCommand(world, cmd, replyAddr, completionMsgSize, completionMsgData); + }; + ft->fDefinePlugInCmd(Command::name(),commandRunner,nullptr); + } + + + + struct NRTProgressUnit: SCUnit + { + + static const char* name() + { + static std::string n = std::string(Wrapper::getName()) + "Monitor"; + return n.c_str(); + } + + NRTProgressUnit() + { + mInterval = static_cast(0.02 / controlDur()); + set_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + } + + void next(int) + { + if (0 == mCounter++) + { + index id = static_cast(mInBuf[0][0]); + if(auto ptr = get(id).lock()) + { + if(ptr->mClient.done()) mDone = 1; + out0(0) = static_cast(ptr->mClient.progress()); + } + else + std::cout << "WARNING: No " << Wrapper::getName() << " with ID " << id << std::endl; + } + mCounter %= mInterval; + } + + private: + index mInterval; + index mCounter{0}; + }; + + + struct NRTTriggerUnit: SCUnit + { + + static index count(){ + static index counter = -1; + return counter--; + } + + index ControlOffset() { return mSpecialIndex + 1; } + + index ControlSize() + { + return index(mNumInputs) + - mSpecialIndex //used for oddball cases + - 3; //id + trig + blocking; + } + + static const char* name() + { + static std::string n = std::string(Wrapper::getName()) + "Trigger"; + return n.c_str(); + } + + NRTTriggerUnit() + : mControlsIterator{mInBuf + ControlOffset(),ControlSize()} + { + mID = static_cast(mInBuf[0][0]); + if(mID == -1) mID = count(); + auto cmd = NonRealTime::rtalloc(mWorld,mID,mWorld, mControlsIterator, this); + runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr); + set_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + } + + ~NRTTriggerUnit() + { + if(auto ptr = get(mID).lock()) + { + auto cmd = NonRealTime::rtalloc(mWorld,mID); + runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr); + } + } + + void next(int) + { + index triggerInput = static_cast(mInBuf[static_cast(mNumInputs) - 2][0]); + mTrigger = mTrigger || triggerInput; + + if(auto ptr = get(mID).lock()) + { + bool trigger = (mPreviousTrigger <= 0) && mTrigger > 0; + mPreviousTrigger = mTrigger; + mTrigger = 0; + auto& client = ptr->mClient; + + if(trigger) + { + mControlsIterator.reset(1 + mInBuf); //add one for ID + auto& params = ptr->mParams; + Wrapper::setParams(this,params,mControlsIterator,true,false); + bool blocking = mInBuf[mNumInputs - 1][0] > 0; + CommandProcess* cmd = rtalloc(mWorld,mID,blocking); + runAsyncCommand(mWorld,cmd, nullptr,0, nullptr); + mRunCount++; + } + else + { + mDone = ptr->mDone; + out0(0) = mDone ? 1 : static_cast(client.progress()); + } + } +// else printNotFound(id); + } + + private: + bool mPreviousTrigger{0}; + bool mTrigger{0}; + Result mResult; + impl::FloatControlsIter mControlsIterator; + index mID; + index mRunCount{0}; + }; + + struct NRTModelQueryUnit: SCUnit + { + using Delegate = impl::RealTimeBase; + + index ControlOffset() { return mSpecialIndex + 2; } + index ControlSize() + { + return index(mNumInputs) + - mSpecialIndex //used for oddball cases + - 2; // trig + id + } + + static const char* name() + { + static std::string n = std::string(Wrapper::getName()) + "/query"; + return n.c_str(); + } + + NRTModelQueryUnit() + //Offset controls by 1 to account for ID + : mControls{mInBuf + ControlOffset(),ControlSize()} + { + index id = static_cast(in0(1)); + if(auto ptr = get(id).lock()) + { + auto& client = ptr->mClient; + mDelegate.init(*this,client,mControls); + set_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + }else printNotFound(id); + } + + void next(int) + { + index id = static_cast(in0(1)); + if(auto ptr = get(id).lock()) + { + auto& client = ptr->mClient; + auto& params = ptr->mParams; + mControls.reset(mInBuf + ControlOffset()); + mDelegate.next(*this,client,params,mControls); + }else printNotFound(id); + } + + private: + Delegate mDelegate; + FloatControlsIter mControls; + index mID; + }; + + + using ParamSetType = typename Client::ParamSetType; + + template + using SetupMessageCmd = typename FluidSCMessaging::template SetupMessageCmd; + + + template + struct DefineCommandIf + { + void operator()() { } + }; + + + template + struct DefineCommandIf + { + void operator()() { + // std::cout << CommandType::name() << std::endl; + defineNRTCommand(); + } + }; + + template + struct RegisterUnitIf + { + void operator()(InterfaceTable*) {} + }; + + template + struct RegisterUnitIf + { + void operator()(InterfaceTable* ft) { registerUnit(ft,UnitType::name()); } + }; + + + using IsRTQueryModel_t = typename Client::isRealTime; + static constexpr bool IsRTQueryModel = IsRTQueryModel_t::value; + + static constexpr bool IsModel = Client::isModelObject::value; + + + public: + static void setup(InterfaceTable* ft, const char*) + { + defineNRTCommand(); + DefineCommandIf()(); + DefineCommandIf()(); + DefineCommandIf()(); + + DefineCommandIf()(); + + defineNRTCommand(); + RegisterUnitIf()(ft); + RegisterUnitIf()(ft); + + RegisterUnitIf()(ft); + Client::getMessageDescriptors().template iterate(); + } + + + void init(){}; + + private: + static Result validateParameters(ParamSetType& p) + { + auto results = p.constrainParameterValues(); + for (auto& r : results) + { + if (!r.ok()) return r; + } + return {}; + } + + 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(); + } + }; + + FifoMsg mFifoMsg; + char* mCompletionMessage = nullptr; + void* mReplyAddr = nullptr; + const char* mName = nullptr; + index checkThreadInterval; + index pollCounter{0}; + index mPreviousTrigger{0}; + + bool mSynchronous{true}; + Wrapper* mWrapper{static_cast(this)}; + Result mResult; + }; + + //initialize static cache + template + using Cache = typename NonRealTime::Cache; + + template + Cache NonRealTime::mCache{}; + +} +} +} diff --git a/include/wrapper/RealTimeBase.hpp b/include/wrapper/RealTimeBase.hpp new file mode 100644 index 0000000..607c5e7 --- /dev/null +++ b/include/wrapper/RealTimeBase.hpp @@ -0,0 +1,171 @@ +#pragma once + +#include + +namespace fluid{ +namespace client{ +namespace impl{ + template + struct RealTimeBase + { + using HostVector = FluidTensorView; + using Params = 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(controls), + std::forward(countScan)); + return countScan; + } + + // static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; } + // static index ControlSize(Unit* unit) { return static_cast(unit->mNumInputs) - unit->mSpecialIndex - 1 -(IsModel_t::value ? 1 : 0); } + + void init(SCUnit& unit, Client& client, FloatControlsIter& controls) + { + assert(!(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) &&"Client can't have both audio and control outputs"); + // consoltr.reset(unit.mInBuf + unit.mSpecialIndex + 1); + client.sampleRate(unit.fullSampleRate()); + mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); + mOutputConnections.reserve(asUnsigned(client.audioChannelsOut())); + mAudioInputs.reserve(asUnsigned(client.audioChannelsIn())); + mOutputs.reserve(asUnsigned( + std::max(client.audioChannelsOut(), client.controlChannelsOut()))); + + + Result r; + if(!(r = expectedSize(controls)).ok()) + { +// mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; + std::cout + << "ERROR: " << Wrapper::getName() + << " wrong number of arguments." + << r.message() + << std::endl; + return; + } + + + for (index i = 0; i < client.audioChannelsIn(); ++i) + { + mInputConnections.emplace_back(unit.isAudioRateIn(static_cast(i))); + mAudioInputs.emplace_back(nullptr, 0, 0); + } + + for (index i = 0; i < client.audioChannelsOut(); ++i) + { + mOutputConnections.emplace_back(true); + mOutputs.emplace_back(nullptr, 0, 0); + } + + for (index i = 0; i < client.controlChannelsOut(); ++i) + { + mOutputs.emplace_back(nullptr, 0, 0); + } + } + + void next(SCUnit& unit, Client& client,Params& params,FloatControlsIter& controls) + { + bool trig = IsModel_t::value ? !mPrevTrig && unit.in0(0) > 0 : false; + bool shouldProcess = IsModel_t::value ? trig : true; + mPrevTrig = trig; + +// if(shouldProcess) +// { + // controls.reset(unit.mInBuf + unit.mSpecialIndex + 1); + Wrapper::setParams(&unit, params, controls); + params.constrainParameterValues(); +// } + + for (index i = 0; i < client.audioChannelsIn(); ++i) + { + assert(i <= std::numeric_limits::max()); + if (mInputConnections[asUnsigned(i)]) + mAudioInputs[asUnsigned(i)].reset(const_cast(unit.in(static_cast(i))), 0, + unit.fullBufferSize()); + } + + for (index i = 0; i < client.audioChannelsOut(); ++i) + { + assert(i <= std::numeric_limits::max()); + if (mOutputConnections[asUnsigned(i)]) + mOutputs[asUnsigned(i)].reset(unit.out(static_cast(i)), 0, + unit.fullBufferSize()); + } + + for (index i = 0; i < client.controlChannelsOut(); ++i) + { + assert(i <= std::numeric_limits::max()); + mOutputs[asUnsigned(i)].reset(unit.out(static_cast(i)), 0, 1); + } + client.process(mAudioInputs, mOutputs, mContext); + } + private: + std::vector mInputConnections; + std::vector mOutputConnections; + std::vector mAudioInputs; + std::vector mOutputs; + FluidContext mContext; + bool mPrevTrig; + }; +} +} +} diff --git a/include/wrapper/Realtime.hpp b/include/wrapper/Realtime.hpp new file mode 100644 index 0000000..e6e183a --- /dev/null +++ b/include/wrapper/Realtime.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include "ArgsFromClient.hpp" +#include "Meta.hpp" +#include "RealTimeBase.hpp" +#include +#include + +// Real Time Processor +namespace fluid { +namespace client { +namespace impl { + +template +class RealTime : public SCUnit +{ + + using Delegate = impl::RealTimeBase; + using Params = typename Client::ParamSetType; + +public: + + // static index ControlOffset(Unit* unit) { return Delegate::ControlOffset(unit); } + // static index ControlSize(Unit* unit) { return Delegate::ControlSize(unit); } + + static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; } + static index ControlSize(Unit* unit) + { + return static_cast(unit->mNumInputs) + - unit->mSpecialIndex + - 1 + - (IsModel_t::value ? 1 : 0); + } + + static void setup(InterfaceTable* ft, const char* name) + { + ft->fDefineUnitCmd(name, "latency", doLatency); + registerUnit(ft,name); + } + + static void doLatency(Unit* unit, sc_msg_iter*) + { + float l[]{ + static_cast(static_cast(unit)->mClient.latency()) + }; + auto ft = Wrapper::getInterfaceTable(); + + std::stringstream ss; + ss << '/' << Wrapper::getName() << "_latency"; + std::cout << ss.str() << std::endl; + ft->fSendNodeReply(&unit->mParent->mNode, -1, ss.str().c_str(), 1, l); + } + + RealTime() + : mControls{mInBuf + ControlOffset(this),ControlSize(this)}, + mClient{Wrapper::setParams(this, mParams, mControls,true)} + { + init(); + } + + void init() + { +// auto& client = mClient; + + mDelegate.init(*this,mClient,mControls); + mCalcFunc = make_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + + // assert( + // !(client.audioChannelsOut() > 0 && client.controlChannelsOut() > 0) && + // "Client can't have both audio and control outputs"); + // + // Result r; + // if(!(r = expectedSize(mWrapper->mControlsIterator)).ok()) + // { + // mCalcFunc = Wrapper::getInterfaceTable()->fClearUnitOutputs; + // std::cout + // << "ERROR: " << Wrapper::getName() + // << " wrong number of arguments." + // << r.message() + // << std::endl; + // return; + // } + // + // mWrapper->mControlsIterator.reset(mInBuf + mSpecialIndex + 1); + // + // client.sampleRate(fullSampleRate()); + // mInputConnections.reserve(asUnsigned(client.audioChannelsIn())); + // mOutputConnections.reserve(asUnsigned(client.audioChannelsOut())); + // mAudioInputs.reserve(asUnsigned(client.audioChannelsIn())); + // mOutputs.reserve(asUnsigned( + // std::max(client.audioChannelsOut(), client.controlChannelsOut()))); + // + // 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 < client.audioChannelsOut(); ++i) + // { + // mOutputConnections.emplace_back(true); + // mOutputs.emplace_back(nullptr, 0, 0); + // } + // + // for (index i = 0; i < client.controlChannelsOut(); ++i) + // { mOutputs.emplace_back(nullptr, 0, 0); } + // + // mCalcFunc = make_calc_function(); + // Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + } + + void next(int) + { + mControls.reset(mInBuf + ControlOffset(this)); + mDelegate.next(*this,mClient,mParams,mControls); + } +private: + Delegate mDelegate; + FloatControlsIter mControls; + Params mParams{Client::getParameterDescriptors()}; + Client mClient; + Wrapper* mWrapper{static_cast(this)}; +}; + +} +} +} diff --git a/release-packaging/Classes/FluidBufAmpGate.sc b/release-packaging/Classes/FluidBufAmpGate.sc index 29faef9..0063f3c 100644 --- a/release-packaging/Classes/FluidBufAmpGate.sc +++ b/release-packaging/Classes/FluidBufAmpGate.sc @@ -1,36 +1,43 @@ -FluidBufAmpGate : UGen { +FluidBufAmpGate : FluidBufProcessor { - *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)); + *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, blocking = 0| + + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); - source = source.asUGenInput; - indices = indices.asUGenInput; + source = source.asUGenInput; + indices = indices.asUGenInput; + + ^FluidProxyUgen.kr(\FluidBufAmpGateTrigger,-1, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq,maxSize, trig, blocking); + } - source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + *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, freeWhenDone = true, action | - ^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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, trig, blocking); - } + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); - *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 | + source = source ? -1; + indices = indices ? -1; - ^FluidNRTProcess.new( - server, this, action, [indices] - ).process( - source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 0],freeWhenDone,action ); } - *processBlocking { |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| + *processBlocking { |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, freeWhenDone = true, action | - ^FluidNRTProcess.new( - server, this, action, [indices], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq - ); - } + + var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead)); + + source = source ? -1; + indices = indices ? -1; + + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action + ); + } + } diff --git a/release-packaging/Classes/FluidBufAmpSlice.sc b/release-packaging/Classes/FluidBufAmpSlice.sc index dd2e46c..0e20f07 100644 --- a/release-packaging/Classes/FluidBufAmpSlice.sc +++ b/release-packaging/Classes/FluidBufAmpSlice.sc @@ -1,35 +1,39 @@ -FluidBufAmpSlice : UGen { +FluidBufAmpSlice : FluidBufProcessor { - *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| + *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, blocking = 0| + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufAmpSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking); + } - source = source.asUGenInput; - indices = indices.asUGenInput; + *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, freeWhenDone = true, action | - source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; + source = source.asUGenInput; + indices = indices.asUGenInput; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking); - } + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - *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, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking); + ^this.new(server, nil, [indices]).processList( + [source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,0],freeWhenDone, action + ); } - *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 | + *processBlocking { |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, freeWhenDone = true, action | - ^FluidNRTProcess.new( - server, this, action, [indices] - ).process( - source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq - ); - } + source = source.asUGenInput; + indices = indices.asUGenInput; - *processBlocking { |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| + source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw}; - ^FluidNRTProcess.new( - server, this, action, [indices], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq + ^this.new(server, nil, [indices]).processList( + [source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,1],freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufAudioTransport.sc b/release-packaging/Classes/FluidBufAudioTransport.sc index 783ef75..66e5cb0 100644 --- a/release-packaging/Classes/FluidBufAudioTransport.sc +++ b/release-packaging/Classes/FluidBufAudioTransport.sc @@ -1,40 +1,58 @@ -FluidBufAudioTransport : UGen{ - *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| +FluidBufAudioTransport : FluidBufProcessor { - 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; + *objectClassName{ + ^\FluidBufAudioTransp + } - destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw}; - destination = destination.asUGenInput; + *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, blocking = 0| - //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) + 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; - } - *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, blocking = 0| - ^this.new1(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, trig, blocking); + ^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, 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| - ^FluidNRTProcess.new( - server, this, action, [destination] - ).process( - source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize + *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, freeWhenDone = true, action| + + 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; + + ^this.new( + server, nil, [destination] + ).processList( + [source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize,0], freeWhenDone, action ) } - *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 + *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, freeWhenDone = true, action| + + 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; + + ^this.new( + server, nil, [destination] + ).processList( + [source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize,1], freeWhenDone, action ) } } - diff --git a/release-packaging/Classes/FluidBufCompose.sc b/release-packaging/Classes/FluidBufCompose.sc index a681ceb..c060466 100644 --- a/release-packaging/Classes/FluidBufCompose.sc +++ b/release-packaging/Classes/FluidBufCompose.sc @@ -1,33 +1,38 @@ -FluidBufCompose : UGen { - - *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; - - 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, trig, blocking); - } +FluidBufCompose : FluidBufProcessor { *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1| - ^this.new1('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking); } - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action| - ^FluidNRTProcess.new( - server, this, action, [destination], blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain - ); + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + + ^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action| - ^process( - source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain - ); + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufCompose: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination] + ).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action); } } diff --git a/release-packaging/Classes/FluidBufFlatten.sc b/release-packaging/Classes/FluidBufFlatten.sc index f08833b..e664290 100644 --- a/release-packaging/Classes/FluidBufFlatten.sc +++ b/release-packaging/Classes/FluidBufFlatten.sc @@ -1,31 +1,46 @@ -FluidBufFlatten : UGen { +FluidBufFlatten : FluidBufProcessor { - *new1 { |rate, source, destination, axis = 1, trig = 1, blocking| - source = source.asUGenInput; - destination = destination.asUGenInput; + *kr { |source, destination, axis = 1, trig = 1, blocking = 1| + + 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); + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, destination, axis, trig, blocking); } - *kr { |source, destination, axis = 1, trig = 1, blocking = 1| - ^this.new1('control', source, destination, axis, trig, blocking); - } + *process { |server, source, destination, axis = 1, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; - *process { |server, source, destination, axis = 1, action| - ^FluidNRTProcess.new( - server, this, action, [destination], blocking:1 - ).process( - source, destination, axis + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination], + ).processList( + [source, destination, axis,0],freeWhenDone,action ); } - *processBlocking { |server, source, destination, axis = 1, action| - ^process( - source, destination, axis - ); - } + *processBlocking { |server, source, destination, axis = 1, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination], + ).processList( + [source, destination, axis,1],freeWhenDone,action + ); + + } } diff --git a/release-packaging/Classes/FluidBufHPSS.sc b/release-packaging/Classes/FluidBufHPSS.sc index 33ae626..b7b0a9a 100644 --- a/release-packaging/Classes/FluidBufHPSS.sc +++ b/release-packaging/Classes/FluidBufHPSS.sc @@ -1,45 +1,51 @@ -FluidBufHPSS : UGen { +FluidBufHPSS : FluidBufProcessor { - *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| + *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, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - harmonic = harmonic.asUGenInput; - percussive = percussive.asUGenInput; - residual = residual.asUGenInput; - source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; - //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, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, 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, trig = 1, blocking = 0| + *process {|server, 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, freeWhenDone=true, action| - ^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, trig, blocking - ); - } + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; - *process {|server, 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, action| - ^FluidNRTProcess.new( - server, this, action, [harmonic, percussive, residual].select{|x| x!= -1} - ).process( - source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize + ^this.new( + server, nil, [harmonic, percussive, residual].select{|x| x!= -1} + ).processList( + [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,0], freeWhenDone,action ); } - *processBlocking {|server, 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, action| + *processBlocking {|server, 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, freeWhenDone=true, action| - ^FluidNRTProcess.new( - server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}, blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize - ); + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - } + harmonic = harmonic ? -1; + percussive = percussive ? -1; + residual = residual ? -1; + source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw}; + + + ^this.new( + server, nil, [harmonic, percussive, residual].select{|x| x!= -1} + ).processList( + [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,1], freeWhenDone,action + ); + + } } diff --git a/release-packaging/Classes/FluidBufLoudness.sc b/release-packaging/Classes/FluidBufLoudness.sc index 31675e8..c6c5568 100644 --- a/release-packaging/Classes/FluidBufLoudness.sc +++ b/release-packaging/Classes/FluidBufLoudness.sc @@ -1,6 +1,6 @@ -FluidBufLoudness : UGen{ - *new1 { |rate,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0| - +FluidBufLoudness : FluidBufProcessor{ + *kr { |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; source = source.asUGenInput; @@ -8,27 +8,41 @@ 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, trig, blocking); + + ^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, 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, trig = 1, blocking = 0| - ^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, trig, blocking ); - } + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action| + + var maxwindowSize = windowSize.nextPowerOfTwo; + + source = source.asUGenInput; + features = features.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action| - ^FluidNRTProcess.new( - server, this, action, [features] - ).process( - source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action| - ^FluidNRTProcess.new( - server, this, action, [features], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action| + + var maxwindowSize = windowSize.nextPowerOfTwo; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action ); } } diff --git a/release-packaging/Classes/FluidBufMFCC.sc b/release-packaging/Classes/FluidBufMFCC.sc index 845c8a2..f7bd1c3 100644 --- a/release-packaging/Classes/FluidBufMFCC.sc +++ b/release-packaging/Classes/FluidBufMFCC.sc @@ -1,37 +1,45 @@ -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, trig = 1, blocking = 0| - +FluidBufMFCC : FluidBufProcessor{ + *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, blocking = 0| + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; features = features.asUGenInput; source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; - - //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) - // 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, trig, blocking); + + ^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, 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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, trig, blocking); - } + *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,freeWhenDone=true, action | + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + features = features.asUGenInput; - *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 | - ^FluidNRTProcess.new( - server, this, action, [features] - ).process( - source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + + ^this.new( + server, nil,[features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs,windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action ); } - *processBlocking { |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 | - ^FluidNRTProcess.new( - server, this, action, [features],blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize + *processBlocking { |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,freeWhenDone=true, action | + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw}; + + ^this.new( + server, nil,[features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action ); } } diff --git a/release-packaging/Classes/FluidBufMelBands.sc b/release-packaging/Classes/FluidBufMelBands.sc index 28ee4b4..d238e61 100644 --- a/release-packaging/Classes/FluidBufMelBands.sc +++ b/release-packaging/Classes/FluidBufMelBands.sc @@ -1,38 +1,50 @@ -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, trig = 1, blocking = 0| +FluidBufMelBands : FluidBufProcessor { - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + *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, blocking = 0| - source = source.asUGenInput; - features = features.asUGenInput; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; - features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + source = source.asUGenInput; + features = features.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) - // same for maxNumBands which is passed numBands + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; - ^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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufMelBandsTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize, trig, blocking); } - *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| - ^FluidNRTProcess.new( - server, this, action, [features] - ).process( - source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize + *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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action ); } - *processBlocking { |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| - ^FluidNRTProcess.new( - server, this, action, [features], blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize + *processBlocking { |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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw}; + features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action ); + } } -} \ No newline at end of file diff --git a/release-packaging/Classes/FluidBufNMF.sc b/release-packaging/Classes/FluidBufNMF.sc index 74ee122..c21873b 100644 --- a/release-packaging/Classes/FluidBufNMF.sc +++ b/release-packaging/Classes/FluidBufNMF.sc @@ -1,47 +1,36 @@ -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, trig = 1, blocking = 0| - - source = source.asUGenInput; - resynth = resynth.asUGenInput; - bases = bases.asUGenInput; - activations = activations.asUGenInput; - - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; +FluidBufNMF : FluidBufProcessor //: 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, blocking = 0| + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; resynth = resynth ? -1; bases = bases ? -1; - activations = activations ? -1; - - ^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, trig = 1, blocking = 0| - ^this.new1(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking); - + activations = activations ? -1; + + ^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking); } + + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1,freeWhenDone = true, action| - - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action| - - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; - - ^FluidNRTProcess.new( - server, this, action, [resynth, bases, activations].select{|x| x!= -1} - ).process( - source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed - ); + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; + resynth = resynth ? -1; + bases = bases ? -1; + activations = activations ? -1; + + ^this.new( + server,nil,[resynth, bases, activations].select{|x| x!= -1} + ).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize,0],freeWhenDone,action); } + + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1,freeWhenDone = true, action| - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action| - - source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; - - ^FluidNRTProcess.new( - server, this, action, [resynth, bases, activations].select{|x| x!= -1},blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed - ); + source.isNil.if {"FluidBufNMF: Invalid source buffer".throw}; + resynth = resynth ? -1; + bases = bases ? -1; + activations = activations ? -1; + + ^this.new( + server,nil,[resynth, bases, activations].select{|x| x!= -1} + ).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, 1],freeWhenDone,action); } } diff --git a/release-packaging/Classes/FluidBufNMFCross.sc b/release-packaging/Classes/FluidBufNMFCross.sc index 354e66f..22499b5 100644 --- a/release-packaging/Classes/FluidBufNMFCross.sc +++ b/release-packaging/Classes/FluidBufNMFCross.sc @@ -1,34 +1,48 @@ -FluidBufNMFCross : UGen{ - - *new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - +FluidBufNMFCross : FluidBufProcessor { + + *kr { |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; - output = output.asUGenInput; - source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw}; - target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw}; + 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, trig, blocking); + + ^FluidProxyUgen.kr(\FluidBufNMFCrossTrigger, -1, 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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking); - } + *process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + 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}; - *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 + + ^this.new( + server, nil, [output] + ).processList( + [source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize,0],freeWhenDone, action ); } - *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 + *processBlocking { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + 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}; + + + ^this.new( + server, nil, [output] + ).processList( + [source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize,1],freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufNNDSVD.sc b/release-packaging/Classes/FluidBufNNDSVD.sc index 128c6fa..05d76ea 100644 --- a/release-packaging/Classes/FluidBufNNDSVD.sc +++ b/release-packaging/Classes/FluidBufNNDSVD.sc @@ -1,35 +1,47 @@ -FluidBufNNDSVD : UGen{ - *new1 { |rate, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking=0| - - source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw}; - bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; - activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; - source = source.asUGenInput; - bases = bases.asUGenInput; - activations = activations.asUGenInput; - - ^super.new1(rate, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking) - - } +FluidBufNNDSVD : FluidBufProcessor{ *kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - ^this.new1(\control, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking); + + source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; + + ^FluidProxyUgen.kr1(\FluidBufNNDSVDTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking); } - *process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [bases] - ).process( - source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize + *process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; + + ^this.new( + server, nil, [bases] + ).processList( + [source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,0],freeWhenDone, action ) } - *processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [bases],blocking:1 - ).process( - source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize + *processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw}; + bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw}; + source = source.asUGenInput; + bases = bases.asUGenInput; + activations = activations.asUGenInput; + + ^this.new( + server, nil, [bases] + ).processList( + [source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,1],freeWhenDone, action ) } } diff --git a/release-packaging/Classes/FluidBufNoveltySlice.sc b/release-packaging/Classes/FluidBufNoveltySlice.sc index 0ee8bcf..bae3651 100644 --- a/release-packaging/Classes/FluidBufNoveltySlice.sc +++ b/release-packaging/Classes/FluidBufNoveltySlice.sc @@ -1,7 +1,8 @@ -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, trig = 1, blocking = 0 | +FluidBufNoveltySlice : FluidBufProcessor { - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + *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 , blocking = 0| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; source = source.asUGenInput; indices = indices.asUGenInput; @@ -9,29 +10,41 @@ 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, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufNoveltySliceTrigger, -1, 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, trig = 1 , blocking = 0| + *process { |server, 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, freeWhenDone = true, action | + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, trig, blocking); + source = source.asUGenInput; + indices = indices.asUGenInput; - } - - *process { |server, 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, action | - ^FluidNRTProcess.new( - server, this, action, [indices] - ).process( - source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize,0],freeWhenDone,action ); } - *processBlocking { |server, 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, action | - ^FluidNRTProcess.new( - server, this, action, [indices], blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize + *processBlocking { |server, 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, freeWhenDone = true, action | + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize,1],freeWhenDone,action ); } } diff --git a/release-packaging/Classes/FluidBufOnsetSlice.sc b/release-packaging/Classes/FluidBufOnsetSlice.sc index 95568c0..77f6f38 100644 --- a/release-packaging/Classes/FluidBufOnsetSlice.sc +++ b/release-packaging/Classes/FluidBufOnsetSlice.sc @@ -1,37 +1,48 @@ -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, trig = 1, blocking = 0| +FluidBufOnsetSlice : FluidBufProcessor { + *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, blocking = 0| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + source = source.asUGenInput; + indices = indices.asUGenInput; - source = source.asUGenInput; - indices = indices.asUGenInput; + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufOnsetSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); + } - source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + *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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - //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) + source = source.asUGenInput; + indices = indices.asUGenInput; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action + ); } - *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, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, trig, blocking); - } + *processBlocking { |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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - *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| - ^FluidNRTProcess.new( - server, this, action, [indices] - ).process( - source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize - ); - } + source = source.asUGenInput; + indices = indices.asUGenInput; - *processBlocking { |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| - ^FluidNRTProcess.new( - server, this, action, [indices], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize + source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw}; + + ^this.new( + server, nil, [indices] + ).processList( + [source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action ); } } diff --git a/release-packaging/Classes/FluidBufPitch.sc b/release-packaging/Classes/FluidBufPitch.sc index a53318a..3092496 100644 --- a/release-packaging/Classes/FluidBufPitch.sc +++ b/release-packaging/Classes/FluidBufPitch.sc @@ -1,39 +1,50 @@ -FluidBufPitch : UGen{ +FluidBufPitch : FluidBufProcessor{ + + *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, blocking = 0| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - *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| + source = source.asUGenInput; + features = features.asUGenInput; - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - - source = source.asUGenInput; - features = features.asUGenInput; - - source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; - features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; - - //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, trig, blocking); + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, 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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, trig, blocking); + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - } + source = source.asUGenInput; + features = features.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [features] - ).process( - source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [features], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufPitch: Invalid source buffer".throw}; + features.isNil.if {"FluidBufPitch: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufScale.sc b/release-packaging/Classes/FluidBufScale.sc index c56ee35..a36425d 100644 --- a/release-packaging/Classes/FluidBufScale.sc +++ b/release-packaging/Classes/FluidBufScale.sc @@ -1,31 +1,43 @@ -FluidBufScale : UGen { +FluidBufScale : FluidBufProcessor { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking| - - source = source.asUGenInput; - destination = destination.asUGenInput; + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking = 1| + + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking); + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking = 1| - ^this.new1('control', source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking); - } + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, action| - ^FluidNRTProcess.new( - server, this, action, [destination], blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination] + ).processList( + [source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, 0], freeWhenDone, action ); - } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, action| - ^process( - source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh - ); + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufScale: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination] + ).processList( + [source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, 1], freeWhenDone, action + ); } } diff --git a/release-packaging/Classes/FluidBufSines.sc b/release-packaging/Classes/FluidBufSines.sc index 4b6d938..c4056db 100644 --- a/release-packaging/Classes/FluidBufSines.sc +++ b/release-packaging/Classes/FluidBufSines.sc @@ -1,38 +1,49 @@ -FluidBufSines : UGen{ +FluidBufSines : FluidBufProcessor { - *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| + *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, blocking = 0| - var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - source = source.asUGenInput; - sines = sines.asUGenInput; - residual = residual.asUGenInput; + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; - source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; - //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, trig, blocking); + ^FluidProxyUgen.multiNew(\FluidBufSinesTrigger, -1, 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, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, trig, blocking); - } + *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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; + + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; - *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| - ^FluidNRTProcess.new( - server, this, action, [sines, residual].select{|x| x!= -1} - ).process( - source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize + ^this.new( + server, nil, [sines, residual].select{|x| x!= -1} + ).processList( + [source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone = true,action ); } - *processBlocking { |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| - ^FluidNRTProcess.new( - server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize + *processBlocking { |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, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + sines = sines !? {sines.asUGenInput} ?? {-1}; + residual = residual !? {residual.asUGenInput} ?? {-1}; + + source.isNil.if {"FluidBufSines: Invalid source buffer".throw}; + + ^this.new( + server, nil, [sines, residual].select{|x| x!= -1} + ).processList( + [source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action ); } diff --git a/release-packaging/Classes/FluidBufSpectralShape.sc b/release-packaging/Classes/FluidBufSpectralShape.sc index 4c2cbf6..837522a 100644 --- a/release-packaging/Classes/FluidBufSpectralShape.sc +++ b/release-packaging/Classes/FluidBufSpectralShape.sc @@ -1,6 +1,10 @@ -FluidBufSpectralShape : UGen { +FluidBufSpectralShape : FluidBufProcessor { - *new1{ |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| + *objectClassName{ + ^\FluidBufSpecShp + } + + *kr { |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}; @@ -10,33 +14,41 @@ FluidBufSpectralShape : UGen { source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw}; - //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, trig, blocking); + ^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking); } + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, trig, blocking); + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; - } + source = source.asUGenInput; + features = features.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [features] - ).process( - source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize + source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; + features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action| - ^FluidNRTProcess.new( - server, this, action, [features], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action| + + var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize}; + + source = source.asUGenInput; + features = features.asUGenInput; + + source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw}; + features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw}; + + ^this.new( + server, nil, [features] + ).processList( + [source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action ); } - } diff --git a/release-packaging/Classes/FluidBufStats.sc b/release-packaging/Classes/FluidBufStats.sc index 75240a1..e29f5d4 100644 --- a/release-packaging/Classes/FluidBufStats.sc +++ b/release-packaging/Classes/FluidBufStats.sc @@ -1,36 +1,51 @@ -FluidBufStats : UGen{ +FluidBufStats : FluidBufProcessor { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0| - - source = source.asUGenInput; - stats = stats.asUGenInput; + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0| + + source = source.asUGenInput; + stats = stats.asUGenInput; weights = weights.asUGenInput; - source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; - stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; - weights = weights ? -1; - - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking); + source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; + stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; + weights = weights ? -1; + + ^FluidProxyUgen.kr(\FluidBufStatsTrigger, -1, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking); - } + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action| + + source = source.asUGenInput; + stats = stats.asUGenInput; + weights = weights.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action| - ^FluidNRTProcess.new( - server, this, action, [stats] - ).process( - source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights + source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; + stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; + weights = weights ? -1; + + ^this.new( + server, nil, [stats] + ).processList( + [source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action| - ^FluidNRTProcess.new( - server, this, action, [stats], blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action| + + source = source.asUGenInput; + stats = stats.asUGenInput; + weights = weights.asUGenInput; + + source.isNil.if {"FluidBufStats: Invalid source buffer".throw}; + stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw}; + weights = weights ? -1; + + ^this.new( + server, nil, [stats] + ).processList( + [source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights, 1], freeWhenDone, action ); } + } diff --git a/release-packaging/Classes/FluidBufThreadDemo.sc b/release-packaging/Classes/FluidBufThreadDemo.sc index 17795ea..03f5c3d 100644 --- a/release-packaging/Classes/FluidBufThreadDemo.sc +++ b/release-packaging/Classes/FluidBufThreadDemo.sc @@ -1,29 +1,33 @@ -FluidBufThreadDemo : UGen{ +FluidBufThreadDemo : FluidBufProcessor{ - *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, trig, blocking); - } + *kr {|result, time, trig = 1, blocking = 0| + result = result.asUGenInput; + result.isNil.if {this.class.name+": Invalid output buffer".throw}; - *kr {|result, time, trig = 1, blocking = 0| - ^this.new1(\control, result, time, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufThreadDemoTrigger, -1, result, time, trig, blocking); } - *process { |server, result, time = 1000, action| - ^FluidNRTProcess.new( - server, this, action, [result] - ).process( - result, time + *process { |server, result, time = 1000, freeWhenDone = true, action| + + + result ?? {this.class.name+": Invalid output buffer".throw}; + + ^this.new( + server, nil, [result] + ).processList( + [result.asUGenInput, time, 0], freeWhenDone, action ); } - *processBlocking { |server, result, time = 1000, action| - ^FluidNRTProcess.new( - server, this, action, [result], blocking: 1 - ).process( - result, time + *processBlocking { |server, result, time = 1000, freeWhenDone = true, action| + + result ?? {this.class.name+": Invalid output buffer".throw}; + + ^this.new( + server, nil, [result] + ).processList( + [result.asUGenInput, time, 1], freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufThresh.sc b/release-packaging/Classes/FluidBufThresh.sc index ca4c942..47ecaff 100644 --- a/release-packaging/Classes/FluidBufThresh.sc +++ b/release-packaging/Classes/FluidBufThresh.sc @@ -1,31 +1,45 @@ -FluidBufThresh : UGen { +FluidBufThresh : FluidBufProcessor { - *new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking| - - source = source.asUGenInput; - destination = destination.asUGenInput; + *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1| + + source = source.asUGenInput; + destination = destination.asUGenInput; - source.isNil.if {"FluidBufThresh: Invalid source buffer".throw}; - destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw}; - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking); + source.isNil.if {"FluidBufThresh: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw}; + + ^FluidProxyUgen.kr(\FluidBufThreshTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking); } - *kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1| - ^this.new1('control', source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking); - } + *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; - *process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, action| - ^FluidNRTProcess.new( - server, this, action, [destination], blocking:1 - ).process( - source, startFrame, numFrames, startChan, numChans, destination, threshold + source.isNil.if {"FluidBufThresh: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination], + ).processList( + [source, startFrame, numFrames, startChan, numChans, destination, threshold, 0], freeWhenDone, action ); } - *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, action| - ^process( - source, startFrame, numFrames, startChan, numChans, destination, threshold - ); + *processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action| + + source = source.asUGenInput; + destination = destination.asUGenInput; + + source.isNil.if {"FluidBufThresh: Invalid source buffer".throw}; + destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw}; + + ^this.new( + server, nil, [destination], + ).processList( + [source, startFrame, numFrames, startChan, numChans, destination, threshold, 1], freeWhenDone, action + ); + } } diff --git a/release-packaging/Classes/FluidBufTransientSlice.sc b/release-packaging/Classes/FluidBufTransientSlice.sc index e68d043..7329236 100644 --- a/release-packaging/Classes/FluidBufTransientSlice.sc +++ b/release-packaging/Classes/FluidBufTransientSlice.sc @@ -1,30 +1,43 @@ -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, trig = 1, blocking = 0| +FluidBufTransientSlice : FluidBufProcessor { - source = source.asUGenInput; - indices = indices.asUGenInput; + *objectClassName{^\FluidBufTrSlice} - source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; - indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + *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, blocking = 0| - ^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking); - } + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; - *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, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking); + ^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking); } - *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| - ^FluidNRTProcess.new( - server, this, action,[indices] - ).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength + *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, freeWhenDone = true, action| + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + + ^this.new( + server, nil,[indices] + ).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,0], freeWhenDone, action ); } - *processBlocking { |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| - ^FluidNRTProcess.new( - server, this, action,[indices], blocking: 1 - ).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength + *processBlocking { |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, freeWhenDone = true, action| + + source = source.asUGenInput; + indices = indices.asUGenInput; + + source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw}; + indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw}; + + ^this.new( + server, nil,[indices] + ).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,1], freeWhenDone, action ); } } diff --git a/release-packaging/Classes/FluidBufTransients.sc b/release-packaging/Classes/FluidBufTransients.sc index e87f167..c20f73a 100644 --- a/release-packaging/Classes/FluidBufTransients.sc +++ b/release-packaging/Classes/FluidBufTransients.sc @@ -1,36 +1,43 @@ -FluidBufTransients : UGen { +FluidBufTransients : FluidBufProcessor { + *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, 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 ? -1; + residual = residual ? -1; - source = source.asUGenInput; - transients = transients.asUGenInput; - residual = residual.asUGenInput; + source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; - 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, trig, blocking); + ^FluidProxyUgen.kr(\FluidBufTransientsTrigger, -1, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking); } + *process { |server, 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, freeWhenDone = true, action| - *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, blocking = 0| - ^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking); + source = source.asUGenInput; + transients = transients ? -1; + residual = residual ? -1; - } + source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; - *process { |server, 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, action| - ^FluidNRTProcess.new( - server, this, action,[transients, residual].select{|x| x!= -1} - ).process( - source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength + ^this.new( + server, nil,[transients, residual].select{|x| x!= -1} + ).processList( + [source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,0],freeWhenDone,action ); } - *processBlocking { |server, 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, action| - ^FluidNRTProcess.new( - server, this, action,[transients, residual].select{|x| x!= -1}, blocking: 1 - ).process( - source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength - ); - } + *processBlocking { |server, 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, freeWhenDone = true, action| + + source = source.asUGenInput; + transients = transients ? -1; + residual = residual ? -1; + + source.isNil.if {"FluidBufTransients: Invalid source buffer".throw}; + + ^this.new( + server, nil,[transients, residual].select{|x| x!= -1} + ).processList( + [source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,1],freeWhenDone = true,action + ); + } } diff --git a/release-packaging/Classes/FluidCorpusBuilders.sc b/release-packaging/Classes/FluidCorpusBuilders.sc index b8cea79..efb81de 100644 --- a/release-packaging/Classes/FluidCorpusBuilders.sc +++ b/release-packaging/Classes/FluidCorpusBuilders.sc @@ -34,6 +34,7 @@ FluidLoadFolder { entry.add(\bounds->startEnd[i]); entry.add(\numchans->f.numChannels); entry.add(\sr->f.sampleRate); + entry.add(\path->f.path); index.add(label->entry); counter = counter + 1; if(counter == (files.size)) {action !? action.value(index)}; diff --git a/release-packaging/Classes/FluidDataSet.sc b/release-packaging/Classes/FluidDataSet.sc index 6d1dea1..9df17cc 100644 --- a/release-packaging/Classes/FluidDataSet.sc +++ b/release-packaging/Classes/FluidDataSet.sc @@ -1,91 +1,65 @@ -FluidDataSetExistsError : Exception{ -} - -FluidDataSet : FluidManipulationClient { +FluidDataSet : FluidDataObject +{ + *new{|server| ^super.new(server) } - var numNeighbours, <>weight; *new {|server, numNeighbours = 3, weight = 1| - ^super.new1(server,[\numNeighbours,numNeighbours,\weight,weight]); + ^super.new(server,[numNeighbours,weight]) + .numNeighbours_(numNeighbours) + .weight_(weight); } + prGetParams{^[this.numNeighbours,this.weight,-1,-1];} + + fitMsg{|dataSet, labelSet| + ^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id) + } + fit{|dataSet, labelSet, action| - this.prSendMsg(\fit,[dataSet.asSymbol, labelSet.asSymbol], action); + actions[\fit] = [nil,action]; + this.prSendMsg(this.fitMsg(dataSet, labelSet)); } - predict{|dataSet, labelSet, action| - this.prSendMsg(\predict, - [dataSet.asSymbol, labelSet.asSymbol], - action); + predictMsg{|dataSet, labelSet| + ^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id) + } + + predict{|dataSet, labelSet, action| + actions[\predict] = [nil, action]; + this.prSendMsg(this.predictMsg(dataSet, labelSet)); } + predictPointMsg{|buffer| + ^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer)) + } + predictPoint {|buffer, action| - buffer = this.prEncodeBuffer(buffer); - this.prSendMsg(\predictPoint, - [buffer.asUGenInput], action, - [string(FluidMessageResponse,_,_)] - ); + actions[\predictPoint] = [string(FluidMessageResponse,_,_),action]; + this.prSendMsg(this.predictPointMsg(buffer)); } + + kr{|trig, inputBuffer,outputBuffer| + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, this.numNeighbours, this.weight, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); + } + } diff --git a/release-packaging/Classes/FluidKNNRegressor.sc b/release-packaging/Classes/FluidKNNRegressor.sc index 1209a27..a6a7cad 100644 --- a/release-packaging/Classes/FluidKNNRegressor.sc +++ b/release-packaging/Classes/FluidKNNRegressor.sc @@ -1,25 +1,46 @@ -FluidKNNRegressor : FluidRTDataClient { +FluidKNNRegressor : FluidRealTimeModel { + + var <>numNeighbours, <>weight; *new {|server, numNeighbours = 3, weight = 1| - ^super.new1(server,[\numNeighbours,numNeighbours,\weight,weight]); + ^super.new(server,[numNeighbours,weight]) + .numNeighbours_(numNeighbours) + .weight_(weight); } + prGetParams{^[this.numNeighbours,this.weight,-1,-1];} + + fitMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id) + } + fit{|sourceDataSet, targetDataSet, action| - this.prSendMsg(\fit, - [sourceDataSet.asSymbol, targetDataSet.asSymbol], - action - ); + actions[\fit] = [nil,action]; + this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet)); } + predictMsg{ |sourceDataSet, targetDataSet| + ^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id) + } + predict{ |sourceDataSet, targetDataSet,action| - this.prSendMsg(\predict, - [sourceDataSet.asSymbol, targetDataSet.asSymbol], - action); + actions[\predict] = [nil, action]; + this.prSendMsg(this.predictMsg(sourceDataSet, targetDataSet)); } + predictPointMsg { |buffer| + ^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer)); + } + predictPoint { |buffer, action| - buffer = this.prEncodeBuffer(buffer); - this.prSendMsg(\predictPoint, [buffer], action, - [number(FluidMessageResponse,_,_)]); + actions[\predictPoint] = [number(FluidMessageResponse,_,_),action]; + this.prSendMsg(this.predictPointMsg(buffer)); } + + kr{|trig, inputBuffer,outputBuffer| + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, this.numNeighbours, this.weight, + this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)); + } } diff --git a/release-packaging/Classes/FluidLabelSet.sc b/release-packaging/Classes/FluidLabelSet.sc index cd715fa..7f0d9c3 100644 --- a/release-packaging/Classes/FluidLabelSet.sc +++ b/release-packaging/Classes/FluidLabelSet.sc @@ -1,81 +1,54 @@ -FluidLabelSetExistsError : Exception{ -} +FluidLabelSet : FluidDataObject { -FluidLabelSet : FluidManipulationClient { + *new{|server| ^super.new(server) } - var numDimensions, <>distanceMetric; + *new {|server,numDimensions = 2, distanceMetric = 1| - ^super.new1(server,[ - \numDimensions,numDimensions, - \distanceMetric, distanceMetric - ]) + ^super.new(server,[numDimensions, distanceMetric]) + .numDimensions_(numDimensions) + .distanceMetric_(distanceMetric); } + prGetParams{ + ^[this.numDimensions, this.distanceMetric]; + } + + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); + } fitTransform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\fitTransform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action); + actions[\fitTransform] = [nil,action]; + this.fitTransformMsg(sourceDataSet,destDataSet).postln; + + this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); } // not implemented diff --git a/release-packaging/Classes/FluidMFCC.sc b/release-packaging/Classes/FluidMFCC.sc index 76d37b6..326c7e6 100644 --- a/release-packaging/Classes/FluidMFCC.sc +++ b/release-packaging/Classes/FluidMFCC.sc @@ -4,6 +4,7 @@ FluidMFCC : MultiOutUGen { ^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, numBands, minFreq, maxFreq, maxNumCoeffs, windowSize, hopSize, fftSize, maxFFTSize); } + init {arg ...theInputs; inputs = theInputs; ^this.initOutputs(inputs.at(5),rate); diff --git a/release-packaging/Classes/FluidMLP.sc b/release-packaging/Classes/FluidMLP.sc index 1d1f42e..d459d1f 100644 --- a/release-packaging/Classes/FluidMLP.sc +++ b/release-packaging/Classes/FluidMLP.sc @@ -1,103 +1,151 @@ -FluidMLPRegressor : FluidRTDataClient { +FluidMLPRegressor : FluidRealTimeModel { const hidden, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation; + *new {|server, hidden = #[3,3] , activation = 2, outputActivation = 0, tapIn = 0, tapOut = -1,maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2| - var hiddenCtrlLabels; - hidden = [hidden.size]++hidden; - - hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i}; - - ^super.new1(server, - [hiddenCtrlLabels,hidden].lace ++ - [ - \activation,activation, - \outputActivation, outputActivation, - \tapIn, tapIn, - \tapOut, tapOut, - \maxIter, maxIter, - \learnRate,learnRate, - \momentum, momentum, - \batchSize,batchSize, - \validation,validation, - ]) + + ^super.new(server, [hidden.size] ++ hidden ++ [activation, outputActivation, tapIn, tapOut, maxIter, learnRate, momentum, batchSize, validation]) + .hidden_(hidden) + .activation_(activation) + .outputActivation_(outputActivation) + .tapIn_(tapIn) + .tapOut_(tapOut) + .maxIter_(maxIter) + .learnRate_(learnRate) + .momentum_(momentum) + .batchSize_(batchSize) + .validation_(validation); } - clear{ |action| - this.prSendMsg(\clear,action:action); + prGetParams{ + ^[this.hidden.size] ++ this.hidden ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation, -1, -1] + } + + clearMsg{ ^this.prMakeMsg(\clear, id) } + + clear{ |action| + actions[\clear] = [nil, action]; + this.prSendMsg(this.clearMsg); } + fitMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id); + } + fit{|sourceDataSet, targetDataSet, action| - this.prSendMsg(\fit, - [sourceDataSet.asSymbol, targetDataSet.asSymbol], - action,numbers(FluidMessageResponse,_,1,_) - ); + actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet)); } - predict{ |sourceDataSet, targetDataSet, action| - this.prSendMsg(\predict, - [sourceDataSet.asSymbol, targetDataSet.asSymbol], - action); + predictMsg{|sourceDataSet, targetDataSet| + ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id); + } + + predict{|sourceDataSet, targetDataSet, action| + actions[\predict] = [nil,action]; + this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet)); } + + predictPointMsg { |sourceBuffer, targetBuffer| + ^this.prMakeMsg(\predictPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(targetBuffer), + ["/b_query", targetBuffer.asUGenInput]); + } + predictPoint { |sourceBuffer, targetBuffer, action| - sourceBuffer = this.prEncodeBuffer(sourceBuffer); - targetBuffer = this.prEncodeBuffer(targetBuffer); - this.prSendMsg(\predictPoint, - [sourceBuffer.asUGenInput, targetBuffer.asUGenInput], action,outputBuffers:[targetBuffer]); + actions[\predictPoint] = [nil,{action.value(targetBuffer)}]; + this.predictPointMsg(sourceBuffer, targetBuffer); + this.prSendMsg(this.predictPointMsg(sourceBuffer, targetBuffer)); } + + kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1| + var params; + tapIn = tapIn ? this.tapIn; + tapOut = tapOut ? this.tapOut; + + this.tapIn_(tapIn).tapOut_(tapOut); + + params = this.prGetParams.drop(-2) ++ [this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)]; + + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, *params); + } + } -FluidMLPClassifier : FluidRTDataClient { +FluidMLPClassifier : FluidRealTimeModel { const hidden, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation; + *new {|server, hidden = #[3,3] , activation = 2, maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2| - var hiddenCtrlLabels; - hidden = [hidden.size]++hidden; - - hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i}; - - ^super.new1(server, - [hiddenCtrlLabels,hidden].lace ++ - [ - \activation,activation, - \maxIter, maxIter, - \learnRate,learnRate, - \momentum, momentum, - \batchSize,batchSize, - \validation,validation, - ]) + ^super.new(server,[hidden.size] ++ hidden ++ [activation, maxIter, learnRate, momentum, batchSize, validation]) + .hidden_(hidden) + .activation_(activation) + .maxIter_(maxIter) + .learnRate_(learnRate) + .momentum_(momentum) + .batchSize_(batchSize) + .validation_(validation); } + prGetParams{ + ^[ this.hidden.size] ++ this.hidden ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation, -1, -1]; + } + + clearMsg{ ^this.prMakeMsg(\clear,id) } + clear{ |action| - this.prSendMsg(\clear,action:action); + actions[\clear] = [nil,action]; + this.prSendMsg(this.clearMsg); } + fitMsg{|sourceDataSet, targetLabelSet| + ^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id); + } fit{|sourceDataSet, targetLabelSet, action| - this.prSendMsg(\fit, - [sourceDataSet.asSymbol, targetLabelSet.asSymbol], - action,numbers(FluidMessageResponse,_,1,_) - ); + actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet)); } + predictMsg{|sourceDataSet, targetLabelSet| + ^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id); + } + predict{ |sourceDataSet, targetLabelSet, action| - this.prSendMsg(\predict, - [sourceDataSet.asSymbol, targetLabelSet.asSymbol], - action); + actions[\predict]=[nil,action]; + this.prSendMsg(this.predictMsg(sourceDataSet,targetLabelSet)); } + predictPointMsg { |sourceBuffer| + ^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer)) + } + predictPoint { |sourceBuffer, action| - sourceBuffer = this.prEncodeBuffer(sourceBuffer); - this.prSendMsg(\predictPoint, - [sourceBuffer], action, string(FluidMessageResponse,_,_)); + actions[\predictPoint] = [string(FluidMessageResponse,_,_),action]; + this.prSendMsg(this.predictPointMsg(sourceBuffer)); } + + kr{|trig, inputBuffer,outputBuffer| + + var params = this.prGetParams.drop(-2) ++ [this.prEncodeBuffer(inputBuffer), + this.prEncodeBuffer(outputBuffer)]; + + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, *params); + } } diff --git a/release-packaging/Classes/FluidManipulationClient.sc b/release-packaging/Classes/FluidManipulationClient.sc deleted file mode 100644 index c39bebb..0000000 --- a/release-packaging/Classes/FluidManipulationClient.sc +++ /dev/null @@ -1,298 +0,0 @@ -FluidProxyUgen : UGen { - - var <>pluginname; - - *kr { |pluginname...args| - ^this.new1('control', pluginname,*args) - } - - init { |pluginname...args| - this.pluginname = pluginname; - inputs = args; - pluginname - .asSymbol - .asClass - .superclasses - .indexOf(FluidRTDataClient) ??{inputs= inputs ++ [Done.none,0]}; - rate = 'control'; - } - - name{ - ^pluginname.asString; - } -} - -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) - } - - *newFromDesc { arg rate, numOutputs, inputs, specialIndex; - ^FluidProxyUgen.newFromDesc(rate, numOutputs, inputs, specialIndex) - } - - *new{ |server,objectID...args| - server = server ? Server.default; - if(server.serverRunning.not,{ - (this.asString + "– server not running").error; ^nil - }); - ^super.newCopyArgs(server ?? {Server.default});//.baseinit(objectID,*args) - } - - makeDef { |defName,objectID,args| - var initialVals = []; - args!? { if(args.size > 0) {initialVals = args.unlace(2)[1].flatten}}; - ^SynthDef(defName,{ - var ugen = FluidProxyUgen.kr(this.class.name, *(initialVals ++ objectID)); - this.ugen = ugen; - ugen - }); - } - - updateSynthControls {} - - baseinit { |objectID...args| - var makeFirstSynth,synthMsg,synthArgs; - id = UniqueID.next; - postit = {|x| x.postln;}; - keepAlive = true; - defName = (this.class.name.asString ++ id).asSymbol; - def = this.makeDef(defName,objectID,args); - synth = Synth.basicNew(def.name, server); - synthMsg = synth.newMsg(RootNode(server),args); - def.doSend(server,synthMsg); - - onSynthFree = { - synth = nil; - 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)}; - } - - free{ - keepAlive = false; - if(server.serverRunning){server.sendMsg("/cmd","free"++this.class.name,id)}; - synth.tryPerform(\free); - ^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,_)]); - } - - prEncodeBuffer { |buf| buf !? {^buf.asUGenInput} ?? {^-1} } - - prSendMsg { |msg, args, action,parser,outputBuffers| - if(this.server.serverRunning.not,{(this.asString + "– server not running").error; ^nil}); - forkIfNeeded{ - synth ?? {onSynthFree.value; server.sync}; - OSCFunc( - { |msg| - defer{ - var result; - result = FluidMessageResponse.collectArgs(parser,msg.drop(3)); - if(outputBuffers.notNil) { - forkIfNeeded { - outputBuffers.collectInPlace{ |b| - server.cachedBufferAt(b) !? {|x| x.updateInfo} - }; - server.sync; - if(action.notNil){action.value(*(outputBuffers++result))}{action.value}; - }; - } - {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)); - } - } -} - -FluidDataClient : FluidManipulationClient { - - var synthControls; - - var 0 and: synthControls.isNil) {synthControls = params.unlace[0]}; - parameters = ().putPairs(params); - parameterDefaults = parameters.copy; - this.makePropertyMethods; - this.baseinit(uid,*params); - } - - makePropertyMethods{ - if (parameters.keys.size > 0) { - parameters.keys.do{|c,i| - this.addUniqueMethod(c,{ parameters.at(c) }); - this.addUniqueMethod((c++\_).asSymbol,{|responder,x| - //if we have a default or initial value set, then fall back to - //this if val is nil. Otherwise, fallback even furter to -1 as - // a best guess - x = x ?? { parameterDefaults !? { parameterDefaults[c] } ?? {-1} }; - parameters.put(c, x.asUGenInput); - synth !? { if(synth.isRunning){ synth.set(c,x); } }; - responder - }); - } - }; - } - - - makeDef {|defName,uid,args| - var defControls = synthControls; - var ugenControls = [this.class.name] ++ synthControls ++ uid; - - var f = ( - "{ |dataClient|" - " SynthDef("++defName.asCompileString++", { |" ++ defControls.join(",") ++ "|" - " var ugen = FluidProxyUgen.kr(" ++ ugenControls.join(",") ++ ");" - " dataClient.ugen = ugen;" - " ugen" - " })" - "}" - ); - var res = f.interpret.value(this); - ^res - } - - updateSynthControls{ - synth !? { synth.set(*parameters.asKeyValuePairs); }; - } -} - -FluidRTDataClient : FluidDataClient -{ - - - *new1{|server, params| - params = params ?? {[]}; - ^super.new1(server,params) - } - - - init { |uid, params| - id = uid; - params = params ?? {[]}; - if(params.size > 0) {synthControls = params.unlace[0]}{synthControls=[]}; - params = params ++ [\inBus,Bus.control(server),\outBus,Bus.control(server),\inBuffer,-1,\outBuffer,-1]; - parameters = ().putPairs(params); - parameterDefaults = parameters.copy; - this.makePropertyMethods; - this.baseinit(uid,*params); - } - - makeDef {|defName,uid,args| - var defControls = [\inBus, \outBus] ++ synthControls ++ [\inBuffer,\outBuffer]; - var ugenControls = [this.class.name,"T2A.ar(In.kr(inBus))"] ++ synthControls ++ [\inBuffer,\outBuffer,uid]; - var f = ( - "{ |dataClient|" - " SynthDef("++defName.asCompileString++", { |" ++ defControls.join(",") ++ "|" - " var ugen = FluidProxyUgen.kr(" ++ ugenControls.join(",") ++ ");" - " dataClient.ugen = ugen;" - " Out.kr(outBus,ugen);" - " })" - "}" - ); - var res = f.interpret.value(this); - - ^res - } -} - - -FluidServerCache { - - var cache; - - *new{ ^super.new.init } - - init{ - cache = IdentityDictionary.new; - } - - do { |server, func| - cache[server]!?{cache[server].do{|x|func.value(x)}} - } - - 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; - NotificationCenter.register(server,\newAllocators,this, - { - this.clearCache(server); - }); - } - } - - clearCache { |server| - cache[server] !? { cache.removeAt(server) }; - } - -} diff --git a/release-packaging/Classes/FluidManipulationJSON.sc b/release-packaging/Classes/FluidManipulationJSON.sc index 68f3aa0..875902b 100644 --- a/release-packaging/Classes/FluidManipulationJSON.sc +++ b/release-packaging/Classes/FluidManipulationJSON.sc @@ -1,4 +1,4 @@ -+ FluidManipulationClient { ++ FluidDataObject { tmpJSONFilename{ ^Platform.defaultTempDir++"tmp_fluid_data_"++ Date.localtime.stamp++"_"++UniqueID.next++".json"; @@ -6,8 +6,8 @@ dump {|action| var filename = this.tmpJSONFilename; - action ?? {action = postit}; - this.write(filename, { + action ?? {action = postResponse}; + this.write(filename, { action.value(this.parseJSON(File.readAllString(filename))); File.delete(filename); }); diff --git a/release-packaging/Classes/FluidMessageResponse.sc b/release-packaging/Classes/FluidMessageResponse.sc index 7ea2317..0ebba79 100644 --- a/release-packaging/Classes/FluidMessageResponse.sc +++ b/release-packaging/Classes/FluidMessageResponse.sc @@ -17,15 +17,12 @@ FluidMessageResponse : Object } *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.asInteger.asAscii},String)], split + 1] + ^[a] } *strings {|a,offset| //TODO add an n argument as with numbers() to make this less omnivorous - ^[a.drop(offset).drop(-1).collectAs({|x|x.asInteger.asAscii},String).split(0.asAscii)] + ^[a.drop(offset)]; } *numbers{ |a, n, offset| @@ -41,4 +38,4 @@ FluidMessageResponse : Object server = server ? Server.default ; ^[Buffer.cachedBufferAt(server, a[offset]), offset + 1] } -} \ No newline at end of file +} diff --git a/release-packaging/Classes/FluidNormalize.sc b/release-packaging/Classes/FluidNormalize.sc index 8881930..17d5c5a 100644 --- a/release-packaging/Classes/FluidNormalize.sc +++ b/release-packaging/Classes/FluidNormalize.sc @@ -1,31 +1,68 @@ -FluidNormalize : FluidRTDataClient { +FluidNormalize : FluidRealTimeModel { + + var <>min, <>max, <>invert; *new {|server, min = 0, max = 1, invert = 0| - ^super.new1(server,[\min,min,\max,max, \invert, invert]); + ^super.new(server,[min,max,invert]) + .min_(min).max_(max).invert_(invert); } + prGetParams{ + ^[this.min,this.max,this.invert,-1,-1]; + } + + + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id,dataSet.id) + } + fit{|dataSet, action| - this.prSendMsg(\fit,[dataSet.asSymbol], action); + actions[\fit] = [nil,action]; + this.prSendMsg(this.fitMsg(dataSet)); } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); + } + transform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\transform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action - ); + actions[\transform] = [nil,action]; + this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet)); } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) + } + fitTransform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\fitTransform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action - ); + actions[\fitTransform] = [nil,action]; + this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet)); } + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); + } + transformPoint{|sourceBuffer, destBuffer, action| - sourceBuffer = this.prEncodeBuffer(sourceBuffer); - destBuffer = this.prEncodeBuffer(destBuffer); - this.prSendMsg(\transformPoint, - [sourceBuffer, destBuffer], action, - outputBuffers:[destBuffer] - ); + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer)); } + + kr{|trig, inputBuffer,outputBuffer,min,max,invert| + + min = min ? this.min; + max = max ? this.max; + invert = invert ? this.invert; + + this.min_(min).max_(max).invert_(invert); + + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, this.min, this.max, this.invert, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer)); + } + + } diff --git a/release-packaging/Classes/FluidPCA.sc b/release-packaging/Classes/FluidPCA.sc index 899d6e0..94c7df3 100644 --- a/release-packaging/Classes/FluidPCA.sc +++ b/release-packaging/Classes/FluidPCA.sc @@ -1,24 +1,62 @@ -FluidPCA : FluidRTDataClient { +FluidPCA : FluidRealTimeModel{ - *new {|server, numDimensions = 2| - ^super.new1(server,[\numDimensions,numDimensions]); - } + var <>numDimensions; + + *new {|server, numDimensions = 2| + ^super.new(server,[numDimensions]).numDimensions_(numDimensions); + } + + prGetParams{ + ^[numDimensions,-1,-1]; + } + + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id, dataSet.id); + } fit{|dataSet, action| - this.prSendMsg(\fit,[dataSet.asSymbol], action); + actions[\fit] = [nil, action]; + this.prSendMsg(this.fitMsg(dataSet)); + } + + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id); } transform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\transform,[sourceDataSet.asSymbol, destDataSet.asSymbol], action, [numbers(FluidMessageResponse,_,1,_)]); + actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); + } + + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id); } fitTransform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\fitTransform,[sourceDataSet.asSymbol, destDataSet.asSymbol], action, [numbers(FluidMessageResponse,_,1,_)]); + actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action]; + this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); + } + + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint,id, + this.prEncodeBuffer(sourceBuffer), + this.prEncodeBuffer(destBuffer), + ["/b_query",destBuffer.asUGenInput] + ); } transformPoint{|sourceBuffer, destBuffer, action| - sourceBuffer = this.prEncodeBuffer(sourceBuffer); - destBuffer = this.prEncodeBuffer(destBuffer); - this.prSendMsg(\transformPoint,[sourceBuffer, destBuffer], action, outputBuffers:[destBuffer]); + actions[\transformPoint] = [nil,{action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); + } + + kr{|trig, inputBuffer,outputBuffer,numDimensions| + + numDimensions = numDimensions ? this.numDimensions; + this.numDimensions_(numDimensions); + + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, this.numDimensions, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer)); } + } diff --git a/release-packaging/Classes/FluidServerCache.sc b/release-packaging/Classes/FluidServerCache.sc new file mode 100644 index 0000000..bd383a5 --- /dev/null +++ b/release-packaging/Classes/FluidServerCache.sc @@ -0,0 +1,92 @@ +FluidProxyUgen : UGen { + + var <>pluginname; + + *kr { |pluginname...args| + ^this.new1('control', pluginname,*args) + } + + init { |pluginname...args| + this.pluginname = pluginname; + inputs = args; + rate = 'control'; + } + + name{ + ^pluginname.asString; + } + + poll{ |trig = 10, label, trigid = -1| + ^super.poll(trig, label ? this.name, trigid) + } +} + +FluidServerCache { + + var invert; + + *new {|server, invert = 0| + ^super.new(server,[invert]).invert_(invert); } - fit{|dataSet, action| - this.prSendMsg(\fit, [dataSet.asSymbol], action); + prGetParams{ + ^[this.invert, -1, 1]; + } + + fitMsg{|dataSet| + ^this.prMakeMsg(\fit,id,dataSet.id); + } + + fit{|dataSet, action| + actions[\fit] = [nil, action]; + this.prSendMsg(this.fitMsg(dataSet)); } + transformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id); + } + transform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\transform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action - ); + actions[\transform] = [nil,action]; + this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet)); } + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id) + } + fitTransform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\fitTransform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action - ); + actions[\fitTransform] = [nil,action]; + this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet)); } + + transformPointMsg{|sourceBuffer, destBuffer| + ^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]); + } + transformPoint{|sourceBuffer, destBuffer, action| - sourceBuffer = this.prEncodeBuffer(sourceBuffer); - destBuffer = this.prEncodeBuffer(destBuffer); - this.prSendMsg(\transformPoint, - [sourceBuffer, destBuffer], action, outputBuffers:[destBuffer] - ); + actions[\transformPoint] = [nil, {action.value(destBuffer)}]; + this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer)); } + + kr{|trig, inputBuffer,outputBuffer,invert| + + invert = invert ? this.invert; + this.invert_(invert); + + ^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig), + id, this.invert, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer)); + } } diff --git a/release-packaging/Classes/FluidUMAP.sc b/release-packaging/Classes/FluidUMAP.sc index 524b165..c6c7cd7 100644 --- a/release-packaging/Classes/FluidUMAP.sc +++ b/release-packaging/Classes/FluidUMAP.sc @@ -1,29 +1,35 @@ -FluidUMAP : FluidRTDataClient { +FluidUMAP : FluidModelObject { - *new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1, batchSize = 50| - ^super.new1(server,[ - \numDimensions,numDimensions, - \numNeighbours, numNeighbours, - \minDist, minDist, - \iterations, iterations, - \learnRate, learnRate - ]) - } + var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate, <>batchSize; - fit{|sourceDataSet, action| - this.prSendMsg(\fit, - [sourceDataSet.asSymbol], action); - } - - transform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\transform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action); + *new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1, batchSize = 50| + ^super.new(server,[numDimensions, numNeighbours, minDist, iterations, learnRate, batchSize]) + .numDimensions_(numDimensions) + .numNeighbours_(numNeighbours) + .minDist_(minDist) + .iterations_(iterations) + .learnRate_(learnRate) + .batchSize_(batchSize); } + prGetParams{ + ^[ + this.numDimensions, + this.numNeighbours, + this.minDist, + this.iterations, + this.learnRate, + this.batchSize + ] + } + + fitTransformMsg{|sourceDataSet, destDataSet| + ^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id) + } fitTransform{|sourceDataSet, destDataSet, action| - this.prSendMsg(\fitTransform, - [sourceDataSet.asSymbol, destDataSet.asSymbol], action); + actions[\fitTransform] = [nil, action]; + this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet)); } transformPoint{|sourceBuffer, destBuffer, action| diff --git a/release-packaging/Examples/buffer_compositing/bufcompose-MS-FIR.scd b/release-packaging/Examples/buffer_compositing/bufcompose-MS-FIR.scd index b13b37a..d72ecc9 100644 --- a/release-packaging/Examples/buffer_compositing/bufcompose-MS-FIR.scd +++ b/release-packaging/Examples/buffer_compositing/bufcompose-MS-FIR.scd @@ -19,7 +19,11 @@ FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1, // (optional) compare auraly the stereo with the MS b.play; -{PlayBuf.ar(1,[c,d])}.play; +{PlayBuf.ar(1, [c,d])}.play; + +//check nullsum with real-time versions +{PlayBuf.ar(1, c) - PlayBuf.ar(2, b).madd(-3.0.dbamp).sum}.play; +{PlayBuf.ar(1, d) - PlayBuf.ar(2, b).madd(-3.0.dbamp * [1,-1]).sum}.play; // The geeky bit: copy the side (buffer d) on itself with specific amplitudes and delays, in effect applying a FIR filter through expensive convolution diff --git a/release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd b/release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd index e1d05ef..502518c 100644 --- a/release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd +++ b/release-packaging/Examples/buffer_compositing/fileiterator-2passes.scd @@ -40,6 +40,6 @@ c.postln b.play -{PlayBuf.ar(1,b.bufnum,startPos:c[740])}.play +{PlayBuf.ar(1,b.bufnum,startPos:c[15])}.play Buffer.freeAll \ No newline at end of file diff --git a/release-packaging/Examples/buffer_compositing/fileiterator.scd b/release-packaging/Examples/buffer_compositing/fileiterator.scd index 9808f72..7e0f32a 100644 --- a/release-packaging/Examples/buffer_compositing/fileiterator.scd +++ b/release-packaging/Examples/buffer_compositing/fileiterator.scd @@ -37,4 +37,4 @@ b.plot c.postln b.play -{PlayBuf.ar(1,b.bufnum,startPos:c[740])}.play +{PlayBuf.ar(1,b.bufnum,startPos:c[15])}.play diff --git a/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd b/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd index 90fc66c..9ceb017 100644 --- a/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd +++ b/release-packaging/Examples/dataset/0-demo-dataset-maker-utilities.scd @@ -1,6 +1,6 @@ // 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+. +~ds = FluidDataSet(s);//no name needs to be provided //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}; @@ -8,11 +8,11 @@ // 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/sons/smallnum/"); -~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/sons/segments/"); +~loader = FluidLoadFolder(File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles"); -// 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 +// 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. ~slicer = FluidSliceCorpus({ |src,start,num,dest| - FluidBufOnsetSlice.kr(src,start,num,metric: 9, minSliceLength: 17, indices:dest, threshold:2,blocking: 1) + FluidBufOnsetSlice.kr(src, start, num, metric: 9, minSliceLength: 17, indices:dest, threshold:0.7, blocking: 1) }); // 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. @@ -20,10 +20,10 @@ 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,blocking: 1); - stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc),blocking: 1); - flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1); - writer = FluidDataSetWr.kr(~ds,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1) + mfcc = FluidBufMFCC.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], trig:1, blocking: 1); + stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], trig:Done.kr(mfcc), blocking: 1); + flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1); + writer = FluidDataSetWr.kr(~ds, label, -1, ~flatbuf[voice], trig: Done.kr(flatten), blocking: 1) }); ) @@ -134,7 +134,7 @@ t = Main.elapsedTime; //retrieve a sound to match ~targetsound = Buffer(s); -~targetname = ~slicer.index.keys.asArray.last.asSymbol; +~targetname = ~slicer.index.keys.asArray.scramble[0].asSymbol; #a,b = ~slicer.index[~targetname][\bounds]; FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targetsound,action: {~targetsound.play;}) @@ -145,6 +145,7 @@ FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targ 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)); + FreeSelfWhenDone.kr(flatten); }.play; ) diff --git a/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd b/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd index 91b940f..8ed2aa0 100644 --- a/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd +++ b/release-packaging/Examples/dataset/1-learning examples/10a-weighted-MFCCs-comparison.scd @@ -1,8 +1,8 @@ // define a few processes ( -~ds = FluidDataSet(s,\ds10); -~dsW = FluidDataSet(s,\ds10W); -~dsL = FluidDataSet(s,\ds10L); +~ds = FluidDataSet(s); +~dsW = FluidDataSet(s); +~dsL = FluidDataSet(s); //define as many buffers as we have parallel voices/threads in the extractor processing (default is 4) ~loudbuf = 4.collect{Buffer.new}; ~weightbuf = 4.collect{Buffer.new}; @@ -94,10 +94,12 @@ t = Main.elapsedTime; // extracting whatever stats we want. In this case, mean/std/lowest/highest, and the same on the first derivative - excluding MFCC0 as it is mostly volume, keeping MFCC1-12 -~curated = FluidDataSet(s,\ds10c); -~curatedW = FluidDataSet(s,\ds10cW); - +( +~curated = FluidDataSet(s); +~curatedW = FluidDataSet(s); ~curator = FluidDataSetQuery.new(s); +) + ( ~curator.addRange(1,12,{ ~curator.addRange(14,12,{ diff --git a/release-packaging/Examples/dataset/1-learning examples/10b-weighted-pitch-comparison.scd b/release-packaging/Examples/dataset/1-learning examples/10b-weighted-pitch-comparison.scd index 72b8e0c..a063065 100644 --- a/release-packaging/Examples/dataset/1-learning examples/10b-weighted-pitch-comparison.scd +++ b/release-packaging/Examples/dataset/1-learning examples/10b-weighted-pitch-comparison.scd @@ -68,7 +68,6 @@ c.free //for completion, here is just with rejection of outliers - not as good, but a decent second best! FluidBufStats.process(s,~pitches, stats:~stats,outliersCutoff: 1.5) ~stats.getn(0,14,{|x|~pitchIQRStats = x;x.reshape(7,2).do{|y| "%\t\t\t%\n".postf(y[0].round(0.1),y[1].round(0.01))}}) -//now that is impressive! c = {SinOsc.ar(~pitchIQRStats[0],mul: 0.05)}.play b.play c.free diff --git a/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd b/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd index 687958f..9b55fcf 100644 --- a/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd +++ b/release-packaging/Examples/dataset/1-learning examples/11-compositing-datasets.scd @@ -2,10 +2,10 @@ // define a few datasets ( -~pitchDS = FluidDataSet(s,\pitch11); -~loudDS = FluidDataSet(s,\loud11); -~mfccDS = FluidDataSet(s,\mfcc11); -~durDS = FluidDataSet(s,\dur11); +~pitchDS = FluidDataSet(s); +~loudDS = FluidDataSet(s); +~mfccDS = FluidDataSet(s); +~durDS = FluidDataSet(s); //define as many buffers as we have parallel voices/threads in the extractor processing (default is 4) ~pitchbuf = 4.collect{Buffer.new}; @@ -102,7 +102,7 @@ t = Main.elapsedTime; /////////////////////////////////////////////////////// //reduce the MFCC timbral space stats (many potential ways to explore here... - 2 are provided to compare, with and without the derivatives before running a dimension reduction) -~tempDS = FluidDataSet(s,\temp11); +~tempDS = FluidDataSet(s); ~query = FluidDataSetQuery(s); ~query.addRange(0,24);//add only means and stddev of the 12 coeffs... @@ -115,12 +115,12 @@ t = Main.elapsedTime; // standardizing before the PCA, as argued here: // https://scikit-learn.org/stable/auto_examples/preprocessing/plot_scaling_importance.html ~stan = FluidStandardize(s); -~stanDS = FluidDataSet(s,\stan11); +~stanDS = FluidDataSet(s); ~stan.fitTransform(~tempDS,~stanDS) //shrinking A: using 2 stats on the values, and 2 stats on the redivative (12 x 2 x 2 = 48 dim) ~pca = FluidPCA(s,4);//shrink to 4 dimensions -~timbreDSd = FluidDataSet(s,\timbreDSd11); +~timbreDSd = FluidDataSet(s); ~pca.fitTransform(~stanDS,~timbreDSd,{|x|x.postln;})//accuracy //shrinking B: using only the 2 stats on the values @@ -133,7 +133,7 @@ t = Main.elapsedTime; //keep its own PCA so we can keep the various states for later transforms ~pca2 = FluidPCA(s,4);//shrink to 4 dimensions -~timbreDS = FluidDataSet(s,\timbreDS11); +~timbreDS = FluidDataSet(s); ~pca2.fitTransform(~tempDS,~timbreDS,{|x|x.postln;})//accuracy // comparing NN for fun @@ -181,7 +181,7 @@ Routine{ /////////////////////////////////////////////////////// // compositing queries - defining a target and analysing it -~globalDS = FluidDataSet(s,\global11); +~globalDS = FluidDataSet(s); // define a source ~targetsound = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav",42250,44100); @@ -267,9 +267,9 @@ Routine{ // make sure to define and describe the source above (lines 178 to 201) // let's make normalised versions of the 3 datasets, keeping the normalisers separate to query later -~loudDSn = FluidDataSet(s,\loud11n); -~pitchDSn = FluidDataSet(s,\pitch11n); -~timbreDSn = FluidDataSet(s,\timbre11n); +~loudDSn = FluidDataSet(s); +~pitchDSn = FluidDataSet(s); +~timbreDSn = FluidDataSet(s); ~normL = FluidNormalize(s) ~normP = FluidNormalize(s) @@ -290,13 +290,15 @@ Routine{ // let's assemble the query // first let's normalise our target descriptors -~targetPitch = Buffer(s) -~targetLoud = Buffer(s) -~targetMFCC = Buffer(s) -~targetMFCCs = Buffer(s) -~targetMFCCsp = Buffer(s) -~targetTimbre = Buffer(s) -~targetAll= Buffer(s) +( +~targetPitch = Buffer(s); +~targetLoud = Buffer(s); +~targetMFCC = Buffer(s); +~targetMFCCs = Buffer(s); +~targetMFCCsp = Buffer(s); +~targetTimbre = Buffer(s); +~targetAll= Buffer(s); +) ~normL.transformPoint(~flatLoudbuf[0], ~targetLoud) //normalise the loudness (all dims) ~normP.transformPoint(~flatPitchbuf[0], ~targetPitch) //normalise the pitch (all dims) diff --git a/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd b/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd index 4b88813..5167a5d 100644 --- a/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd +++ b/release-packaging/Examples/dataset/1-learning examples/12-windowed-clustered-segmentation.scd @@ -1,122 +1,228 @@ -// load a source -b = Buffer.read(s,"/Volumes/machins/projets/newsfeed/sons/textes/Audio/synth/fromtexttospeech-AmE-George.wav") -b.play +// load a source folder +~loader = FluidLoadFolder(File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/"); +~loader.play; //slightly oversegment with novelty //segments should still make sense but might cut a few elements in 2 or 3 -~originalslices = Buffer(s); -FluidBufNoveltySlice.process(s, b, indices: ~originalslices, feature: 1, kernelSize: 29, threshold: 0.05, filterSize: 5, hopSize: 128, action: {~originalslices.numFrames.postln;}) +~slicer = FluidSliceCorpus({ |src,start,num,dest| FluidBufNoveltySlice.kr(src,start,num,indices:dest, feature: 1, kernelSize: 29, threshold: 0.1, filterSize: 5, hopSize: 128, blocking: 1)}); +~slicer.play(s, ~loader.buffer,~loader.index); //test the segmentation by looping them ( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(1, ~originalslices, - MouseX.kr(0, BufFrames.kr(~originalslices) - 1), 0, 1), - BufRd.kr(1, ~originalslices, - MouseX.kr(1, BufFrames.kr(~originalslices)), 0, 1), - BufRd.kr(1,~originalslices, - MouseX.kr(0, BufFrames.kr(~originalslices) - 1), 0, 1)), 0, 1); +~originalindices = Array.newFrom(~slicer.index.keys).sort{|a,b| ~slicer.index[a][\bounds][0]< ~slicer.index[b][\bounds][0]}.collect{|x|~slicer.index[x][\bounds]}; +d = {arg start=0, end = 44100; + BufRd.ar(1, ~loader.buffer, Phasor.ar(0,1,start,end,start),0,1); }.play; + +w = Window.new(bounds:Rect(100,100,400,60)).front; +b = ControlSpec(0, ~originalindices.size - 1, \linear, 1); // min, max, mapping, step +c = StaticText(w, Rect(340, 20, 50, 20)).align_(\center); +a = Slider(w, Rect(10, 20, 330, 20)) +.action_({var val = b.map(a.value).asInteger; + c.string_(val.asString); + d.set(\start,~originalindices[val][0], \end, ~originalindices[val][1]); +}); ) -//analyse each segment with MFCCs in a dataset -~originalslices.getn(0,~originalslices.numFrames, {|x|~originalslicesarray = x; if ((x.last != b.numFrames), {~originalslicesarray = ~originalslicesarray ++ (b.numFrames)}); });//retrieve the indices and add the file boundary at the end if not there already +//analyse each segment with 20 MFCCs in a dataset and spectralshapes in another one +( +~featuresbuf = 4.collect{Buffer.new}; +~statsbuf = 4.collect{Buffer.new}; +~flatbuf = 4.collect{Buffer.new}; +~slicesMFCC = FluidDataSet(s); +~slicesShapes = FluidDataSet(s); +~extractor = FluidProcessSlices({|src,start,num,data| + var features, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice; + label = data.key; + voice = data.value[\voice]; + features = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1, numCoeffs: 20, features:~featuresbuf[voice],trig:1,blocking: 1); + stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1); + flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1); + writer = FluidDataSetWr.kr(~slicesMFCC,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1); + features = FluidBufSpectralShape.kr(src,startFrame:start,numFrames:num,numChans:1, features:~featuresbuf[voice],trig:Done.kr(writer),blocking: 1); + stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1); + flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1); + writer = FluidDataSetWr.kr(~slicesShapes,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1); +}); +) -//iterates through the -//a few buffers and our dataset - with back and forth from the language ( -~mfccs = Buffer(s); -~stats = Buffer(s); -~flat = Buffer(s); -~slices = FluidDataSet(s,\slices); - -Routine{ - s.sync; - (~originalslicesarray.size - 1).do{|i| - FluidBufMFCC.process(s, b, startFrame: ~originalslicesarray[i], numFrames: (~originalslicesarray[i+1] - ~originalslicesarray[i]), numChans: 1,features: ~mfccs, numCoeffs: 20, action: { - FluidBufStats.process(s, ~mfccs, startChan: 1, stats: ~stats, action: { - FluidBufFlatten.process(s, ~stats, ~flat, action: { - ~slices.addPoint(i.asSymbol, ~flat); - }); - }); - }); - }; -}.play; +t = Main.elapsedTime; +~extractor.play(s,~loader.buffer, ~slicer.index, action:{(Main.elapsedTime - t).postln;"Analysis done".postln}); ) -~slices.print +~slicesMFCC.print +~slicesShapes.print //run a window over consecutive segments, forcing them in 2 classes, and merging the consecutive segments of similar class //we overlap the analysis with the last (original) slice to check for continuity ( ~winSize = 4;//the number of consecutive items to split in 2 classes; +~curated = FluidDataSet(s); ~query = FluidDataSetQuery(s); -~kmeans = FluidKMeans(s,2,100); -~windowDS = FluidDataSet(s,\windowDS); -~windowLS = FluidLabelSet(s,\windowLS); +~stan = FluidStandardize(s); +~kmeans = FluidKMeans(s,2,1000); +~windowDS = FluidDataSet(s); +~windowLS = FluidLabelSet(s); ) -( -Routine{ - ~indices = [0]; - ~head = 0; - - ~sliceDict = Dictionary.new(4); - ~tempDict = Dictionary.new(4); - - ~slices.dump{|x|~sliceDict = x;}; - s.sync; - - while ( {~head <= (~originalslicesarray.size - ~winSize)}, - { - var step = ~winSize - 1; - var nbass = []; - //run a process on ~winSize items from ~head (with an overlap of 1) - //copy the items to a subdataset - ~winSize.do{|i| - ~tempDict.put((i.asString), ~sliceDict["data"][(i+~head).asString]);//here one could curate which stats to take - "whichslices:%\n".postf(i+~head); - }; - ~windowDS.load(Dictionary.newFrom([\cols, 133, \data, ~tempDict])); - s.sync; - "% - loaded\n".postf(~head); +//curate stats (MFCCs) +~query.clear +~query.addRange((0*20)+1,10); +~query.transform(~slicesMFCC,~curated); - //kmeans 2 and retrieve ordered array of class assignations - ~kmeans.fitPredict(~windowDS, ~windowLS, {|x| nbass = x;}); - s.sync; - "% - fitted1: ".postf(~head); nbass.postln; +//OR +//curate stats (moments) +~query.clear +~query.addRange(0,3); +~query.transform(~slicesShapes,~curated); - // if (nbass.includes(0.0), { Routine{~kmeans.fitPredict(~windowDS, ~windowLS, {|x| nbass = x; "% - fitted2: ".postf(~head); nbass.postln; s.sync;});}.play; }); +//OR +//curate both +~query.clear +~query.addColumn(0);//add col 0 (mean of mfcc0 as 'loudness') +~query.transform(~slicesMFCC,~curated);//mfcc0 as loudness +~query.clear; +~query.addRange(0,3);//add some spectral moments +~query.transformJoin(~slicesShapes, ~curated, ~curated);//join in centroids - ~windowLS.dump{|x|~assignments = x.at("data").asSortedArray.flop[1].flatten;}; - s.sync; - "% - assigned ".postf(~head); +//optionally standardize in place +~stan.fitTransform(~curated, ~curated); - ~assignments.postln; +~curated.print - step.do{|i| - if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])}); - }; +//retrieve the dataset as dictionary +~curated.dump{|x|~sliceDict = x;}; - ~head = ~head + step; - "-----------------".postln; - }); +~originalslicesarray = (~originalindices.flatten ++ ~loader.buffer.numFrames).asSet.asArray.sort +~orginalkeys = Array.newFrom(~slicer.index.keys).sort{|a,b| ~slicer.index[a][\bounds][0]< ~slicer.index[b][\bounds][0]} + +//the windowed function, recursive to deal with sync dependencies +( +~windowedFunct = {arg head, winSize, overlap; + var nbass = [], assignments = [], tempDict = (); + //check the size of everything to not overrun + winSize = (~originalslicesarray.size - head).min(winSize); + //copy the items to a subdataset from hear + winSize.do{|i| + tempDict.put((i.asString), ~sliceDict["data"][(~orginalkeys[(i+head)]).asString]);//here one could curate which stats to take + // "whichslices:%\n".postf(i+head); + }; + ~windowDS.load(Dictionary.newFrom([\cols, ~sliceDict["cols"].asInteger, \data, tempDict]), action: { + // "% - loaded\n".postf(head); + + //kmeans 2 and retrieve ordered array of class assignations + ~kmeans.fitPredict(~windowDS, ~windowLS, action: {|x| + nbass = x; + // "% - fitted1: ".postf(head); nbass.postln; + + if (nbass.includes(winSize.asFloat), { + ~kmeans.fitPredict(~windowDS, ~windowLS, {|x| + nbass = x; + // "% - fitted2: ".postf(head); nbass.postln; + if (nbass.includes(winSize.asFloat), { + ~kmeans.fitPredict(~windowDS, ~windowLS, {|x| + nbass = x; + // "% - fitted3: ".postf(head); nbass.postln; + }); + }); + }); + }); + + ~windowLS.dump{|x| + var assignments = x.at("data").asSortedArray.flop[1].flatten; + "% - assigned ".postf(head); - //leftovers - if ( (~originalslicesarray.size - ~head) > 1, { - //run a process on (a.size - ~head) items from ~head - (~originalslicesarray.size - ~head - 1).do{|i| - if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])}); - // (~head+i).postln; - }; + assignments.postln; + + (winSize-1).do{|i| + if (assignments[i+1] != assignments[i], { + ~newindices= ~newindices ++ (~originalslicesarray[head+i+1]).asInteger; + ~newkeys = ~newkeys ++ (~orginalkeys[head+i+1]); + }); + + }; + //if we still have some frames to do, do them + if (((winSize + head) < ~originalslicesarray.size), { + "-----------------".postln; + ~windowedFunct.value(head + winSize - overlap, winSize, overlap); + }, {~newindices = (~newindices ++ ~loader.buffer.numFrames); "done".postln;});//if we're done close the books + }; + }); }); - ~indices.postln; -}.play; +} ) -{var i = 8;BufRd.ar(1,b,Line.ar(~originalslicesarray[i],~originalslicesarray[i+1],(~originalslicesarray[i+1] - ~originalslicesarray[i])/b.sampleRate, doneAction: 2))}.play; -{var i = 4;BufRd.ar(1,b,Line.ar(~indices[i],~indices[i+1],(~indices[i+1] - ~indices[i])/b.sampleRate, doneAction: 2))}.play; +//the job + +//test 1 - start at the begining, consider 4 items at a time, make 2 clusters, overlap 1 +~newindices = [~originalslicesarray[0]]; ~newkeys = [~orginalkeys[0]]; +~windowedFunct.value(0, 4, 1); + +//OPTIONAL: try again with more clusters (3) and a wider window (6) and more overlap (2) +~newindices = [~originalslicesarray[0]]; ~newkeys = [~orginalkeys[0]]; +~kmeans.numClusters = 3; +~windowedFunct.value(0,6,2); + +//compare sizes +~orginalkeys.size +~newkeys.size; //export to reaper +( +//first create a new file that ends with rpp - it will overwrite if the file exists +f = File.new("/tmp/clusteredslices-" ++ Date.getDate.stamp ++".rpp","w+"); + +if (f.isOpen , { + var path, prevpath ="", sr, count, dur; + //write the header + f.write(" 0, { + f.write("\n>\n"); + }); + count = count + dur; + }; + //write the track footer + f.write(">\n"); + + // a second track with the new ~indices + prevpath = ""; + //write the track header + f.write(" 0, { + f.write("\n>\n"); + }); + count = count + dur; + }; + //write the track footer + f.write(">\n"); + + //write the footer + f.write(">\n"); + f.close; +}); +) + +(then open the time-stamped reaper file clusterdslice in the folder tmp) \ No newline at end of file diff --git a/release-packaging/Examples/dataset/1-learning examples/13-massive-parallelisation-example.scd b/release-packaging/Examples/dataset/1-learning examples/13-massive-parallelisation-example.scd new file mode 100644 index 0000000..4db597a --- /dev/null +++ b/release-packaging/Examples/dataset/1-learning examples/13-massive-parallelisation-example.scd @@ -0,0 +1,330 @@ +// Lookup in a KDTree using melbands +// Demonstration of a massive parallel approach to batch process swiftly in SC + +s.options.numBuffers = 16384 //The method below for doing the analysus quickly needs lots of buffers +s.reboot + +//Step 0: Make a corpus + +//We'll jam together some random flucoma sounds for illustrative purposes +//Get some files +( +~audioexamples_path = File.realpath(FluidBufMelBands.class.filenameSymbol).dirname.withTrailingSlash +/+ "../AudioFiles/*.wav"; +~allTheSounds = SoundFile.collect(~audioexamples_path); +~testSounds = ~allTheSounds; +~testSounds.do{|f| f.path.postln}; // print out the files that are loaded +) + +//Load the files into individual buffers: +( +~audio_buffers = ~testSounds.collect{|f| + Buffer.readChannel( + server: s, + path:f.path, + channels:[0], + action:{("Loaded" + f.path).postln;} + ) +}; +) + +//Do a segmentation of each buffer, in parallel +( +fork{ + ~index_buffers = ~audio_buffers.collect{Buffer.new}; + s.sync; + ~count = ~audio_buffers.size; + ~audio_buffers.do{|src,i| + FluidBufOnsetSlice.process( + server:s, + source:src, + indices:~index_buffers[i], + metric: 9, + threshold:0.2, + minSliceLength: 17, + action:{ + (~testSounds[i].path ++ ":" + ~index_buffers[i].numFrames + "slices").postln; + ~count = ~count - 1; + if(~count == 0){"Done slicing".postln}; + } + ); + } +} +) + +// we now have an array of index buffers, one per source buffer, each containing the segmentation points as a frame positions +// this allows us to make an array of sizes +~index_buffers.collect{|b| b.numFrames}.sum + +//For each of these segments, let's make a datapoint using the mean melbands. +// There's a number of ways of skinning this cat w/r/t telling the server what to do, but here we want to minimize traffic between language and server, and also produce undertsandable code + +//First, we'll grab the onset points as language-side arrays, then scroll through each slice getting the mean melbands +( +// - a dataset to keep the mean melbands in +~mels = FluidDataSet(s); +// - a dictionary to keep the slice points in for later playback +~slices = Dictionary(); +//The code below (as well as needing lots of buffers), creates lots of threads and we need a big ass scheduling queue +~clock = TempoClock(queueSize:8192); +) + + +// Do the Mel analysis in a cunning parallel fashion +( +{ + var counter, remaining; + var condition = Condition.new; // used to create a test condition to pause the routine ... + var index_arrays = Dictionary(); + + "Process started. Please wait.".postln; + + ~total_slice_count = ~index_buffers.collect{|b| b.numFrames}.sum + ~index_buffers.size; //we get an extra slice in buffer + ~featurebuffers = ~total_slice_count.collect{Buffer.new}; // create a buffer per slice + + //Make our dictionary FluidDataSet-shaped + ~slices.put("cols",3);//[bufnum,start,end] for each slice + ~slices.put("data",Dictionary()); + + //Collect each set of onsets into a language side array and store them in a dict + ~index_buffers.do{|b,i| // iterate over the 4 buffers + { + b.loadToFloatArray( // load to language side array + action:{|indices| + //Glue the first and last samples of the buffer on to the index list, and place in dictionary wiht the + //Buffer object as a key + + index_arrays.put(~audio_buffers[i], Array.newFrom([0] ++ indices ++ (~audio_buffers[i].numFrames - 1))); + + if(i==(~index_buffers.size-1)) {condition.unhang}; + } + ) + }.fork(stackSize:~total_slice_count); + }; + condition.hang; //Pause until all the callbacks above have completed + "Arrays loaded. Starting on the analysis, please wait.".postln; + + //For each of these lists of points, we want to scroll over the indices in pairs and get some mel bands + counter = 0; + remaining = ~total_slice_count; + + s.sync; + + // now iterate over Dict and calc melbands + + index_arrays.keysValuesDo{|buffer, indices| + indices.doAdjacentPairs{|start,end,num| + var analysis = Routine({|counter| + + FluidBufMelBands.processBlocking( + server:s, + source:buffer, + startFrame:start, + numFrames:(end-1) - start, + features:~featurebuffers[counter], + action:{ + remaining = remaining - 1; + if(remaining == 0) { ~numMelBands = ~featurebuffers[0].numChannels;condition.unhang }; + } + ); + }); + + ~slices["data"].put(counter,[buffer.bufnum,start,end]); + + //I'm spawning new threads to wait for the analysis callback from the server. The final callback will un-hang this thread + analysis.value(counter); //Done differently to other blocks because I need to pass in the value of counter + counter = counter + 1; + } + }; + condition.hang; + "Analysis of % slices done.\n".postf(~total_slice_count); +}.fork(clock:~clock); +) + + +// Run stats on each mel buffer + +// create a stats buffer for each of the slices +~statsbuffers = ~total_slice_count.collect{Buffer.new}; // create n Slices buffers - to be filled with (40 mel bands * 7 stats) + +// run stats on all the buffers +( +{ + var remaining = ~total_slice_count; + ~featurebuffers.do{|buffer,i| + FluidBufStats.processBlocking( + server:s, + source:buffer, + stats:~statsbuffers[i], + action:{ + remaining = remaining - 1; + if(remaining == 0) { "done".postln}; + } + ); + }; +}.fork(clock:~clock); +) + +~featurebuffers.size + +//Flatten each stats buffer into a data point +~flatbuffers = ~total_slice_count.collect{Buffer.new};// create an array of flatten stats + +( +{ + var remaining = ~total_slice_count; + ~statsbuffers.do{|buffer,i| + FluidBufFlatten.processBlocking( + server:s, + source:buffer, + destination:~flatbuffers[i], + action:{ + remaining = remaining - 1; + if(remaining == 0) { "Got flat points".postln; }; + } + ); + }; +}.fork(clock:~clock); +) + + +//Ram each flat point into a data set. At this point we have more data than we need, but we'll prune in moment +( +"Filling dataset".postln; +~mels.clear; + +// ~flatbuffers = flatbuffers; +~flatbuffers.do{|buf,i| + ~mels.addPoint(i,buf); +}; + +~mels.print; +) + + +// Prune & standardise + +// Tidy up the temp arrays of buffers we do not need anymore + +( +"Cleaning".postln; +(~featurebuffers ++ ~statsbuffers ++ ~flatbuffers).do{|buf| buf.free}; +) + +//Above we sneakily made a dictionary of slice data for playback (bufnum,start,end). Let's throw it in a dataset +~slicedata = FluidDataSet(s); // will hold slice data (bufnum,start,end) for playback + +//dict -> dataset +( +~slicedata.load(~slices); +~slicedata.print; +) + +// Step 1. Let's prune and standardize before fitting to a tree +( +~meanmels = FluidDataSet(s);//will hold pruned mel data +~stdmels = FluidDataSet(s);//will standardised, pruned mel data +~standardizer = FluidStandardize(s); +~pruner = FluidDataSetQuery(s); +~tree = FluidKDTree(s,numNeighbours:10,lookupDataSet:~slicedata);//we have to supply the lookup data set when we make the tree (boo!) +) + +//Prune, standardize and fit KDTree +( +{ + ~meanmels.clear; + ~stdmels.clear; + ~pruner.addRange(0,~numMelBands).transform(~mels,~meanmels); //prune with a 'query' -- so this is dropping all but ~meanmels + ~standardizer.fitTransform(~meanmels,~stdmels); + ~tree.fit(~stdmels,{"KDTree ready".postln}); +}.fork(clock:~clock); +) + +~meanmels.print + +//Step 2: Set the FluidStandardizer and FluidKDTree up for listening +//set the buffers and busses needed +( +~stdInputPoint = Buffer.alloc(s,40); +~stdOutputPoint = Buffer.alloc(s,40); +~treeOutputPoint = Buffer.alloc(s,3 * 10);//numNeighbours x triples of bufnum,start,end +) + + +// let's play a random sound (to make sure we understand our data structure! +( +{ + var randPoint, buf, start, stop, dur; + + randPoint = ~slices["data"].keys.asArray.scramble[0]; // this good way of getting - but recast as strong + + buf= ~slices["data"][randPoint][0]; + start = ~slices["data"][randPoint][1]; + stop = ~slices["data"][randPoint][2]; + + dur = stop - start; + + BufRd.ar(1,buf, Line.ar(start,stop,dur/s.sampleRate, doneAction: 2), 0, 2); +}.play +) + + +// Query KD tree + +// a target sound from outside our dataset +~inBuf = Buffer.readChannel(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", numFrames:15000, channels:[0]); +~inBuf.play + +//OR one from within +~inBuf = Buffer.alloc(s,15000); +~randomSlice = ~slices["data"].keys.asArray.scramble[0]; +~audio_buffers[~slices["data"][~randomSlice][0]].copyData(~inBuf,srcStartAt: ~slices["data"][~randomSlice][1], numSamples: 15000.min(~slices["data"][~randomSlice][2] - (~slices["data"][~randomSlice][1]))); +~inBuf.play + +// now try getting a point, playing it, grabbing nearest neighbour and playing it ... + +( +~inBufMels = Buffer(s); +~inBufStats = Buffer(s); +~inBufFlat = Buffer(s); +~inBufComp = Buffer(s); +~inBufStand = Buffer(s); +) + +// FluidBuf Compose is buf version of dataSetQuery + +( +FluidBufMelBands.process(s, ~inBuf, features: ~inBufMels, action: { + FluidBufStats.process(s, ~inBufMels, stats:~inBufStats, action: { + FluidBufFlatten.process(s, ~inBufStats, ~inBufFlat, action: { + FluidBufCompose.process(s, ~inBufFlat, numFrames: ~numMelBands, destination: ~inBufComp, action: { + ~standardizer.transformPoint(~inBufComp, ~inBufStand, { + ~tree.kNearest(~inBufStand,{ |a|a.postln;~nearest = a;}) + }) + }) + }) + }) +}) +) + +// playback nearest in order +( +fork{ + ~nearest.do{|i| + var buf, start, stop, dur; + + buf= ~slices["data"][i.asInteger][0]; + start = ~slices["data"][i.asInteger][1]; + stop = ~slices["data"][i.asInteger][2]; + dur = (stop - start)/ s.sampleRate; + {BufRd.ar(1,buf, Line.ar(start,stop,dur, doneAction: 2), 0, 2);}.play; + + i.postln; + dur.wait; + }; +} +) + + + + + diff --git a/release-packaging/Examples/dataset/1-learning examples/1a-starting-1D-example.scd b/release-packaging/Examples/dataset/1-learning examples/1a-starting-1D-example.scd index d10bbfd..5c72f38 100644 --- a/release-packaging/Examples/dataset/1-learning examples/1a-starting-1D-example.scd +++ b/release-packaging/Examples/dataset/1-learning examples/1a-starting-1D-example.scd @@ -1,12 +1,12 @@ s.reboot -~ds = FluidDataSet.new(s,\simple1data) +~ds = FluidDataSet.new(s) ~point = Buffer.alloc(s,1,1) ( Routine{ 10.do{|i| ~point.set(0,i); - ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); - s.sync; + ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); //because buffer.set do an immediate update in the RT thread we can take for granted it'll be updated when we call addPoint + s.sync; //but we need to sync to make sure everything is done on the DataSet before the next iteration } }.play ) @@ -29,7 +29,7 @@ Routine{ /*** KMEANS ***/ -~kmeans = FluidKMeans.new(s,maxIter:100) +~kmeans = FluidKMeans.new(s,maxIter:100); ~kmeans.numClusters = 2; //play with this ~kmeans.fit(~ds,action:{|x| "Done fitting with these number of items per cluster ".post;x.postln;}) @@ -49,7 +49,7 @@ Routine{ ( ~labels.size{|x| - Routine{x.asInteger.do{|i| //size does not return a value, but we can retrieve it via a function + Routine{x.asInteger.do{|i| ~labels.getLabel(i,action: {|l| ("Label for entry " + i ++ ":" + l).postln; }); @@ -60,4 +60,14 @@ Routine{ ) // or simply print it -~labels.print \ No newline at end of file +~labels.print + +// or dump and format +( +~labels.dump{|x| + var keys = x["data"].keys.asArray.sort; + keys.do{|key| + "Label for entry % is %\n".postf(key, x["data"][key][0]); + } +} +) diff --git a/release-packaging/Examples/dataset/1-learning examples/2a-starting-1D-example2.scd b/release-packaging/Examples/dataset/1-learning examples/2a-starting-1D-example2.scd index 5e6517f..a6475b3 100644 --- a/release-packaging/Examples/dataset/1-learning examples/2a-starting-1D-example2.scd +++ b/release-packaging/Examples/dataset/1-learning examples/2a-starting-1D-example2.scd @@ -1,5 +1,5 @@ s.reboot -~ds = FluidDataSet.new(s,\simple1data) +~ds = FluidDataSet.new(s) ~point = Buffer.alloc(s,1,1) ( Routine{ @@ -41,8 +41,8 @@ Routine{ Routine{ 15.do{|i| ~point.set(0,i); - s.sync; ~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for a value of " + i ++ ":" + x).postln}); + s.sync; } }.play ) diff --git a/release-packaging/Examples/dataset/1-learning examples/3a-classifier-example.scd b/release-packaging/Examples/dataset/1-learning examples/3a-classifier-example.scd index eb0855a..5f80842 100644 --- a/release-packaging/Examples/dataset/1-learning examples/3a-classifier-example.scd +++ b/release-packaging/Examples/dataset/1-learning examples/3a-classifier-example.scd @@ -1,6 +1,6 @@ ( -~simpleInput = FluidDataSet(s,\simpleInput); -~simpleOutput = FluidLabelSet(s,\simpleOutput); +~simpleInput = FluidDataSet(s); +~simpleOutput = FluidLabelSet(s); b = Buffer.alloc(s,2); ~knn = FluidKNNClassifier(s); ~knn.numNeighbours = 3 @@ -53,7 +53,8 @@ Routine{ ~simpleInput.addPoint((i+50).asString,b,{("Added Input" + (i+50)).postln}); ~simpleOutput.addLabel((i+50).asString,"Red",{("Added Output" + (i+50)).postln}); s.sync; - } + }; + \done.postln; }.play; ) diff --git a/release-packaging/Examples/dataset/1-learning examples/4-regressor-example.scd b/release-packaging/Examples/dataset/1-learning examples/4-regressor-example.scd index 313fdad..2d6b92d 100644 --- a/release-packaging/Examples/dataset/1-learning examples/4-regressor-example.scd +++ b/release-packaging/Examples/dataset/1-learning examples/4-regressor-example.scd @@ -9,9 +9,10 @@ n = 200 // creates the dataset with these associated indices and values ( -~simpleInput = FluidDataSet(s,\simpleInput); -~simpleOutput = FluidDataSet(s,\simpleOutput); -b = Buffer.alloc(s,1,1); +~simpleInput = FluidDataSet(s); +~simpleOutput = FluidDataSet(s); +b = Buffer.alloc(s,1); +c = Buffer.alloc(s,1); ~mappingviz = Buffer.alloc(s,512); ) @@ -19,12 +20,11 @@ b = Buffer.alloc(s,1,1); Routine{ n.do{|i| b.set(0,~idx[i]); + c.set(0,~data[i]); ~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln}); + ~simpleOutput.addPoint(i.asString,c,{("Added Output" + i).postln}); + ~mappingviz.set((~idx[i]/61.4).asInteger,~data[i]); 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 ) diff --git a/release-packaging/Examples/dataset/1-learning examples/5-normalization-and-standardization-example.scd b/release-packaging/Examples/dataset/1-learning examples/5-normalization-and-standardization-example.scd index fc78214..1cf3de6 100644 --- a/release-packaging/Examples/dataset/1-learning examples/5-normalization-and-standardization-example.scd +++ b/release-packaging/Examples/dataset/1-learning examples/5-normalization-and-standardization-example.scd @@ -1,7 +1,7 @@ ( // set some variables ~nb_of_dim = 10; -~dataset = FluidDataSet(s,\test,~nb_of_dim); +~dataset = FluidDataSet(s); ) ( @@ -14,6 +14,7 @@ Routine{ s.sync; }); buf.free; + \done.postln; }.play ) @@ -38,7 +39,7 @@ Routine{ ~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); +~normed_dataset = FluidDataSet(s); // normalize the full dataset ~normalize.transform(~dataset,~normed_dataset,{"done".postln;}); @@ -55,7 +56,7 @@ Routine{ ~standardize.fit(~dataset,{"done".postln;}); // standardize the full dataset -~standardized_dataset = FluidDataSet(s,\standardized,~nb_of_dim); +~standardized_dataset = FluidDataSet(s); ~standardize.transform(~dataset,~standardized_dataset,{"done".postln;}); // look at a point to see that it has points in it @@ -89,7 +90,7 @@ Routine{ ~normtree.fit(~normed_dataset) ~normtree.kNearest(~normbuf, {|x| ("Labels:" + x).postln}); ~normtree.kNearestDist(~normbuf, {|x| ("Distances:" + x).postln}); -// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will probably have changed. The distance is now different too +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg might 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); @@ -101,7 +102,7 @@ Routine{ ~stdtree.fit(~standardized_dataset) ~stdtree.kNearest(~stdbuf, {|x| ("Labels:" + x).postln}); ~stdtree.kNearestDist(~stdbuf, {|x| ("Distances:" + x).postln}); -// 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 +// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg might 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 @@ -112,7 +113,7 @@ Routine{ ~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! +//query the single nearest neighbourg via 3 different data scaling. Depending on the random source at the begining, you should get (small or large) differences between the 3 answers! [~tree,~normtree,~stdtree].do{|t| t.numNeighbours =1 }; ~tree.kNearest(~query_buf, {|x| ("Original:" + x).post;~tree.kNearestDist(~query_buf, {|x| (" with a distance of " + x).postln});}); ~normtree.kNearest(~normbuf, {|x| ("Normalized:" + x).post;~normtree.kNearestDist(~normbuf, {|x| (" with a distance of " + x).postln});}); diff --git a/release-packaging/Examples/dataset/1-learning examples/7a-making-datasets-with-json.scd b/release-packaging/Examples/dataset/1-learning examples/7a-making-datasets-with-json.scd index fd11823..01b29e9 100644 --- a/release-packaging/Examples/dataset/1-learning examples/7a-making-datasets-with-json.scd +++ b/release-packaging/Examples/dataset/1-learning examples/7a-making-datasets-with-json.scd @@ -41,4 +41,12 @@ ~classifier = FluidKNNClassifier.new(s, numNeighbours:2); ~classifier.fit(~ds,~ls, {\done.postln;}) -~classifier.predictPoint(~target, action: {|x|x.postln;}) +// run many times for random pleasure +( +fork{ + var value = 7.rand; + ~ds.getPoint(("entry-"++value).asSymbol,~target); + s.sync; + ~classifier.predictPoint(~target, action: {|x|"entry % is an % entry.\n".postf(value,x);}) +} +) \ No newline at end of file diff --git a/release-packaging/Examples/dataset/1-learning examples/8a-mlp-didactic.scd b/release-packaging/Examples/dataset/1-learning examples/8a-mlp-didactic.scd index e923789..54810e2 100644 --- a/release-packaging/Examples/dataset/1-learning examples/8a-mlp-didactic.scd +++ b/release-packaging/Examples/dataset/1-learning examples/8a-mlp-didactic.scd @@ -8,13 +8,13 @@ Routine{ d = Dictionary.new; d.add(\cols -> 2); d.add(\data -> Dictionary.newFrom(["f-f", [0,0], "f-t", [0,1], "t-f", [1,0], "t-t", [1,1]])); - ~mlpHelpSource = FluidDataSet.new(s,\mlpHelpSource); + ~mlpHelpSource = FluidDataSet.new(s); s.sync; ~mlpHelpSource.load(d); s.sync; d.add(\cols -> 1); d.add(\data -> Dictionary.newFrom(["f-f", [0], "f-t", [1], "t-f", [1], "t-t", [0]])); - ~mlpHelpTarget = FluidDataSet.new(s,\mlpHelpTarget); + ~mlpHelpTarget = FluidDataSet.new(s); s.sync; ~mlpHelpTarget.load(d); s.sync; @@ -27,7 +27,7 @@ Routine{ // make an MLPregressor ~mlp = FluidMLPRegressor(s, [3], FluidMLPRegressor.sigmoid, FluidMLPRegressor.sigmoid,maxIter:1000,learnRate: 0.1,momentum: 0.1,batchSize: 1,validation: 0);//1000 epoch at a time -//train on it and observe the error +//train it by executing the following line multiple time, and observe the error ~mlp.fit(~mlpHelpSource,~mlpHelpTarget,{|x|x.postln;}); //to make a plot of the error let's do a classic 'shades of truth' (a grid of 11 x 11 with each values of truth between 0 and 1 @@ -37,7 +37,7 @@ Routine{ d.add(\cols -> 2); d.add(\data -> Dictionary.newFrom(121.collect{|x|[x.asString, [x.div(10)/10,x.mod(10)/10]]}.flatten)); - ~mlpHelpShades = FluidDataSet.new(s,\mlpHelpShades); + ~mlpHelpShades = FluidDataSet.new(s); s.sync; ~mlpHelpShades.load(d); s.sync; @@ -48,7 +48,7 @@ Routine{ ~mlpHelpShades.print // let's make a destination for our regressions -~mlpHelpRegressed = FluidDataSet.new(s,\mlpHelpRegressed); +~mlpHelpRegressed = FluidDataSet.new(s); // then predict the full DataSet in our trained network ~mlp.predict(~mlpHelpShades,~mlpHelpRegressed); @@ -77,6 +77,7 @@ w.refresh; w.front; ) +~mlp.free ~mlpHelpShades.free ~mlpHelpSource.free ~mlpHelpTarget.free \ No newline at end of file diff --git a/release-packaging/Examples/dataset/1-learning examples/8b-mlp-synth-control.scd b/release-packaging/Examples/dataset/1-learning examples/8b-mlp-synth-control.scd index 7c77e48..49a3d38 100644 --- a/release-packaging/Examples/dataset/1-learning examples/8b-mlp-synth-control.scd +++ b/release-packaging/Examples/dataset/1-learning examples/8b-mlp-synth-control.scd @@ -7,8 +7,8 @@ var output = Buffer.alloc(s,10); var mlp = FluidMLPRegressor(s,[6],activation: 1,outputActivation: 1,maxIter: 1000,learnRate: 0.1,momentum: 0,batchSize: 1,validation: 0); var entry = 0; -~inData = FluidDataSet(s,\indata); -~outData = FluidDataSet(s,\outdata); +~inData = FluidDataSet(s); +~outData = FluidDataSet(s); w = Window("ChaosSynth", Rect(10, 10, 790, 320)).front; a = MultiSliderView(w,Rect(10, 10, 400, 300)).elasticMode_(1).isFilled_(1); @@ -42,8 +42,10 @@ f.action = {arg x,y; //if trained, predict the point f.x f.y c = Button(w, Rect(730,240,50, 20)).states_([["train", Color.red, Color.white], ["trained", Color.white, Color.grey]]).action_{ mlp.fit(~inData,~outData,{|x| trained = 1; - c.value = 1; - e.value = x.round(0.001).asString; + { + c.value = 1; + e.value = x.round(0.001).asString; + }.defer; });//train the network }; d = Button(w, Rect(730,10,50, 20)).states_([["entry", Color.white, Color.grey], ["entry", Color.red, Color.white]]).action_{ diff --git a/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd b/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd index a055e86..9ca0eaa 100644 --- a/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd +++ b/release-packaging/Examples/dataset/1-learning examples/8c-mlp-regressor-as-dim-redux.scd @@ -1,9 +1,9 @@ s.reboot; //Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers ( -~raw = FluidDataSet(s,\Mel40); -~norm = FluidDataSet(s,\Mel40n); -~retrieved = FluidDataSet(s,\ae2); +~raw = FluidDataSet(s); +~norm = FluidDataSet(s); +~retrieved = FluidDataSet(s); ~audio = Buffer.read(s,File.realpath(FluidBufMelBands.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav"); ~melfeatures = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 40); @@ -56,7 +56,7 @@ FluidBufMelBands.process(s,~audio, features: ~melfeatures,action: {\done.postln; ~retrieved.print; //let's normalise it for display -~normData = FluidDataSet(s,\ae2N); +~normData = FluidDataSet(s); ~reducedarray = Array.new(100); ~normalView = FluidNormalize(s,0.1,0.9); ( diff --git a/release-packaging/Examples/dataset/1-learning examples/9-regressor-comparison.scd b/release-packaging/Examples/dataset/1-learning examples/9-regressor-comparison.scd index 8da71f2..84e80da 100644 --- a/release-packaging/Examples/dataset/1-learning examples/9-regressor-comparison.scd +++ b/release-packaging/Examples/dataset/1-learning examples/9-regressor-comparison.scd @@ -2,9 +2,9 @@ //Here we make a 3-points pair of dataset. -~dsIN = FluidDataSet(s,\dsIN); +~dsIN = FluidDataSet(s); ~dsIN.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\point1, [10], \point2, [20], \point3, [30]])])); -~dsOUT = FluidDataSet(s,\dsOUT); +~dsOUT = FluidDataSet(s); ~dsOUT.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\point1, [0.8], \point2, [0.2], \point3, [0.5]])])); //check the values @@ -13,13 +13,13 @@ (0.dup(10) ++ 0.8 ++ 0.dup(9) ++ 0.2 ++ 0.dup(9) ++ 0.5 ++ 0.dup(10)).plot(\source,discrete: true, minval:0, maxval: 1).plotMode=\bars; //Let's make a complete dataset to predict each points in our examples: -~dsALLin = FluidDataSet(s,\dsALLin); +~dsALLin = FluidDataSet(s); ~dsALLin.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom(Array.fill(41,{|x| [x.asSymbol, [x]];}).flatten(1);)])); ~dsALLin.print //We'll regress these values via KNN and plot ~regK = FluidKNNRegressor(s,numNeighbours: 2,weight: 1); -~dsALLknn = FluidDataSet(s,\dsALLknn); +~dsALLknn = FluidDataSet(s); ~regK.fit(~dsIN,~dsOUT); ~regK.predict(~dsALLin,~dsALLknn); @@ -36,7 +36,7 @@ //Let's do the same process with MLP ~regM = FluidMLPRegressor(s,hidden: [4],activation: 1,outputActivation: 1,maxIter: 10000,learnRate: 0.1,momentum: 0,batchSize: 1,validation: 0); -~dsALLmlp = FluidDataSet(s,\dsALLmlp); +~dsALLmlp = FluidDataSet(s); ~regM.fit(~dsIN,~dsOUT,{|x|x.postln;}); ~regM.predict(~dsALLin,~dsALLmlp); @@ -86,7 +86,7 @@ ~dsALLknn.dump{|x| 41.do{|i| ~knnALLval.add((x["data"][i.asString])); };//draw everything - [~source, ~knnALLval.flatten(1), ~mlpALLval.flatten(1)].flop.flatten(1).plot(\source,numChannels: 3, discrete: false, minval:0, maxval: 1).plotMode=\bars; + {[~source, ~knnALLval.flatten(1), ~mlpALLval.flatten(1)].flop.flatten(1).plot(\source,numChannels: 3, discrete: false, minval:0, maxval: 1).plotMode=\bars;}.defer; }; }); }); diff --git a/release-packaging/Examples/dataset/2-various other examples/scaling-dimension-as-weighting/2-3Dscaling.scd b/release-packaging/Examples/dataset/2-various other examples/scaling-dimension-as-weighting/2-3Dscaling.scd new file mode 100644 index 0000000..71b7e7f --- /dev/null +++ b/release-packaging/Examples/dataset/2-various other examples/scaling-dimension-as-weighting/2-3Dscaling.scd @@ -0,0 +1,161 @@ +// Make: +// - A kmeans +// - a datasetquery +// - a normalizer +// - a standardizer +// - 3 DataSets of example points R-G-B descriptions +// - 3 DataSets for the scaled versions +// - 1 summative dataset and a LabelSet for predicted labels + +( +~classifier = FluidKMeans(s,5, 1000); +~query = FluidDataSetQuery(s); +~stan = FluidStandardize(s); +~norm = FluidNormalize(s); +~sourceR = FluidDataSet(s); +~sourceG = FluidDataSet(s); +~sourceB = FluidDataSet(s); +~scaledR = FluidDataSet(s); +~scaledG = FluidDataSet(s); +~scaledB = FluidDataSet(s); +~composited = FluidDataSet(s); +~labels = FluidLabelSet(s); +) + +//Make some random, but clustered test points, each descriptor category in a separate dataset +( +~sourceR.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, 1.0.sum3rand]}.flatten))])); +~sourceG.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, 1.0.rand2]}.flatten))])); +~sourceB.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, (0.5.sum3rand).squared + [0.75,-0.1].choose]}.flatten))])); +) + +//here we manipulate + +//assemble the scaled dataset +( +~query.addColumn(0, { + ~query.transformJoin(~sourceB, ~sourceG, ~composited, { + ~query.transformJoin(~sourceR, ~composited, ~composited); + }); +}); +) + +~composited.print + +//Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set +~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict = x;};~composited.dump{|x|~compodict=x;};}); + +//Visualise: +( +w = Window("sourceClasses", Rect(128, 64, 820, 120)); +w.drawFunc = { + Pen.use{ + ~compodict["data"].keysValuesDo{|key, colour| + Pen.fillColor = Color.fromArray((colour * 0.5 + 0.5 ).clip(0,1) ++ 1); + Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict["data"].at(key).asInteger[0] * 20 + 10),15,15)); + }; + }; +}; +w.refresh; +w.front; +) + +// standardize our colours and rerun +( +~stan.fitTransform(~sourceR, ~scaledR, { + ~stan.fitTransform(~sourceG, ~scaledG, { + ~stan.fitTransform(~sourceB, ~scaledB, { + //assemble + ~query.addColumn(0, { + ~query.transformJoin(~scaledB, ~scaledG, ~composited, { + ~query.transformJoin(~scaledR, ~composited, ~composited, { + //fit + ~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};}); + }); + }); + }); + }); + }); +}); +) + +//Visualise: +( +w = Window("stanClasses", Rect(128, 204, 820, 120)); +w.drawFunc = { + Pen.use{ + ~compodict2["data"].keysValuesDo{|key, colour| + Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1); + Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15)); + }; + }; +}; +w.refresh; +w.front; +) + +//now let's normalise instead +( +~norm.fitTransform(~sourceR, ~scaledR, { + ~norm.fitTransform(~sourceG, ~scaledG, { + ~norm.fitTransform(~sourceB, ~scaledB, { + //assemble + ~query.addColumn(0, { + ~query.transformJoin(~scaledB, ~scaledG, ~composited, { + ~query.transformJoin(~scaledR, ~composited, ~composited, { + //fit + ~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};}); + }); + }); + }); + }); + }); +}); +) + +//Visualise: +( +w = Window("normClasses", Rect(128, 344, 820, 120)); +w.drawFunc = { + Pen.use{ + ~compodict2["data"].keysValuesDo{|key, colour| + Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1); + Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15)); + }; + }; +}; +w.refresh; +w.front; +) + +// let's mess up with the scaling of one dimension: let's multiply the range of Red by 10 +~norm.min = -10; +~norm.max = 10; +( +~norm.fitTransform(~sourceR, ~scaledR, { + //assemble + ~query.addColumn(0, { + ~query.transformJoin(~scaledB, ~scaledG, ~composited, { + ~query.transformJoin(~scaledR, ~composited, ~composited, { + //fit + ~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};}); + }); + }); + }); +}); +) + +//Visualise: +( +w = Window("norm10rClasses", Rect(128, 484, 820, 120)); +w.drawFunc = { + Pen.use{ + ~compodict2["data"].keysValuesDo{|key, colour| + Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1); + Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15)); + }; + }; +}; +w.refresh; +w.front; +) diff --git a/release-packaging/Examples/nb_of_slices.scd b/release-packaging/Examples/nb_of_slices.scd deleted file mode 100644 index c30174e..0000000 --- a/release-packaging/Examples/nb_of_slices.scd +++ /dev/null @@ -1,59 +0,0 @@ -( -b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); -c = Buffer.new(s); -) - -( -// with basic params -Routine{ - var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh; - startTime = Main.elapsedTime; - prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999 - target = 10; //number of slices desired - tolerance = 0; // the acceptable error in the number of slices yield - maxIters = 100; //max number of iterations acceptable - - //makes a first iteration - FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames}); - //makes a second iteration - if ( (prevVal < target), { - curThresh = (prevThresh * 0.5).max(0.000001); - }, { - curThresh = (prevThresh * 2).min(0.999999); - }); - FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames}); - - //makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached - iters = 2; - while ( { - (iters < maxIters) && ((curVal - target).abs > tolerance) - }, { - iters = iters + 1; - dVal = curVal - prevVal; - dThresh = curThresh - prevThresh; - - prevThresh = curThresh; - prevVal = curVal; - - if ( (dVal == 0), { - //if we have not change results between the last 2 passes, make the variation of threshold bigger - curThresh = (dThresh + curThresh).min(0.999999).max(0.000001); - },{ - //if we have - curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001); - }); - FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames}); - } - ); - //depending on the outcome, gives the right info back - - if ( iters >= maxIters, { - // failed - "Failed to find a suitable threshold in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01)); - }, { - // succeeded - "Found % as a suitable threshold for % slices in % seconds and % iterations.\n".postf(curThresh, curVal, (Main.elapsedTime - startTime).round(0.01), iters); - } - ); -}.play -) \ No newline at end of file diff --git a/release-packaging/Examples/segmenting/nb_of_slices.scd b/release-packaging/Examples/segmenting/nb_of_slices.scd index c30174e..83f27d6 100644 --- a/release-packaging/Examples/segmenting/nb_of_slices.scd +++ b/release-packaging/Examples/segmenting/nb_of_slices.scd @@ -7,6 +7,7 @@ c = Buffer.new(s); // with basic params Routine{ var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh; + startTime = Main.elapsedTime; prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999 target = 10; //number of slices desired @@ -14,14 +15,17 @@ Routine{ maxIters = 100; //max number of iterations acceptable //makes a first iteration - FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames}); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh).wait; + prevVal = c.numFrames; + //makes a second iteration if ( (prevVal < target), { curThresh = (prevThresh * 0.5).max(0.000001); }, { curThresh = (prevThresh * 2).min(0.999999); }); - FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames}); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh).wait; + curVal = c.numFrames; //makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached iters = 2; @@ -42,7 +46,8 @@ Routine{ //if we have curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001); }); - FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames}); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh).wait; + curVal = c.numFrames; } ); //depending on the outcome, gives the right info back diff --git a/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidAmpSlice.schelp index 347ddf2..2343f2d 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. A frequency of 0 bypasses the filter. + The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass 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. @@ -54,7 +54,8 @@ code:: // detrending explained // Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track ( -{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); +{ + var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6)); env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60); [source, env] }.plot(0.08); diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp index 70a137f..f9acdc3 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpGate.schelp @@ -16,80 +16,81 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process - This is the method that calls for the slicing to be calculated on a given source buffer. +METHOD:: process, processBlocking +This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server - The server on which the buffers to be processed are allocated. +The server on which the buffers to be processed are allocated. ARGUMENT:: source - The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. +The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed. ARGUMENT:: startFrame - Where in the srcBuf should the slicing process start, in sample. +Where in the srcBuf should the slicing process start, in sample. ARGUMENT:: numFrames - How many frames should be processed. +How many frames should be processed. ARGUMENT:: startChan - For multichannel sources, which channel should be processed. +For multichannel sources, which channel should be processed. ARGUMENT:: numChans - For multichannel sources, how many channel should be summed. +For multichannel sources, how many channel should be summed. 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. +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 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 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 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 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. +The length in samples that the Slice will stay ON. Changes of states during that period will be ignored. ARGUMENT:: minSilenceLength - The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored. +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 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 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. +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. 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. +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. A frequency of 0 bypasses the filter. +The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##Linkwitz–Riley high-pass 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:: freeWhenDone +Free the server instance when processing complete. Default true 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. +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. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: code:: // define a test signal and a destination buffer ( -b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); -c = Buffer.new(s); + b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs})); + c = Buffer.new(s); ) b.play b.plot - //basic tests: absThresh sanity FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12) c.query @@ -140,30 +141,33 @@ STRONG::A musical example.:: CODE:: //load a buffer ( -b = Buffer.read(s,File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); -c = Buffer.new(s); + b = Buffer.read(s, File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav"); + c = Buffer.new(s); ) // slice the samples -FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40) -c.query -c.getn(0,c.numFrames*2,{|item|item.postln;}) -//reformatting to read the onsets and offsets as pairs -c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});}) - +( +Routine{ + FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40).wait; + c.query; + c.getn(0,c.numFrames*2,{|item|item.postln;}); + //reformatting to read the onsets and offsets as pairs + c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});}); +}.play +) //loops over a splice with the MouseX, taking the respective onset and offset of a given slice ( -{ - BufRd.ar(1, b, - Phasor.ar(0,1, - BufRd.kr(2, c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], - BufRd.kr(2, c, - MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], - BufRd.kr(2,c, - MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] - ), 0, 1); -}.play; + { + BufRd.ar(1, b, + Phasor.ar(0,1, + BufRd.kr(2, c, + MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0], + BufRd.kr(2, c, + MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1], + BufRd.kr(2,c, + MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0] + ), 0, 1); + }.play; ) :: @@ -178,15 +182,16 @@ b.play // create a new buffer as destinations c = Buffer.new(s); - +OSCFunc.trace(false) //run the process on them ( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30); - (Main.elapsedTime - t).postln; -}.play + // with basic params + Routine{ + var t = Main.elapsedTime; + var proc= FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30); + proc.wait; + (Main.elapsedTime - t).postln; + }.play ) // list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting diff --git a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp index f5739cc..b01fccd 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAmpSlice.schelp @@ -16,7 +16,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server @@ -67,11 +67,13 @@ ARGUMENT:: minSliceLength 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. A frequency of 0 bypasses the filter. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -143,14 +145,16 @@ b.play c = Buffer.new(s); //run the process on them -( -// with basic params -Routine{ - t = Main.elapsedTime; - FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5); - (Main.elapsedTime - t).postln; -}.play -) + ( + // with basic params + Routine{ + var t = Main.elapsedTime; + var proc = FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5); + proc.wait; + c.query; + (Main.elapsedTime - t).postln; + }.play + ) // list the indicies of detected attacks - the two input channels have been summed. c.getn(0,c.numFrames,{|item|(item * 2).postln;}) diff --git a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp index 2f38ff1..2955970 100644 --- a/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufAudioTransport.schelp @@ -11,7 +11,7 @@ Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTI CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking Process two audio link::Classes/Buffer:: ARGUMENT:: server @@ -65,6 +65,9 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument. @@ -89,6 +92,7 @@ FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,act // listen to the source and the result b.play c.play +d.updateInfo d.play // more interesting sources: two cardboard bowing gestures diff --git a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp index 5c2b323..626b58f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufCompose.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufCompose.schelp @@ -12,7 +12,7 @@ The algorithm takes a srcBuf, and writes the information at the provided dstBuf. CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This method triggers the compositing. ARGUMENT:: server @@ -48,11 +48,13 @@ ARGUMENT:: destStartChan ARGUMENT:: destGain The gain applied to the samples in the region of the destination buffer over which the source is to be copied. The default value (0) will overwrite that section of the destination buffer, and a value of 1.0 would sum the source to the material that was present. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and destination instance variables have been updated on the client side. The function will be passed destination as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor DISCUSSION:: It is important to understand the rules used for determining the final desintinaiton buffer dimensions to get the most out of this object. If needs be, the destination buffer will be resized to the maxima of the requsted source numFrames and numChannels. Frames will be written up to the limit of actually available samples (meaning you can create zero padding); channels will be written modulo the available channels, taking into account the channel offsets, meaning you can have channels repeat or loop into the source buffer's channels. See the examples below. @@ -68,29 +70,42 @@ d = Buffer.new(s); ) // with basic params (basic summing of each full buffer in all dimensions) -FluidBufCompose.process(s, source: b, destination: d); -FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + FluidBufCompose.process(s, source: b, destination: d).wait; + FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play; +) //constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d); -FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d).wait; + FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) //constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d); -FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0); -d.query; -d.play; - +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d).wait; + FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) //constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left -d.free; d = Buffer.new(s); -FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d); -FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0); -d.query; -d.play; +( +Routine{ + d.free; d = Buffer.new(s); + FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d).wait; + FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0).wait; + d.query; + d.play; +}.play +) :: diff --git a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp index 1b744b0..8d404a5 100644 --- a/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufFlatten.schelp @@ -47,6 +47,9 @@ The link::Classes/Buffer:: to write the flattened data to ARGUMENT:: axis Whether to group points channel-wise or frame-wise +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action Runs when processing is complete @@ -65,6 +68,8 @@ Whether to group points channel-wise or frame-wise ARGUMENT:: trig Trigger signal to defer / retrigger processing +returns:: an instance of the processor + EXAMPLES:: code:: @@ -83,10 +88,10 @@ FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,acti // 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:{ +FluidBufFlatten.process(s,~pitchdata,~flatdata,axis:1,action:{ ~flatdata.loadToFloatArray(action:{ |a| a.postln; }) }) ) -:: \ No newline at end of file +:: diff --git a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp index 711bf5b..11fe72f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufHPSS.schelp @@ -24,7 +24,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the HPSS to be calculated on a given source buffer. ARGUMENT:: server @@ -102,11 +102,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument. -RETURNS:: - Nothing, as the various destination buffers are declared in the function call. +returns:: an instance of the processor Discussion:: HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation. @@ -123,16 +125,17 @@ code:: d = Buffer.new(s); e = Buffer.new(s); ) - +OSCFunc.trace(true, true) // run with basic parameters ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d); + FluidBufHPSS.process(s, b, harmonic: c, percussive: d).wait; (Main.elapsedTime - t).postln; }.play ) - +c.query +d.query //play the harmonic c.play; //play the percussive @@ -143,11 +146,12 @@ d.play; //more daring parameters, in mode 2 ( - Routine{ - t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512); - (Main.elapsedTime - t).postln; - }.play +Routine{ + t = Main.elapsedTime; + FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512) + .wait; + (Main.elapsedTime - t).postln; +}.play ) //play the harmonic @@ -171,9 +175,12 @@ c = Buffer.read(s,File.realpath(FluidBufHPSS.class.filenameSymbol).dirname.withT ) // composite one on left one on right as test signals -FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b) -b.play - +( +Routine{ + FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b).wait; + b.play +}.play +) // create 2 new buffers as destinations d = Buffer.new(s); e = Buffer.new(s); @@ -181,7 +188,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufHPSS.process(s, b, harmonic: d, percussive:e); + FluidBufHPSS.process(s, b, harmonic: d, percussive:e).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp index be696d5..74e0cae 100644 --- a/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufLoudness.schelp @@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the loudness descriptor to be calculated on a given source buffer. ARGUMENT:: server @@ -51,11 +51,13 @@ ARGUMENT:: windowSize ARGUMENT:: hopSize How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -73,7 +75,7 @@ b.play; b.plot; ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, source:b, features: c); + FluidBufLoudness.process(s, source:b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -109,7 +111,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410); + FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp index ec1f1bd..8ad8c8a 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMFCC.schelp @@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. ARGUMENT:: server @@ -59,11 +59,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -78,7 +80,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMFCC.process(s, b, features: c); + FluidBufMFCC.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -108,7 +110,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMFCC.process(s, b, numCoeffs:5, features: c); + FluidBufMFCC.process(s, b, numCoeffs:5, features: c).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp index da18154..8b76900 100644 --- a/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufMelBands.schelp @@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. ARGUMENT:: server @@ -60,11 +60,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -79,7 +81,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10); + FluidBufMelBands.process(s, b, features: c, numBands:10).wait; (Main.elapsedTime - t).postln; }.play ) @@ -109,7 +111,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufMelBands.process(s, b, features: c, numBands:10); + FluidBufMelBands.process(s, b, features: c, numBands:10).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp index ba97ecb..9f43e75 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMF.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMF.schelp @@ -35,7 +35,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the factorisation to be calculated on a given source buffer. ARGUMENT:: server @@ -102,11 +102,13 @@ ARGUMENT:: windowType ARGUMENT:: randomSeed The NMF process needs to seed its starting point. If specified, the same values will be used. The default of -1 will randomly assign them. (not implemented yet) +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [resynth, bases, activations] as an argument. -RETURNS:: - Nothing, as the various destination buffers are declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -142,7 +144,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 components Routine { - FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2); + FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2).wait; e.query; f.query; g.query; @@ -179,16 +181,15 @@ y = Buffer.new(s); // matrix factorisation, requesting everything - wait for the computation time to appear. ( Routine{ - t = Main.elapsedTime; - FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size); - (Main.elapsedTime - t).postln; + var t = Main.elapsedTime; + FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size) + .wait; + (Main.elapsedTime - t).postln; }.play ) //look at the resynthesised components, the bases and the activations c.plot; x.plot; y.plot; -//null test of the sum of sources -{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play // play the components spread in the stereo field {Splay.ar(PlayBuf.ar(5,c,doneAction:2))}.play @@ -196,6 +197,9 @@ c.plot; x.plot; y.plot; //play a single source {PlayBuf.ar(5,c,doneAction:2)[~which_component].dup}.play +//null test of the sum of sources +{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play + //play noise using one of the bases as filter. ( { @@ -251,7 +255,7 @@ b.play // train using the first 2 seconds of the sound file ( Routine { - FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10); + FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10).wait; ~originalNMF.query; }.play; ) @@ -296,7 +300,7 @@ z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, des //process the whole file, splitting it with the 2 trained bases ( Routine{ - FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2); + FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2).wait; ~originalNMF.query; }.play; ) @@ -354,7 +358,7 @@ e.query ( // use the seeding basis, without updating Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3); + FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3).wait; e.query; f.query; g.query; @@ -373,7 +377,7 @@ g.plot; ( // use the seeding bases, with updating this time Routine { - FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3); + FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3).wait; e.query; f.query; g.query; diff --git a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp index e359dad..998b946 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNMFCross.schelp @@ -52,9 +52,13 @@ The analysus hop size in samples (default winSize / 2) ARGUMENT:: fftSize The analsyis FFT size in samples (default = winSize) +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A function to run when processing is complete, taking the output buffer as its argument +returns:: an instance of the processor INSTANCEMETHODS:: @@ -69,4 +73,4 @@ 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 +:: diff --git a/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp b/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp index b0656d1..822e4a2 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNNDSVD.schelp @@ -10,7 +10,7 @@ See http://nimfa.biolab.si/nimfa.methods.seeding.nndsvd.html CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the decomposition to be calculated on a given source buffer. ARGUMENT:: server @@ -53,9 +53,15 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument. +returns:: an instance of the processor + + INSTANCEMETHODS:: private:: synth, server @@ -71,16 +77,24 @@ b = Buffer.read(s,File.realpath(FluidBufNNDSVD.class.filenameSymbol).dirname.wit ) //how many bases do I need to decompose the buffer with 90% accuracy -FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1, action: {\done.postln;}) - +( +Routine{ + FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1).wait; + "% bases".format(~bases.numChannels).postln; +}.play; +) //check how many bases we are returned: -~bases.numChannels + //try the same process with less accuracy -FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5, action: {\done.postln;}) -~bases.numChannels +( +Routine{ + FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5).wait; + "% bases".format(~bases.numChannels).postln; +}.play +) //use the bases to run NMF on FluidBufNMF.process(s, b, resynth: ~resynth, bases: ~bases, activations: ~activations,actMode: 2, components: ~bases.numChannels, action: {\done.postln;}) -{PlayBuf.ar(~resynth.numChannels, ~resynth)[1]}.play +{PlayBuf.ar(~resynth.numChannels, ~resynth)[2]}.play :: diff --git a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp index 0dba199..dbbf115 100644 --- a/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufNoveltySlice.schelp @@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server @@ -67,11 +67,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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 function will be passed indices as an argument. -RETURNS:: - Nothing, as the various destination buffers are declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -87,7 +89,7 @@ c = Buffer.new(s); // with basic params, with a minimum slight length to avoid over Routine{ t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8).wait; (Main.elapsedTime - t).postln; }.play ) @@ -120,11 +122,13 @@ c = Buffer.new(s); ) // process with a given filterSize -FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1) - +( +Routine{ + FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1).wait; //check the number of slices: it is the number of frames in the transBuf minus the boundary index. -c.query; - + c.query; +}.play; +) //play slice number 3 ( { @@ -164,7 +168,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3); + FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp index 4942473..9533d90 100644 --- a/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufOnsetSlice.schelp @@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server @@ -74,11 +74,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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 function will be passed indices as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -93,7 +95,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5); + FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5).wait; (Main.elapsedTime - t).postln; }.play ) @@ -134,7 +136,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001); + FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp index 4caac6a..99cb9d8 100644 --- a/release-packaging/HelpSource/Classes/FluidBufPitch.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufPitch.schelp @@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the pitch descriptor to be calculated on a given source buffer. ARGUMENT:: server @@ -44,7 +44,7 @@ ARGUMENT:: algorithm TABLE:: ## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC). ## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 :: - ## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications.” QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html +## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications." QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html :: ARGUMENT:: minFreq @@ -65,11 +65,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -87,7 +89,7 @@ b.play; b.plot; ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c); + FluidBufPitch.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -122,7 +124,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1); + FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1).wait; (Main.elapsedTime - t).postln; }.play ) @@ -143,7 +145,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).asInteger, 2)})}); + FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufScale.schelp b/release-packaging/HelpSource/Classes/FluidBufScale.schelp index e24a528..a57c4a3 100644 --- a/release-packaging/HelpSource/Classes/FluidBufScale.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufScale.schelp @@ -13,7 +13,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the scaling to be calculated on a given source buffer. ARGUMENT:: server @@ -49,40 +49,61 @@ The output value when the input is inputLow ARGUMENT:: outputHigh The output value when the input is inputHigh +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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. -RETURNS:: -Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: code:: -// make a buffer of know qualities -b = Buffer.sendCollection(s,1.0.series(1.1,2.0)) -// and a destination buffer -c = Buffer(s) -// play with the scaling -FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10) -// retrieve the buffer and enjoy the results. -c.getn(0,10,{|x|x.postln;}) +( +Routine{ + // make a buffer of known qualities + b = Buffer.sendCollection(s,1.0.series(1.1,2.0)); + // and a destination buffer + c = Buffer(s); + // play with the scaling + FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10).wait; + // retrieve the buffer and enjoy the results. + c.getn(0,10,{|x|x.postln;}) +}.play +) // also works in multichannel - explore the following buffer -b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2) -b.plot.plotMode_(\points) + //process -FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1) -//enjoy - same shape, different range -c.plot.plotMode_(\points) +( +Routine{ + b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2); + c = Buffer(s); + s.sync; + defer{b.plot(bounds:Rect(400,400,400,400)).plotMode_(\points).bounds}; + FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1).wait; + //enjoy - same shape, different range + defer{c.plot(bounds:Rect(800,400,400,400)).plotMode_(\points)}; +}.play; +) //also works with a subset of the input, resizing the output -b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3) -b.plot(separately: true).plotMode_(\points) -//process -FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1) -//enjoy -c.plot(separately: true).plotMode_(\points) -c.query -c.getn(0,4,{|x|x.postln;}) +( +Routine{ + b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3); + c = Buffer(s); + s.sync; + defer{b.plot(separately: true,bounds:Rect(400,400,400,400)).plotMode_(\points)}; + //process + FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1).wait; + //enjoy + c.query; + c.getn(0,4,{|x|x.postln;}); + defer{c.plot(separately: true,bounds:Rect(800,400,400,400)).plotMode_(\points)}; +}.play +) :: + + diff --git a/release-packaging/HelpSource/Classes/FluidBufSines.schelp b/release-packaging/HelpSource/Classes/FluidBufSines.schelp index 152d651..35d07f1 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSines.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSines.schelp @@ -19,7 +19,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the sinusoidal estimation to be calculated on a given source buffer and to be resynthesised. ARGUMENT:: server @@ -82,11 +82,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument. -RETURNS:: - Nothing, as the various destination buffers are declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -102,7 +104,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: c, residual:d); + FluidBufSines.process(s, b, sines: c, residual:d).wait; (Main.elapsedTime - t).postln; }.play ) @@ -135,7 +137,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384); + FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp index e85d93f..4f49ccd 100644 --- a/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufSpectralShape.schelp @@ -31,7 +31,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer. ARGUMENT:: server @@ -64,11 +64,13 @@ ARGUMENT:: hopSize ARGUMENT:: fftSize The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -83,7 +85,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufSpectralShape.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -113,7 +115,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufSpectralShape.process(s, b, features: c); + FluidBufSpectralShape.process(s, b, features: c).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufStats.schelp b/release-packaging/HelpSource/Classes/FluidBufStats.schelp index 8f378ad..74ac2e6 100644 --- a/release-packaging/HelpSource/Classes/FluidBufStats.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufStats.schelp @@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server @@ -61,11 +61,13 @@ ARGUMENT:: weights ## weights must be positive (anything lower than 0 will be rejected) :: +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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 function will be passed stats as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -73,7 +75,7 @@ STRONG::A didactic example:: CODE:: -// make a buffer of known lenght +// make a buffer of known length b = Buffer.alloc(s,101); // add known values - here, a ramp up @@ -86,7 +88,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufStats.process(s, b, stats:c, numDerivs:1); + FluidBufStats.process(s, b, stats:c, numDerivs:1).wait; (Main.elapsedTime - t).postln; }.play ) @@ -144,11 +146,13 @@ g= Array.new; Routine({ e.doAdjacentPairs({ arg start,end; - 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).asInteger,((end-start)/512).max(2).asInteger, array[12]); - })}); + 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).asInteger,((end-start)/512 ).max(2).asInteger, array[12]);}) + } + ).wait; }); "Done".postln; }).play; @@ -182,10 +186,11 @@ c = Buffer.new(s); // run the stats and send back the values FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})}); +OSCFunc.allEnabled //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});"".postln; +d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t", "kurtosis\t\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; :: STRONG::Outliers and Weights:: diff --git a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp index ea798a9..d8e6f22 100644 --- a/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufThreadDemo.schelp @@ -12,7 +12,7 @@ The process will, after waiting for STRONG::time:: millisecond, return its delay CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the job to be done. In this case, simply waiting STRONG::time:: millisecond before writing a value in the destination buffer. ARGUMENT:: server @@ -24,11 +24,13 @@ ARGUMENT:: result ARGUMENT:: time The duration in milliseconds of the delay that the background thread will wait for before yielding the value to the destination buffer. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A function that will be executed upon completion. It is passed the destination buffer as argument. -RETURNS:: - The instance of FluidNRTProcess which can be used to cancel the job. +returns::The instance of FluidNRTProcess which can be used to cancel the job. METHOD:: kr @@ -43,8 +45,7 @@ ARGUMENT:: time ARGUMENT:: doneAction An integer representing an action to be executed when the process is finished. This can be used to free the enclosing synth, etc. See link::Classes/Done:: for more detail. -RETURNS:: - It report the approximate job progress, from 0 to 1. +returns::It report the approximate job progress, from 0 to 1. EXAMPLES:: @@ -56,11 +57,11 @@ CODE:: b=Buffer.alloc(s,1); // a simple call, where we query the destination buffer upon completion with the action message. -FluidBufThreadDemo.process(s, b, 1000, {|x|x.get(0,{|y|y.postln});}); +FluidBufThreadDemo.process(s, b, 1000, action:{|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 //it stops silently for now but check the synth count going down by 1. +// as the 'process' returns its instance, we can cancel the process easily +c = FluidBufThreadDemo.process(s, b, 100000, action: {|x|x.get(0,{|y|y.postln});}); +c.cancel; // 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/FluidBufThresh.schelp b/release-packaging/HelpSource/Classes/FluidBufThresh.schelp index 74cc5b9..172f641 100644 --- a/release-packaging/HelpSource/Classes/FluidBufThresh.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufThresh.schelp @@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the thresholding to be calculated on a given source buffer. ARGUMENT:: server @@ -41,11 +41,13 @@ The index of the buffer to use as the destination for the processed material. ARGUMENT:: threshold The threshold under which values will be zeroed +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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. -RETURNS:: -Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: diff --git a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp index 709a058..7bbc53f 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransientSlice.schelp @@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the slicing to be calculated on a given source buffer. ARGUMENT:: server @@ -65,11 +65,13 @@ ARGUMENT:: clumpLength ARGUMENT:: minSliceLength The minimum duration of a slice in samples. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + 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 function will be passed indices as an argument. -RETURNS:: - Nothing, as the destination buffer is declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -85,7 +87,7 @@ c = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices:c); + FluidBufTransientSlice.process(s,b, indices:c).wait; (Main.elapsedTime - t).postln; }.play ) @@ -111,7 +113,7 @@ c.query; ( Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410); + FluidBufTransientSlice.process(s,b, 0, 220500, 0, 1, c, 200, 2048, 1024, 1, 3, 1, 15, 30, 4410).wait; (Main.elapsedTime - t).postln; }.play ) @@ -138,7 +140,7 @@ c = Buffer.new(s); // with basic params Routine{ t = Main.elapsedTime; - FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2); + FluidBufTransientSlice.process(s,b, indices: c, threshFwd: 1.2).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp index 4f71858..fc427a0 100644 --- a/release-packaging/HelpSource/Classes/FluidBufTransients.schelp +++ b/release-packaging/HelpSource/Classes/FluidBufTransients.schelp @@ -18,7 +18,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q CLASSMETHODS:: -METHOD:: process +METHOD:: process, processBlocking This is the method that calls for the transient extraction to be performed on a given source buffer. ARGUMENT:: server @@ -69,11 +69,13 @@ ARGUMENT:: windowSize ARGUMENT:: clumpLength The window size in sample within which positive detections will be clumped together to avoid overdetecting in time. +ARGUMENT:: freeWhenDone +Free the server instance when processing complete. Default true + ARGUMENT:: action A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [transients, residual] as an argument. -RETURNS:: - Nothing, as the various destination buffers are declared in the function call. +returns:: an instance of the processor EXAMPLES:: @@ -89,7 +91,7 @@ d = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s,b, transients:c, residual:d); + FluidBufTransients.process(s,b, transients:c, residual:d).wait; (Main.elapsedTime - t).postln; }.play ); @@ -106,7 +108,7 @@ d.play; ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s,b, 0, 220500, 0, 1, c, d, 200, 2048, 1024, 1, 3, 1, 15, 30); + FluidBufTransients.process(s,b, 0, 220500, 0, 1, c, d, 200, 2048, 1024, 1, 3, 1, 15, 30).wait; (Main.elapsedTime - t).postln; }.play ) @@ -136,7 +138,7 @@ d = Buffer.new(s); e = Buffer.new(s); ( Routine{ t = Main.elapsedTime; - FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40); + FluidBufTransients.process(s, b, transients: d, residual:e, threshFwd:1.2, clumpLength:40).wait; (Main.elapsedTime - t).postln; }.play ) diff --git a/release-packaging/HelpSource/Classes/FluidDataSet.schelp b/release-packaging/HelpSource/Classes/FluidDataSet.schelp index a0029ce..4e6818e 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSet.schelp @@ -82,15 +82,17 @@ CODE:: s.boot; ( fork{ - ~ds = FluidDataSet.new(s,\simple1d_1); + ~ds = FluidDataSet.new(s); ~point = Buffer.alloc(s,1,1); s.sync; 10.do{|i| ~point.set(0,i); ~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); - s.sync; + s.sync; }; - ~ds.dump; s.sync; ~ds.free; + ~ds.dump; + s.sync; + ~ds.free; }; ) @@ -100,39 +102,39 @@ 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; + ~ds = FluidDataSet.new(s); s.sync; + ~ds.load(d); s.sync; + ~ds.dump; s.sync; ~ds.free; } ) -// Using synth +// Using a synth ( -~ds = FluidDataSet.new(s,\simple1d_3); +~ds = FluidDataSet.new(s); { - var trig = Impulse.kr(20); - 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}} + var trig = Impulse.kr(20); + var count = PulseCount.kr(trig) - 1; + var buf = LocalBuf(1); + BufWr.kr(count, buf); + FluidDataSetWr.kr(~ds.asUGenInput, buf: buf, trig: trig); + FreeSelf.kr(count - 8); +}.play.onFree{~ds.dump{|o| o.postln;~ds.free}} ) :: STRONG:: Merging Datasets:: - +s.dumpOSC code:: //this is how to add items between 2 datasets. //create 2 datasets ( -~dsA = FluidDataSet.new(s,\simple1d_4a); -~dsB = FluidDataSet.new(s,\simple1d_4b); +~dsA = FluidDataSet.new(s); +~dsB = FluidDataSet.new(s); ) - +Dictionary.new //feed them items with same dimensions but different labels -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\one,[1],\two,[2]])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[3],\four,[4]])])); +~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\one,1,\two,2])])); +~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); ~dsA.print; ~dsB.print; @@ -141,8 +143,8 @@ code:: ~dsB.print; //change the content of the dataset to shared labels -~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[333],\four,[444]])])); -~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,[3],\four,[4]])])); +~dsA.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,333,\four,444])])); +~dsB.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\three,3,\four,4])])); ~dsA.print; ~dsB.print; diff --git a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp index 932443e..9b1d57d 100644 --- a/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp +++ b/release-packaging/HelpSource/Classes/FluidDataSetQuery.schelp @@ -108,10 +108,8 @@ EXAMPLES:: code:: s.reboot; - - // Create a DataSet with random data -~dataSet= FluidDataSet(s,\help_fluid_dataset_query); +~dataSet= FluidDataSet(s); ( ~points = 100.collect{|i|5.collect{|j|j+(i/100)}}; @@ -122,8 +120,9 @@ fork{ s.sync; ~points.do{|x,i| ~tmpbuf.setn(0,x); + s.sync; ~dataSet.addPoint(i,~tmpbuf); - s.sync + // s.sync } } ) @@ -133,11 +132,12 @@ fork{ // Prepare a FluidDataSetQuery object ~query = FluidDataSetQuery.new; -~out = FluidDataSet(s,\help_fluid_dataset_query_out); +~out = FluidDataSet(s); // prepare a simple query ~query.filter(0,"<",0.04); ~query.addColumn(2); +s.dumpOSC ~query.transform(~dataSet, ~out); // check the result diff --git a/release-packaging/HelpSource/Classes/FluidKDTree.schelp b/release-packaging/HelpSource/Classes/FluidKDTree.schelp index f639ca8..cb25c2f 100644 --- a/release-packaging/HelpSource/Classes/FluidKDTree.schelp +++ b/release-packaging/HelpSource/Classes/FluidKDTree.schelp @@ -48,8 +48,6 @@ A function that will run when the query returns, whose argument is an array of d EXAMPLES:: code:: - - // Make a DataSet of random 2D points s.reboot; ( @@ -117,13 +115,40 @@ fork{ ) :: -subsection:: Server Side Queries +subsection:: Queries in a Synth + +Input and output is done via buffers, which will need to be preallocated to the correct sizes: +LIST:: +##Your input buffer should be sized to the input data dimension (2, in this example) +##Your output buffer should be the number of neighbours * output dimensionality +:: + +We can't simply return labels (i.e. strings) from a UGen, so the query returns the actual data points from a DataSet instead. By default, this is the FluidDataSet against which the tree was fitted. However, by passing a different dataset to code::kr::'s code::lookupDataSet:: argument instead, you can return different points, so long as the labels in the two datasets match. In this way, the FluidKDTree can be used to perform nearest neighbour mappings in a synth. + +For instance, whilst fitting the tree against some n-dimensional descriptor data, our lookup dataset could use the same labels to map descriptor entries back to buffers, or locations in buffers, so that queries can be used to trigger audio. code:: -//populate the lookupDataSet + + ,( +Routine{ + var inputBuffer = Buffer.alloc(s,2); + var outputBuffer = Buffer.alloc(s,10);//5 neighbours * 2D data points + s.sync; + { + var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 + var point = 2.collect{TRand.kr(0,1,trig)}; + point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; + ~tree.kr(trig,inputBuffer,outputBuffer,5,nil); + Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(5)),5.collect{|i| "Neighbour" + i}); + Silent.ar; + }.play; +}.play; +) + +//Using a lookup data set instead: //here we populate with numbers that are in effect the indicies, but it could be anything numerical that will be returned on the server-side and would be usable on that side ( -~dsL = FluidDataSet.new(s,\kdtree_help_indices); +~dsL = FluidDataSet.new(s); fork{ d = Dictionary.with( *[\cols -> 1,\data -> Dictionary.newFrom( @@ -133,30 +158,21 @@ fork{ } ) -// instantiate a tree with a lookup dataset. Note that this 'association' has to be done at instantiation. -~tree = FluidKDTree(s,numNeighbours:5, lookupDataSet:~dsL); -~tree.fit(~ds) - -//set the buffers and busses needed -( -~inputPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,5); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - +// Create the buffers, and make a synth, querying our tree with some random points ( -~tree.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); -{ - var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 - var point = 2.collect{TRand.kr(0,1,trig)}; - point.collect{|p,i| BufWr.kr([p],~inputPoint,i)}; - Poll.kr(trig,point); - Out.kr(~pitchingBus.index,[trig]); - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,Array.iota(5))); - Silent.ar; -}.play(~tree.synth,addAction:\addBefore); - +Routine{ + var inputBuffer = Buffer.alloc(s,2); + var outputBuffer = Buffer.alloc(s,5);//5 neighbours * 1D points + s.sync; + { + var trig = Impulse.kr(4); //can go as fast as ControlRate.ir/2 + var point = 2.collect{TRand.kr(0,1,trig)}; + point.collect{|p,i| BufWr.kr([p],inputBuffer,i)}; + ~tree.kr(trig,inputBuffer,outputBuffer,5,~dsL); + Poll.kr(trig, BufRd.kr(1,outputBuffer,Array.iota(5)),5.collect{|i| "Neighbour" + i}); + Silent.ar; + }.play; +}.play; ) :: diff --git a/release-packaging/HelpSource/Classes/FluidKMeans.schelp b/release-packaging/HelpSource/Classes/FluidKMeans.schelp index 32f13e7..547a21d 100644 --- a/release-packaging/HelpSource/Classes/FluidKMeans.schelp +++ b/release-packaging/HelpSource/Classes/FluidKMeans.schelp @@ -87,7 +87,7 @@ fork{ // Create a KMeans instance and a LabelSet for the cluster labels in the server -~clusters = FluidLabelSet(s,\kmeans_help_clusters); +~clusters = FluidLabelSet(s); ~kmeans = FluidKMeans(s); // Fit into 4 clusters @@ -101,10 +101,15 @@ fork{ ) // Cols of kmeans should match DataSet, size is the number of clusters + ~kmeans.cols; ~kmeans.size; ~kmeans.dump; + ~clusters.getLabel(0,{|clusterID| + (0.asString+clusterID).postln; +}); + // Retrieve labels of clustered points ( ~assignments = Array.new(128); @@ -147,57 +152,38 @@ w.front; ~kmeans.predictPoint(~inbuf,{|x|x.postln;}); :: -subsection:: Server Side Queries +subsection:: Queries in a Synth This is the equivalent of predictPoint, but wholly on the server -FluidKMeans is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer + code:: -( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~tempPoint = Buffer.alloc(s,1,2); -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus +outputPoint.getToFloatArray(action:{|a|a.postln}) ( -//Set properties on FluidKMeans: -~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching -{ - var trig = Impulse.kr(5); - var point = WhiteNoise.kr(1.dup); - var copied; - Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~kmeans.synth,addAction:\addBefore); -//catching { - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~kmeans.synth,addAction:\addAfter); + var trig = Impulse.kr(5); + var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + Poll.kr(trig, point, [\pointX,\pointY]); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~kmeans.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); +}.play; ) // to sonify the output, here are random values alternating quadrant, generated more quickly as the cursor moves rightwards ( -//Set properties on FluidKMeans: -~kmeans.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir / 2)); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(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); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~kmeans.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1); +}.play; ) + :: diff --git a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp index 017c452..9f882a1 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNClassifier.schelp @@ -57,12 +57,12 @@ code:: ( ~classifier = FluidKNNClassifier(s); -~source= FluidDataSet(s,\knnclassify_help_examples); -~labels = FluidLabelSet(s,\knnclassify_help_labels); -~test = FluidDataSet(s,\knnclassify_help_test); -~mapping = FluidLabelSet(s,\knnclassify_help_mapping); +~source= FluidDataSet(s); +~labels = FluidLabelSet(s); +~test = FluidDataSet(s); +~mapping = FluidLabelSet(s); ) - +s.dumpOSC //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]]; @@ -149,52 +149,33 @@ w.front; subsection::Server Side Queries This is the equivalent of predictPoint, but wholly on the server -FluidKNNClassifier is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer code:: -( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus +//Generate a random point and sends a trigger to query, and return the class that point matches ( -//Set properties on FluidKNNClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(5); var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~classifier.synth,addAction:\addAfter); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); +}.play; ) // to sonify the output, here are random values alternating quadrant. ( -//Set properties on FluidKNNClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig:2, label: "Query Frequency")); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(trig)*0.1; -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); -}.play(~classifier.synth,addAction:\addAfter); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1); +}.play ) :: diff --git a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp index b138593..9bde42d 100644 --- a/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidKNNRegressor.schelp @@ -53,10 +53,10 @@ 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); +~source = FluidDataSet(s); +~target = FluidDataSet(s); +~test = FluidDataSet(s); +~output = FluidDataSet(s); ~tmpbuf = Buffer.alloc(s,1); ~regressor = FluidKNNRegressor(s); ) @@ -106,7 +106,7 @@ d = Dictionary.with( //We should see a single cycle of a chirp ~outputdata.plot; - +s.dumpOSC // single point transform on arbitrary value ~inbuf = Buffer.loadCollection(s,[0.5]); ~regressor.predictPoint(~inbuf,{|x|x.postln;}); @@ -117,28 +117,15 @@ subsection:: Server Side Queries code:: //Setup ( -~inputPoint = Buffer.alloc(s,1); -~predictPoint = Buffer.alloc(s,1); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - -( -~regressor.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - -~inputSynth = { +{ var input = Saw.kr(2).linlin(-1,1,0,1); var trig = Impulse.kr(ControlRate.ir/10); - BufWr.kr(input,~inputPoint,0); - Out.kr(~pitchingBus.index,[trig]); -}; - -~inputSynth.play(~regressor.synth,addAction:\addBefore); - -~outputSynth = { - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,0),"mapped value") -}; -~outputSynth.play(~regressor.synth,addAction:\addAfter); -~outputSynth.scope + var inputPoint = LocalBuf(1); + var outputPoint = LocalBuf(1); + BufWr.kr(input,inputPoint,0); + ~regressor.kr(trig,inputPoint,outputPoint); + BufRd.kr(1,outputPoint,0);//,"mapped value") +}.scope ) + :: diff --git a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp index 8170ce1..a29d3d6 100644 --- a/release-packaging/HelpSource/Classes/FluidLabelSet.schelp +++ b/release-packaging/HelpSource/Classes/FluidLabelSet.schelp @@ -56,7 +56,7 @@ Post an abbreviated content of the label set in the window by default, but you c EXAMPLES:: code:: -~ls = FluidLabelSet.new(s,\labelset_example); +~ls = FluidLabelSet.new(s); ["one", "two", "three"].collect{|x,i| ~ls.addLabel(i, x);}; ~ls.print; diff --git a/release-packaging/HelpSource/Classes/FluidMDS.schelp b/release-packaging/HelpSource/Classes/FluidMDS.schelp index 0107f2e..914098e 100644 --- a/release-packaging/HelpSource/Classes/FluidMDS.schelp +++ b/release-packaging/HelpSource/Classes/FluidMDS.schelp @@ -63,9 +63,9 @@ 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); +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); ~audio = Buffer.read(s,~audiofile); ~mfcc_feature = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 12); @@ -73,7 +73,6 @@ code:: ~mds = FluidMDS(s); ) - // Load audio and run an mfcc analysis, which gives us 13 points (we'll throw the 0th away) ( ~audio = Buffer.read(s,~audiofile); @@ -90,16 +89,16 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); 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 + startChan:1, numFrames: chunkLen, stats: ~stats, trig: trig,blocking:1 ); 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); + dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats),blocking:1); + LocalOut.kr(Done.kr(dsWr)); + FreeSelf.kr(count - 99); Poll.kr(trig,count); }.play; ) @@ -112,9 +111,9 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); ~reducedarray = Array.new(100); ~standardizer.fitTransform(~raw, ~standardized); ~mds.fitTransform(~standardized, ~reduced, action:{ - ~reduced.dump{|x| 100.do{|i| - ~reducedarray.add(x["data"][i.asString]) - }}}); + ~reduced.dump{|x| 100.do{|i| + ~reducedarray.add(x["data"][i.asString]) +}}}); ) //Visualise the 2D projection of our original 12D data @@ -175,5 +174,4 @@ w.drawFunc = { w.refresh; w.front; ) - :: diff --git a/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp b/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp index 68f9074..6522cd9 100644 --- a/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp +++ b/release-packaging/HelpSource/Classes/FluidMLPClassifier.schelp @@ -86,11 +86,11 @@ EXAMPLES:: code:: ( -~classifier = FluidMLPClassifier(s, [6], FluidMLPClassifier.tanh, 1000, 0.1, 0.1, 50, 0); -~sourcedata= FluidDataSet(s,\mlpclassify_help_examples); -~labels = FluidLabelSet(s,\mlpclassify_help_labels); -~testdata = FluidDataSet(s,\mlpclassify_help_test); -~predictedlabels = FluidLabelSet(s,\mlpclassify_help_mapping); +~classifier=FluidMLPClassifier(s).hidden_([6]).activation_(FluidMLPClassifier.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(50).validation_(0); +~sourcedata= FluidDataSet(s); +~labels = FluidLabelSet(s); +~testdata = FluidDataSet(s); +~predictedlabels = FluidLabelSet(s); ) //Make some clumped 2D points and place into a DataSet ( @@ -104,8 +104,8 @@ code:: ~labeldata.put("mlpclass"++i++\_++j,[~categories[i]]); } }; -~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset])); -~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata])); +~sourcedata.load(Dictionary.with(*[\cols->2,\data->~trainingset]),action:{~sourcedata.print}); +~labels.load(Dictionary.with(*[\cols->1,\data->~labeldata]),action:{~labels.print}); ) //Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set @@ -119,14 +119,16 @@ code:: ~testset.put("mlpclass_test"++i++\_++j, ~centroids[i].collect{|x| x.gauss(0.5/3)}); } }; -~testdata.load(Dictionary.with(*[\cols->2,\data->~testset])); +~testdata.load(Dictionary.with(*[\cols->2,\data->~testset]),action:{~testdata.print}); ) //Run the test data through the network, into the predicted labelset ~classifier.predict(~testdata,~predictedlabels,action:{"Test complete".postln}); - +OSCFunc.trace(true,true) +OSCFunc.allEnabled //get labels from server -~predictedlabels.dump(action:{|d| ~labelsdict = d["data"]}); +~predictedlabels.dump(action:{|d|~labelsdict = d["data"]};~labelsdict.postln); + //Visualise: we're hoping to see colours neatly mapped to quandrants... ( c = Dictionary(); @@ -155,56 +157,34 @@ w.front; ~inbuf = Buffer.loadCollection(s,0.5.dup); ~classifier.predictPoint(~inbuf,{|x|x.postln;}); :: - - -subsection::Server Side Queries -This is the equivalent of predictPoint, but wholly on the server -FluidMLPClassifier is accessed via its own synth, so we need to use -a bus to communicate with it. The inBus receives a trigger to query, using data -from inBuffer; a trigger is then send to outBus with the prediction in outBuffer +subsection::Querying in a Synth +This is the equivalent of code::predictPoint::, but wholly on the server code:: ( -~ib = Bus.control(s); // input bus -~ob = Bus.control(s); //output bus -~inpPoint = Buffer.alloc(s,2); -~outPoint = Buffer.alloc(s,1); -) - -//We make two Synths. One, before FluidKMeans, generates a random point and sends -//a trigger to query. The second, after FluidKMeans, gives us the predicted cluster //triggering upadtes from the outBus -( -//Set properties on FluidMLPClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(5); var point = WhiteNoise.kr(1.dup); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); Poll.kr(trig, point, [\pointX,\pointY]); - point.collect{ |p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - Poll.kr(In.kr(~ob),Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)),\cluster); -}.play(~classifier.synth,addAction:\addAfter); + point.collect{ |p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,0,interpolation:0),\cluster); + Silent.ar; +}.play; ) // to sonify the output, here are random values alternating quadrant. ( -//Set properties on FluidMLPClassifier: -~classifier.inBus_(~ib).outBus_(~ob).inBuffer_(~inpPoint).outBuffer_(~outPoint); -//pitching { var trig = Impulse.kr(MouseX.kr(0,1).exprange(0.5,ControlRate.ir /2).poll(trig: 2,label: "Query Frequency")); var step = Stepper.kr(trig,max:3); var point = TRand.kr(-0.1, [0.1, 0.1], trig) + [step.mod(2).linlin(0,1,-0.6,0.6),step.div(2).linlin(0,1,-0.6,0.6)] ; - point.collect{|p,i| BufWr.kr([p],~inpPoint,i)}; - Out.kr(~ib.index,[trig]); - T2A.ar(trig)*0.1; -}.play(~classifier.synth,addAction:\addBefore); -//catching -{ - SinOsc.ar((Latch.kr(BufRd.kr(1,~outPoint,0,interpolation:0),In.kr(~ob)) + 69).midicps,mul: 0.1); -}.play(~classifier.synth,addAction:\addAfter); + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(1); + point.collect{|p,i| BufWr.kr([p],inputPoint,i)}; + ~classifier.kr(trig,inputPoint,outputPoint); + SinOsc.ar((BufRd.kr(1,outputPoint,0,interpolation:0) + 69).midicps,mul: 0.1) +}.play; ) :: \ No newline at end of file diff --git a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp b/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp index a2d895f..2b8a8c0 100644 --- a/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp +++ b/release-packaging/HelpSource/Classes/FluidMLPRegressor.schelp @@ -101,12 +101,12 @@ code:: //Make a simple mapping between a ramp and a sine cycle, test with an exponentional ramp ( -~source = FluidDataSet(s,\mlp_regressor_source); -~target = FluidDataSet(s,\mlp_regressor_target); -~test = FluidDataSet(s,\mlp_regressor_dest); -~output = FluidDataSet(s,\mlp_regress_out); +~source = FluidDataSet(s); +~target = FluidDataSet(s); +~test = FluidDataSet(s); +~output = FluidDataSet(s); ~tmpbuf = Buffer.alloc(s,1); -~regressor = FluidMLPRegressor(s,[2], FluidMLPRegressor.tanh, FluidMLPRegressor.tanh, maxIter: 1000, learnRate: 0.1, momentum: 0.1, batchSize: 1, validation: 0); +~regressor=FluidMLPRegressor(s).hidden_([2]).activation_(FluidMLPRegressor.tanh).outputActivation_(FluidMLPRegressor.tanh).maxIter_(1000).learnRate_(0.1).momentum_(0.1).batchSize_(1).validation_(0); ) //Make source, target and test data @@ -153,10 +153,11 @@ d = Dictionary.with( ( ~outputdata = Array(128); ~regressor.predict(~test, ~output, action:{ - ~output.dump{|x| 128.do{|i| + ~output.dump{|x| 128.do{|i| ~outputdata.add(x["data"][i.asString][0]) }}; -}); +} +); ) //We should see a single cycle of a chirp. If not, fit a little more epochs @@ -166,37 +167,23 @@ d = Dictionary.with( ~inbuf = Buffer.loadCollection(s,[0.5]); ~outbuf = Buffer.new(s); ~regressor.predictPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,1,{|y|y.postln;};)}); - :: -subsection:: Server Side Queries +subsection:: Querying in a Synth -code:: -//Setup -( -~inputPoint = Buffer.alloc(s,1); -~predictPoint = Buffer.alloc(s,1); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) +This is the equivalent of calling code::predictPoint::, except wholly on the server +code:: ( -~regressor.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - -~inputSynth = { +{ var input = Saw.kr(2).linlin(-1,1,0,1); var trig = Impulse.kr(ControlRate.ir/10); - BufWr.kr(input,~inputPoint,0); - Out.kr(~pitchingBus.index,[trig]); -}; - -~inputSynth.play(~regressor.synth,addAction:\addBefore); - -~outputSynth = { - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,0),"mapped value") -}; -~outputSynth.play(~regressor.synth,addAction:\addAfter); -~outputSynth.scope + var inputPoint = LocalBuf(1); + var outputPoint = LocalBuf(1); + BufWr.kr(input,inputPoint,0); + ~regressor.kr(trig,inputPoint,outputPoint,0,-1); + Poll.kr(trig,BufRd.kr(1,outputPoint,0),"mapped value"); +}.scope; ) diff --git a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp index fbe8375..db9c268 100644 --- a/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp +++ b/release-packaging/HelpSource/Classes/FluidNMFFilter.schelp @@ -79,8 +79,7 @@ d.play //////(beware !!!! loud!!!) ( // separate them in 2 components Routine { - FluidBufNMF.process(s, d, bases: e, components:2); - s.sync; + FluidBufNMF.process(s, d, bases: e, components:2).wait; e.query; }.play ) @@ -112,7 +111,7 @@ c = Buffer.new(s); // train only 2 seconds ( Routine { - FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048); + FluidBufNMF.process(s,b,0,88200,0,1, c, ~bases, components:10,fftSize:2048).wait; c.query; }.play; ) @@ -120,14 +119,15 @@ Routine { // wait for the query to print // find the component that has the picking sound checking the median spectral centroid ( -FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ - |shapes|FluidBufStats.process(s,shapes,stats:~stats, action:{ - |stats|stats.getn(0, (stats.numChannels * stats.numFrames) ,{ - |x| ~centroids = x.select({ - |item, index| (index.mod(7) == 0) && (index.div(70) == 5); - }) - }) - }) +FluidBufSpectralShape.process(s, c, features: ~spectralshapes, action:{ |shapes| + FluidBufStats.process(s,shapes,stats:~stats, action:{|stats| + stats.getn(0, (stats.numChannels * stats.numFrames), {|x| + ~centroids = x.select({ + |item, index| (index.mod(7) == 0) && (index.div(70) == 5); + }); + ~centroids.postln; + }) + }) }); ) diff --git a/release-packaging/HelpSource/Classes/FluidNormalize.schelp b/release-packaging/HelpSource/Classes/FluidNormalize.schelp index 6e2724c..9fce5bb 100644 --- a/release-packaging/HelpSource/Classes/FluidNormalize.schelp +++ b/release-packaging/HelpSource/Classes/FluidNormalize.schelp @@ -92,18 +92,18 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); var chunkLen = (~pitch_feature.numFrames / 10).asInteger; var stats = FluidBufStats.kr( source: ~pitch_feature, startFrame: count * chunkLen, - numFrames: chunkLen, stats: ~stats, trig: trig + numFrames: chunkLen, stats: ~stats, trig: (trig * (count <=9)), blocking:1 ); 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)); + var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); + Poll.kr(trig,count,\count); FreeSelf.kr(count - 9); }.play; ) - // Normalize and load to language-side array ( ~rawarray = Array.new(10); @@ -130,42 +130,28 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); ~inbuf = Buffer.loadCollection(s,0.5.dup); ~outbuf = Buffer.new(s); ~normalizer.transformPoint(~inbuf,~outbuf,{|x|x.postln;x.getn(0,2,{|y|y.postln;};)}); - +OSCFunc.trace(false,true) //Server side queries -//Setup -( -~tempPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,2); -~avgBuf = Buffer.alloc(s,100,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) ( -~normalizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint); -//Pitching (no pun intended): read frames out of buffer and pass to standardize { var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = HPZ1.ar(counter) < 0; + var trig = A2K.kr(HPZ1.ar(counter) < 0); //average 100 frames: one could use the MovingAverage extension here var avg; - BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:100.collect{|x|x})) * 0.01; + var inputPoint = LocalBuf(2); + var outputPoint = LocalBuf(2); + var avgBuf = LocalBuf(100,2); + //running average of pitch features + BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); + avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; //assemble data point - BufWr.kr(avg[0],~tempPoint,0); - BufWr.kr(avg[1],~tempPoint,1); - Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Out.kr(~pitchingBus.index,[T2K.kr(trig)]); -}.play(~normalizer.synth,addAction:\addBefore); - -{ - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"]) -}.play(~normalizer.synth,addAction:\addAfter); - + BufWr.kr(avg[0],inputPoint,0); + BufWr.kr(avg[1],inputPoint,1); + ~normalizer.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); + Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (normalized)", "confidence (normalized)"]) +}.play; ) - - - - :: diff --git a/release-packaging/HelpSource/Classes/FluidPCA.schelp b/release-packaging/HelpSource/Classes/FluidPCA.schelp index 3036bc7..3b897fb 100644 --- a/release-packaging/HelpSource/Classes/FluidPCA.schelp +++ b/release-packaging/HelpSource/Classes/FluidPCA.schelp @@ -63,9 +63,9 @@ 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); +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); ~audio = Buffer.read(s,~audiofile); ~mfcc_feature = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 12); @@ -78,7 +78,7 @@ s.boot; // 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); +FluidBufMFCC.process(s,~audio, features: ~mfcc_feature,action:{"Done MFCCs".postln}); ) // Divide the time series in 100, and take the mean of each segment and add this as a point to @@ -90,15 +90,16 @@ FluidBufMFCC.process(s,~audio, features: ~mfcc_feature); 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 + source: ~mfcc_feature, startFrame: count * chunkLen, + startChan:1, numFrames: chunkLen, stats: ~stats, + trig: trig * (count < 100), blocking: 1 ); 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)); + dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); FreeSelf.kr(count - 99); Poll.kr(trig,count); @@ -150,29 +151,19 @@ subsection:: Server Side Queries Let's map our learned PCA dimensions to the controls of a processor code:: - -( -~inputPoint = Buffer.alloc(s,12); -~predictPoint = Buffer.alloc(s,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - ( -~pca.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~inputPoint).outBuffer_(~predictPoint); - { var mapped; var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var mfcc = FluidMFCC.kr(audio)[1..12]; var smoothed = LagUD.kr(mfcc,1*ControlDur.ir,500*ControlDur.ir); var trig = Impulse.kr(ControlRate.ir / 2); - smoothed.collect{|coeff,i| BufWr.kr([coeff],~inputPoint,i)}; - Out.kr(~pitchingBus,[trig]); - mapped = Latch.kr(BufRd.kr(1,~predictPoint, phase:[0,1]).linlin(-3,3,0,3),In.kr(~catchingBus)).scope; + var inputPoint = LocalBuf(12); + var outputPoint = LocalBuf(2); + smoothed.collect{|coeff,i| BufWr.kr([coeff],inputPoint,i)}; + ~pca.kr(trig, inputPoint, outputPoint, 2); + mapped = BufRd.kr(1,outputPoint, phase:[0,1]).linlin(-3,3,0,3); CombC.ar(audio,3,mapped[0],mapped[1]*3) -}.play(~pca.synth,addAction:\addBefore); - +}.play; ) - :: diff --git a/release-packaging/HelpSource/Classes/FluidStandardize.schelp b/release-packaging/HelpSource/Classes/FluidStandardize.schelp index 4047cb4..02493c3 100644 --- a/release-packaging/HelpSource/Classes/FluidStandardize.schelp +++ b/release-packaging/HelpSource/Classes/FluidStandardize.schelp @@ -59,8 +59,8 @@ 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); +~raw = FluidDataSet(s); +~stand = FluidDataSet(s); ~audio = Buffer.read(s,~audiofile); ~pitch_feature = Buffer.new(s); ~stats = Buffer.alloc(s, 7, 2); @@ -71,10 +71,10 @@ s.boot; // 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); +FluidBufPitch.process(s,~audio, features: ~pitch_feature,action:{"Analysed Pitch".postln}); ) -// Divide the time series in to 10, and take the mean of each segment and add this as a point to +// Divide the time series in to 10, and take the mean of each s"egment and add this as a point to // the 'raw' FluidDataSet ( { @@ -83,19 +83,20 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); 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 + source: ~pitch_feature, startFrame: count * chunkLen, + numFrames: chunkLen, stats: ~stats, + trig: trig * (count < 10), blocking: 1 ); 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)); + var dsWr = FluidDataSetWr.kr(~raw, buf: buf, trig: Done.kr(stats)); LocalOut.kr( Done.kr(dsWr)); FreeSelf.kr(count - 9); + Poll.kr(trig,count, \count); }.play; ) - // Standardize and load to language-side array ( ~rawarray = Array.new(10); @@ -123,41 +124,27 @@ FluidBufPitch.process(s,~audio, features: ~pitch_feature); :: subsection::Server Side Querying - -Because FluidStandardize runs in its own link::Classes/Synth:: on the server, communication is done via control-rate link::Classes/Bus:: objects for triggering and link::Classes/Buffer:: objects for passing and retreiving data. - code:: -//Setup -( -~tempPoint = Buffer.alloc(s,2); -~predictPoint = Buffer.alloc(s,2); -~avgBuf = Buffer.alloc(s,100,2); -~pitchingBus = Bus.control; -~catchingBus = Bus.control; -) - ( -~standardizer.inBus_(~pitchingBus).outBus_(~catchingBus).inBuffer_(~tempPoint).outBuffer_(~predictPoint); -//Pitching (no pun intended): read frames out of buffer and pass to standardize +// read frames out of buffer and pass to standardize { var audio = BufRd.ar(1,~audio,LFSaw.ar(BufDur.ir(~audio).reciprocal).range(0, BufFrames.ir(~audio))); var counter = Stepper.ar(Impulse.ar(ControlRate.ir),max:99); - var trig = HPZ1.ar(counter) < 0; + var trig = A2K.kr(HPZ1.ar(counter) < 0); //average 10 frames: one could use the MovingAverage extension here var avg; - BufWr.kr(FluidPitch.kr(audio),~avgBuf,phase:counter); - avg = Mix.new(BufRd.kr(2, ~avgBuf, phase:100.collect{|x|x})) * 0.01; + var inputPoint= LocalBuf(2); + var outputPoint = LocalBuf(2); + var avgBuf = LocalBuf(100,2); + //average of pitch features + BufWr.kr(FluidPitch.kr(audio),avgBuf,phase:counter); + avg = Mix.new(BufRd.kr(2, avgBuf, phase:100.collect{|x|x})) * 0.01; //assemble data point - BufWr.kr(avg[0],~tempPoint,0); - BufWr.kr(avg[1],~tempPoint,1); - Poll.kr(T2K.kr(trig),BufRd.kr(1,~tempPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); - Out.kr(~pitchingBus.index,[T2K.kr(trig)]); -}.play(~standardizer.synth,addAction:\addBefore); - -//catching -{ - Poll.kr(In.kr(~catchingBus.index),BufRd.kr(1,~predictPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"]) -}.play(~standardizer.synth,addAction:\addAfter); - + BufWr.kr(avg[0],inputPoint,0); + BufWr.kr(avg[1],inputPoint,1); + Poll.kr(trig,BufRd.kr(1,inputPoint,[0,1]),["pitch (raw)", "confidence (raw)"]); + ~standardizer.kr(trig,inputPoint,outputPoint); + Poll.kr(trig,BufRd.kr(1,outputPoint,[0,1]),["pitch (standardized)", "confidence (standardized)"]); +}.play; ) :: diff --git a/release-packaging/HelpSource/Classes/FluidUMAP.schelp b/release-packaging/HelpSource/Classes/FluidUMAP.schelp index cef03d3..381dea1 100644 --- a/release-packaging/HelpSource/Classes/FluidUMAP.schelp +++ b/release-packaging/HelpSource/Classes/FluidUMAP.schelp @@ -47,13 +47,13 @@ code:: //Preliminaries: we want some points, a couple of FluidDataSets, a FluidStandardize and a FluidUMAP ( -~raw = FluidDataSet(s,\umap_help_3D); -~standardized = FluidDataSet(s,\umap_help_3Ds); -~reduced = FluidDataSet(s,\umap_help_2D); -~normalized = FluidDataSet(s,\umap_help_2Dn); +~raw = FluidDataSet(s); +~standardized = FluidDataSet(s); +~reduced = FluidDataSet(s); +~normalized = FluidDataSet(s); ~standardizer = FluidStandardize(s); ~normalizer = FluidNormalize(s); -~umap = FluidUMAP(s, numDimensions: 2, numNeighbours: 5, minDist: 0.2,iterations: 50, learnRate: 0.2,batchSize: 50); +~umap = FluidUMAP(s).numDimensions_(2).numNeighbours_(5).minDist_(0.2).iterations_(50). learnRate_(0.2).batchSize_(50); ) @@ -65,11 +65,11 @@ code:: ~raw.print; //First standardize our DataSet, then apply the UMAP to get 2 dimensions, then normalise these 2 for drawing in the full window size - -~standardizer.fitTransform(~raw,~standardized) -~umap.fitTransform(~standardized,~reduced) -~normalizer.fitTransform(~reduced,~normalized) - +( +~standardizer.fitTransform(~raw,~standardized,action:{"Standardized".postln}); +~umap.fitTransform(~standardized,~reduced,action:{"Finished UMAP".postln}); +~normalizer.fitTransform(~reduced,~normalized,action:{"Normalized Output".postln}); +) //we recover the reduced dataset ~normalized.dump{|x| ~normalizedDict = x["data"]}; diff --git a/scripts/target_post.cmake b/scripts/target_post.cmake index 257bee7..48da1e6 100644 --- a/scripts/target_post.cmake +++ b/scripts/target_post.cmake @@ -41,11 +41,12 @@ endif() target_link_libraries( ${PLUGIN} - PRIVATE + PRIVATE FLUID_DECOMPOSITION # FLUID_MANIP FLUID_SC_WRAPPER HISSTools_FFT + # FLUID_SC_COPYREPLYADDR ) target_include_directories( @@ -59,8 +60,15 @@ target_include_directories( file(GLOB_RECURSE FLUID_MANIPULATION_HEADERS CONFIGURE_DEPENDS "${FLUID_M_PATH}/include/**/*.hpp") +file(GLOB_RECURSE FLUID_SC_HEADERS CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/include/wrapper/*.hpp") + target_sources( ${PLUGIN} PUBLIC ${FLUID_MANIPULATION_HEADERS} + ${FLUID_SC_HEADERS} + #bah. We need to know how big a ReplyAddress struct is, and so this nonsense: + "${SC_PATH}/common/SC_Reply.cpp" + "${SC_PATH}/external_libraries/boost/libs/system/src/error_code.cpp" + # $ ) target_include_directories( @@ -74,6 +82,8 @@ target_include_directories( get_property(HEADERS TARGET FLUID_DECOMPOSITION PROPERTY INTERFACE_SOURCES) source_group(TREE "${flucoma-core_SOURCE_DIR}/include" FILES ${HEADERS}) +source_group(TREE "${FLUID_M_PATH}/include" FILES ${FLUID_MANIPULATION_HEADERS}) +source_group(TREE "${CMAKE_SOURCE_DIR}/include/wrapper" PREFIX wrapper FILES ${FLUID_SC_HEADERS}) # get_property(HEADERS TARGET FLUID_MANIP PROPERTY INTERFACE_SOURCES) # source_group(TREE "${fluid_manipulation_SOURCE_DIR}/include" FILES ${HEADERS}) diff --git a/src/FluidBufSpectralShape/FluidBufSpectralShape.cpp b/src/FluidBufSpectralShape/FluidBufSpectralShape.cpp index adda5b1..09685d8 100644 --- a/src/FluidBufSpectralShape/FluidBufSpectralShape.cpp +++ b/src/FluidBufSpectralShape/FluidBufSpectralShape.cpp @@ -17,5 +17,5 @@ static InterfaceTable *ft; PluginLoad(OfflineFluidDecompositionUGens) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidBufSpectralShape", ft); + makeSCWrapper("FluidBufSpecShp", ft); } diff --git a/src/FluidBufTransientSlice/FluidBufTransientSlice.cpp b/src/FluidBufTransientSlice/FluidBufTransientSlice.cpp index 520b066..7354e08 100644 --- a/src/FluidBufTransientSlice/FluidBufTransientSlice.cpp +++ b/src/FluidBufTransientSlice/FluidBufTransientSlice.cpp @@ -17,5 +17,5 @@ static InterfaceTable* ft; PluginLoad(OfflineFluidDecompositionUGens) { ft = inTable; using namespace fluid::client; - makeSCWrapper("FluidBufTransientSlice", ft); + makeSCWrapper("FluidBufTrSlice", ft); } diff --git a/src/FluidManipulation/FluidManipulation.cpp b/src/FluidManipulation/FluidManipulation.cpp index c98d0eb..65b6540 100644 --- a/src/FluidManipulation/FluidManipulation.cpp +++ b/src/FluidManipulation/FluidManipulation.cpp @@ -38,7 +38,7 @@ PluginLoad(FluidSTFTUGen) makeSCWrapper("FluidMDS",ft); makeSCWrapper("FluidUMAP",ft); makeSCWrapper("FluidAudioTransport",ft); - makeSCWrapper("FluidBufAudioTransport",ft); + makeSCWrapper("FluidBufAudioTransp",ft); makeSCWrapper("FluidDataSetWr", ft); makeSCWrapper("FluidMLPRegressor",ft); makeSCWrapper("FluidMLPClassifier",ft);