#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); template Method> struct GetArgument { T operator()(World* w, sc_msg_iter *args) { T r = (args->*Method)(0); return r; } }; struct FloatControlsIter { FloatControlsIter(float** vals, size_t N):mValues(vals), mSize(N) {} float next() { assert(mCount + 1 < mSize); return *mValues[mCount++]; } void reset(float** vals) { mValues = vals; mCount = 0; } private: float** mValues; size_t mSize; size_t mCount{0}; }; template struct GetControl { T operator()(World*, FloatControlsIter& controls) { return controls.next(); } }; template struct ArgumentGetter : public GetArgument {}; template struct ArgumentGetter : public GetArgument {}; template struct ArgumentGetter : public GetArgument {}; template struct ArgumentGetter { auto operator()(World* w, sc_msg_iter *args) { long bufnum = args->geti(-1); return std::unique_ptr(new SCBufferAdaptor(bufnum,w)); } }; template struct ArgumentGetter { typename FloatPairsArrayT::type operator()(World* w, sc_msg_iter *args) { return {{args->getf(),args->getf()},{args->getf(),args->getf()}}; } }; template struct ControlGetter : public GetControl {}; template struct ControlGetter { auto operator()(World* w, FloatControlsIter& iter) { long bufnum = iter.next(); return std::unique_ptr(new SCBufferAdaptor(bufnum,w)); } }; template struct ControlGetter { typename FloatPairsArrayT::type operator()(World*, FloatControlsIter& iter) { return {{iter.next(),iter.next()},{iter.next(),iter.next()}}; } }; //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); } RealTime(): mControlsIterator{mInBuf + mSpecialIndex + 1,mNumInputs - mSpecialIndex - 1} {} //mControlsIterator{nullptr,0} {} void init() { Wrapper *w = static_cast(this); auto &mClient = w->client(); mInputConnections.reserve(mClient.audioChannelsIn()); mOutputConnections.reserve(mClient.audioChannelsOut()); mAudioInputs.reserve(mClient.audioChannelsIn()); mAudioOutputs.reserve(mClient.audioChannelsOut()); 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); mAudioOutputs.emplace_back(nullptr, 0, 0); } set_calc_function(); Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1); } void next(int n) { Wrapper *w = static_cast(this); auto &client = w->client(); mControlsIterator.reset(mInBuf + client.audioChannelsIn()); w->setParams( mWorld->mVerbosity > 0, mWorld,mControlsIterator); // forward on inputs N + audio inputs as params const Unit *unit = this; for (int i = 0; i < client.audioChannelsIn(); ++i) { if (mInputConnections[i]) mAudioInputs[i].reset(IN(i), 0, fullBufferSize()); } for (int i = 0; i < client.audioChannelsOut(); ++i) { if (mOutputConnections[i]) mAudioOutputs[i].reset(out(i), 0, fullBufferSize()); } client.process(mAudioInputs, mAudioOutputs); } private: std::vector mInputConnections; std::vector mOutputConnections; std::vector mAudioInputs; std::vector mAudioOutputs; FloatControlsIter mControlsIterator; }; template class NonRealTime { public: static void setup(InterfaceTable *ft, const char *name) { DefinePlugInCmd(name, launch, nullptr); } NonRealTime() {} void init(){}; static void launch(World *world, void *inUserData, struct sc_msg_iter *args, void *replyAddr) { Wrapper *w = new Wrapper(); //this has to be on the heap, because it doesn't get destoryed until the async command is done w->parseBuffers(w, world, args); int argsPosition = args->count; Result result = validateParameters(w, world, args); if (!result.ok()) { std::cout << "FluCoMa Error " << Wrapper::getName() << ": " << result.message().c_str(); return; } args->count = argsPosition; w->setParams(false, world, args); size_t msgSize = args->getbsize(); char * completionMsgData = 0; if (msgSize) { completionMsgData = (char *) world->ft->fRTAlloc(world, msgSize); args->getb(completionMsgData, msgSize); } world->ft->fDoAsynchronousCommand(world, replyAddr, Wrapper::getName(), w, process, exchangeBuffers, tidyUp, destroy, msgSize, completionMsgData); } 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) { delete static_cast(data); } private: static Result validateParameters(Wrapper *w, World* world, sc_msg_iter *args) { auto &c = w->client(); auto results = c.template checkParameterValues(world, args); for (auto &r : results) { std::cout << r.message() << '\n'; if (!r.ok()) return r; } return {}; } void parseBuffers(Wrapper *w, World *world, sc_msg_iter *args) { auto &c = w->client(); mBuffersIn.reserve(c.audioBuffersIn()); mInputs.reserve(c.audioBuffersIn()); mBuffersOut.reserve(c.audioBuffersOut()); mOutputs.reserve(c.audioBuffersOut()); for (int i = 0; i < c.audioBuffersIn(); i++) { mBuffersIn.emplace_back(args->geti(0), world); mInputs.emplace_back(); mInputs[i].buffer = &mBuffersIn[i]; mInputs[i].startFrame = args->geti(0); mInputs[i].nFrames = args->geti(0); mInputs[i].startChan = args->geti(0); mInputs[i].nChans = args->geti(0); } for (int i = 0; i < c.audioBuffersOut(); i++) { mBuffersOut.emplace_back(args->geti(0), world); mOutputs.emplace_back(); mOutputs[i].buffer = &mBuffersOut[i]; } } bool process(World *world) { Wrapper *wrapper = static_cast(this); Result r = wrapper->client().process(mInputs, mOutputs); if(!r.ok()) { std::cout << "FluCoMa Error " << Wrapper::getName() << ": " << r.message().c_str(); return false; } return true; } bool exchangeBuffers(World *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(); return true; } std::vector mBuffersIn; std::vector mBuffersOut; std::vector mInputs; std::vector mOutputs; void * mReplyAddr; const char * mName; }; 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 {}; template class FluidSCWrapperImpl : public RealTime {}; // Make base class(es), full of CRTP mixin goodness template using FluidSCWrapperBase = FluidSCWrapperImpl, isNonRealTime, isRealTime>; } // namespace impl template class FluidSCWrapper : public impl::FluidSCWrapperBase { public: using ClientType = Client; FluidSCWrapper() { impl::FluidSCWrapperBase::init(); } static const char *getName(const char *setName = nullptr) { static const char *name = nullptr; return (name = setName ? setName : name); } static InterfaceTable *getInterfaceTable(InterfaceTable *setTable = nullptr) { static InterfaceTable *ft = nullptr; return (ft = setTable ? setTable : ft); } static void setup(InterfaceTable *ft, const char *name) { getName(name); getInterfaceTable(ft); impl::FluidSCWrapperBase::setup(ft, name); } auto setParams(bool verbose, World* world, impl::FloatControlsIter& inputs) { return mClient.template setParameterValues(verbose, world, inputs); } auto setParams(bool verbose, World* world, sc_msg_iter *args) { return mClient.template setParameterValues(verbose,world, args); } Client &client() { return mClient; } private: Client mClient; }; template void makeSCWrapper(InterfaceTable *ft, const char *name) { FluidSCWrapper::setup(ft, name); } } // namespace client } // namespace fluid