diff --git a/CMakeLists.txt b/CMakeLists.txt index 79bd49f..caab9fd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.3) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -mavx -msse -msse2 -msse3 -msse4") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++") @@ -30,6 +30,8 @@ MACRO(SUBDIRLIST result curdir) ENDMACRO() set(FLUID_PATH ~/fluid_decomposition CACHE PATH "The top level of the fluid_decomposition repo") +set(LOCAL_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/include) + get_filename_component(FLUID_ABS_PATH ${FLUID_PATH} ABSOLUTE) message(${FLUID_ABS_PATH}) @@ -59,6 +61,13 @@ if(APPLE OR WIN32) set(CMAKE_SHARED_MODULE_SUFFIX ".scx") endif() +add_library(FLUID_SC_WRAPPER INTERFACE) +target_sources(FLUID_SC_WRAPPER +INTERFACE +${CMAKE_CURRENT_SOURCE_DIR}/include/FluidSCWrapper.hpp +${CMAKE_CURRENT_SOURCE_DIR}/include/SCBufferAdaptor.hpp +) + SUBDIRLIST(PROJECT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src) foreach (project_dir ${PROJECT_DIRS}) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/${project_dir}/CMakeLists.txt") diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp new file mode 100644 index 0000000..4850a3d --- /dev/null +++ b/include/FluidSCWrapper.hpp @@ -0,0 +1,462 @@ + #pragma once + +#include "SCBufferAdaptor.hpp" +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace fluid { +namespace client { + +template class FluidSCWrapper; + +namespace impl { + +template struct Setter; +template struct ArgumentGetter; +template struct ControlGetter; +template using msg_iter_method = T (sc_msg_iter::*)(T); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Iterate over kr/ir inputs via callbacks from params object +struct FloatControlsIter +{ + FloatControlsIter(float** vals, size_t N):mValues(vals), mSize(N) {} + + float next() + { + assert(mCount < mSize && "Boundary error fail horror"); + float f = *mValues[mCount++]; + return f; + } + +// float operator[](size_t i) +// { +// assert(i < mSize); +// return *mValues[i]; +// } + + void reset(float** vals) + { + mValues = vals; + mCount = 0; + } + + private: + float** mValues; + size_t mSize; + size_t mCount{0}; +}; + +//General case +template struct GetControl +{ + T operator()(World*, FloatControlsIter& controls) { return controls.next(); } +}; + +template struct ControlGetter : public GetControl +{}; + +//Specializations +template struct ControlGetter +{ + auto operator() (World* w, FloatControlsIter& iter) + { + typename LongT::type bufnum = iter.next(); + return std::unique_ptr(bufnum >= 0 ? new SCBufferAdaptor(bufnum,w): nullptr); + } +}; + +template +struct ControlGetter +{ + typename FloatPairsArrayT::type operator()(World*, FloatControlsIter& iter) + { + return {{iter.next(),iter.next()},{iter.next(),iter.next()}}; + } +}; + +template +struct ControlGetter +{ + typename FFTParamsT::type operator()(World*, FloatControlsIter& iter) + { + return {static_cast(iter.next()),static_cast(iter.next()),static_cast(iter.next())}; + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// Iterate over arguments in sc_msg_iter, via callbacks from params object + +template Method> struct GetArgument +{ + T operator()(World* w, sc_msg_iter *args) + { + T r = (args->*Method)(T{0}); + return r; + } +}; + +//General cases +template struct ArgumentGetter : public GetArgument +{}; + +template struct ArgumentGetter : public GetArgument +{}; + +template struct ArgumentGetter : public GetArgument +{}; + +//Specializations +template struct ArgumentGetter +{ + auto operator() (World* w, sc_msg_iter *args) + { + typename LongT::type bufnum = args->geti(-1); + return std::unique_ptr(bufnum >= 0 ? new SCBufferAdaptor(bufnum,w) : nullptr); + } +}; + +template struct ArgumentGetter +{ + typename FloatPairsArrayT::type operator()(World* w, sc_msg_iter *args) + { + return {{args->getf(),args->getf()},{args->getf(),args->getf()}}; + } +}; + +template struct ArgumentGetter +{ + typename FFTParamsT::type operator()(World* w, sc_msg_iter *args) + { + return {args->geti(),args->geti(),args->geti()}; + } +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Real Time Processor + +template class RealTime : public SCUnit +{ + using HostVector = FluidTensorView; + // using Client = typename Wrapper::ClientType; + +public: + static void setup(InterfaceTable *ft, const char *name) + { + registerUnit(ft, name); + ft->fDefineUnitCmd(name,"latency",doLatency); + } + + static void doLatency(Unit *unit, sc_msg_iter *args) + { + float l[] {static_cast(static_cast(unit)->mClient.latency())}; + auto ft = Wrapper::getInterfaceTable(); + ft->fSendNodeReply(&unit->mParent->mNode,-1,Wrapper::getName(), 1, l); + } + + RealTime(): + mControlsIterator{mInBuf + mSpecialIndex + 1,mNumInputs - mSpecialIndex - 1}, + mParams{*Wrapper::getParamDescriptors()}, + mClient{Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld, mControlsIterator)} + {} + + void init() + { + assert(!(mClient.audioChannelsOut() > 0 && mClient.controlChannelsOut() > 0) && "Client can't have both audio and control outputs"); + + mInputConnections.reserve(mClient.audioChannelsIn()); + mOutputConnections.reserve(mClient.audioChannelsOut()); + mAudioInputs.reserve(mClient.audioChannelsIn()); + mOutputs.reserve(std::max(mClient.audioChannelsOut(),mClient.controlChannelsOut())); + + for (int i = 0; i < mClient.audioChannelsIn(); ++i) + { + mInputConnections.emplace_back(isAudioRateIn(i)); + mAudioInputs.emplace_back(nullptr, 0, 0); + } + + for (int i = 0; i < mClient.audioChannelsOut(); ++i) + { + mOutputConnections.emplace_back(true); + mOutputs.emplace_back(nullptr, 0, 0); + } + + for (int i = 0; i < mClient.controlChannelsOut(); ++i) + { + mOutputs.emplace_back(nullptr, 0, 0); + } + + set_calc_function(); + Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); + } + + void next(int n) + { + mControlsIterator.reset(mInBuf + 1); //mClient.audioChannelsIn()); + Wrapper::setParams(mParams,mWorld->mVerbosity > 0, mWorld,mControlsIterator); // forward on inputs N + audio inputs as params + const Unit *unit = this; + for (int i = 0; i < mClient.audioChannelsIn(); ++i) + { + if (mInputConnections[i]) mAudioInputs[i].reset(IN(i), 0, fullBufferSize()); + } + for (int i = 0; i < mClient.audioChannelsOut(); ++i) + { + if (mOutputConnections[i]) mOutputs[i].reset(out(i), 0, fullBufferSize()); + } + for(int i = 0; i < mClient.controlChannelsOut();++i) + { + mOutputs[i].reset(out(i),0,1); + } + mClient.process(mAudioInputs, mOutputs); + } +private: + std::vector mInputConnections; + std::vector mOutputConnections; + std::vector mAudioInputs; + std::vector mOutputs; + FloatControlsIter mControlsIterator; +protected: + ParameterSet mParams; + Client mClient; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// Non Real Time Processor +template class NonRealTime +{ +public: + static void setup(InterfaceTable *ft, const char *name) { DefinePlugInCmd(name, launch, nullptr); } + + NonRealTime(World *world,sc_msg_iter *args): + mParams{*Wrapper::getParamDescriptors()}, + mClient{mParams} + {} + + void init(){}; + + static void launch(World *world, void *inUserData, struct sc_msg_iter *args, void *replyAddr) + { + + + Wrapper *w = new Wrapper(world,args); //this has to be on the heap, because it doesn't get destoryed until the async command is done + int argsPosition = args->count; + auto argsRdPos = args->rdpos; + Result result = validateParameters(w, world, args); + if (!result.ok()) + { + std::cout << "FluCoMa Error " << Wrapper::getName() << ": " << result.message().c_str(); + delete w; + return; + } + args->count = argsPosition; + args->rdpos = argsRdPos; + Wrapper::setParams(w->mParams,false, world, args); + + size_t msgSize = args->getbsize(); + std::vector completionMessage(msgSize); +// char * completionMsgData = 0; + if (msgSize) + { + args->getb(completionMessage.data(), msgSize); + } + + world->ft->fDoAsynchronousCommand(world, replyAddr, Wrapper::getName(), w, process, exchangeBuffers, tidyUp, destroy,msgSize, completionMessage.data()); + } + + static bool process(World *world, void *data) { return static_cast(data)->process(world); } + static bool exchangeBuffers(World *world, void *data) { return static_cast(data)->exchangeBuffers(world); } + static bool tidyUp(World *world, void *data) { return static_cast(data)->tidyUp(world); } + static void destroy(World *world, void *data) + { + +// void* c = static_cast(data)->mCompletionMessage; +// if(c) world->ft->fRTFree(world,c); + delete static_cast(data); + } + +protected: + ParameterSet mParams; + Client mClient; +private: + static Result validateParameters(NonRealTime *w, World* world, sc_msg_iter *args) + { + auto results = w->mParams.template checkParameterValues(world, args); + for (auto &r : results) + { + std::cout << r.message() << '\n'; + if (!r.ok()) return r; + } + return {}; + } + + bool process(World *world) + { + Result r = mClient.process();///mInputs, mOutputs); + + if(!r.ok()) + { + std::cout << "FluCoMa Error " << Wrapper::getName() << ": " << r.message().c_str(); + return false; + } + + return true; + } + + bool exchangeBuffers(World *world) + { + mParams.template forEachParamType(world); +// for (auto &b : mBuffersOut) b.assignToRT(world); + return true; + } + + bool tidyUp(World *world) + { +// for (auto &b : mBuffersIn) b.cleanUp(); +// for (auto &b : mBuffersOut) b.cleanUp() + mParams.template forEachParamType(); + return true; + } + + template + struct AssignBuffer + { + void operator()(typename BufferT::type& p, World* w) + { + if(auto b = static_cast(p.get())) + b->assignToRT(w); + } + }; + + template + struct CleanUpBuffer + { + void operator()(typename BufferT::type& p) + { + if(auto b = static_cast(p.get())) + b->cleanUp(); + } + }; + +// std::vector mBuffersIn; +// std::vector mBuffersOut; +// std::vector mInputs; +// std::vector mOutputs; + char * mCompletionMessage = nullptr; + void * mReplyAddr = nullptr; + const char * mName = nullptr; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// 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); + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Template Specialisations for NRT/RT + +template class FluidSCWrapperImpl; + +template class FluidSCWrapperImpl : public NonRealTime +{ +public: + FluidSCWrapperImpl(World* w, sc_msg_iter *args): NonRealTime(w,args){}; +}; + +template class FluidSCWrapperImpl : public RealTime +{}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Make base class(es), full of CRTP mixin goodness +template +using FluidSCWrapperBase = FluidSCWrapperImpl,Params, isNonRealTime, isRealTime>; + +} // namespace impl + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///The main wrapper +template class FluidSCWrapper : public impl::FluidSCWrapperBase +{ + +public: + using Client = C; + using Params = P; + + FluidSCWrapper() //mParams{*getParamDescriptors()}, //impl::FluidSCWrapperBase() + { impl::FluidSCWrapperBase::init(); } + + FluidSCWrapper(World* w, sc_msg_iter *args): impl::FluidSCWrapperBase(w,args) + { impl::FluidSCWrapperBase::init(); } + + + static const char *getName(const char *setName = nullptr) + { + static const char *name = nullptr; + return (name = setName ? setName : name); + } + + static Params *getParamDescriptors(Params *setParams = nullptr) + { + static Params* descriptors = nullptr; + return (descriptors = setParams ? setParams : descriptors); + } + + static InterfaceTable *getInterfaceTable(InterfaceTable *setTable = nullptr) + { + static InterfaceTable *ft = nullptr; + return (ft = setTable ? setTable : ft); + } + + static void setup(Params& p, InterfaceTable *ft, const char *name) + { + getName(name); + getInterfaceTable(ft); + getParamDescriptors(&p); + impl::FluidSCWrapperBase::setup(ft, name); + } + + template + static auto& setParams(ParameterSet& p, bool verbose, World* world, impl::FloatControlsIter& inputs) + { + p.template setParameterValues(verbose, world, inputs); + return p; + } + + template + static auto& setParams(ParameterSet& p, bool verbose, World* world, sc_msg_iter *args) + { + p.template setParameterValues(verbose,world, args); + return p; + } + +// impl::ParameterSet mParams; + +// Client &client() { return mClient; } +// +//private: +// Client mClient; +}; + +template