diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 2d990c6..c2df32d 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -33,24 +33,23 @@ class FluidSCWrapper; namespace impl { - template - struct AssignBuffer +template +struct AssignBuffer +{ + void operator()(const typename BufferT::type& p, World* w) { - void operator()(const typename BufferT::type &p, World *w) - { - if (auto b = static_cast(p.get())) - b->assignToRT(w); - } - }; + if (auto b = static_cast(p.get())) b->assignToRT(w); + } +}; - template - struct CleanUpBuffer +template +struct CleanUpBuffer +{ + void operator()(const typename BufferT::type& p) { - void operator()(const typename BufferT::type &p) - { - if (auto b = static_cast(p.get())) b->cleanUp(); - } - }; + if (auto b = static_cast(p.get())) b->cleanUp(); + } +}; // Iterate over kr/ir inputs via callbacks from params object @@ -65,12 +64,10 @@ struct FloatControlsIter mValues = vals; mCount = 0; } - + index size() const noexcept { return mSize; } - index remain() - { - return mSize - mCount; - } + index remain() { return mSize - mCount; } + private: float** mValues; index mSize; @@ -163,7 +160,8 @@ public: void next(int) { - mControlsIterator.reset(mInBuf + mSpecialIndex + 1); // mClient.audioChannelsIn()); + mControlsIterator.reset(mInBuf + mSpecialIndex + + 1); // mClient.audioChannelsIn()); Wrapper::setParams( mParams, mWorld->mVerbosity > 0, mWorld, mControlsIterator); // forward on inputs N + audio inputs as params @@ -339,7 +337,8 @@ public: // cancels using u_cmd, this is what will fire if (r.status() == Result::Status::kCancelled) { - std::cout << Wrapper::getName() << ": Processing cancelled" << std::endl; + std::cout << Wrapper::getName() << ": Processing cancelled" + << std::endl; w->mCancelled = true; return false; } @@ -492,9 +491,9 @@ class FluidSCWrapperImpl // Make base class(es), full of CRTP mixin goodness template -using FluidSCWrapperBase = - FluidSCWrapperImpl, typename Client::isNonRealTime, - typename Client::isRealTime>; +using FluidSCWrapperBase = FluidSCWrapperImpl, + typename Client::isNonRealTime, + typename Client::isRealTime>; } // namespace impl @@ -506,303 +505,376 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase { using FloatControlsIter = impl::FloatControlsIter; - + template struct ParamReader { - static auto fromArgs(World *, sc_msg_iter* args, std::string, int) - { + static auto fromArgs(World*, sc_msg_iter* args, std::string, int) + { const char* recv = args->gets(""); - - return std::string(recv?recv:""); - } - - static auto fromArgs(World *w, FloatControlsIter& args, std::string, int) - { - //first is string size, then chars - index size = static_cast(args.next()); - char* chunk = static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc(w,size + 1)); - - if (!chunk) { - std::cout << "ERROR: " << FluidSCWrapper::getName() << ": RT memory allocation failed\n"; - return std::string{""}; - } - - for(index i = 0; i < size; ++i) - chunk[i] = static_cast(args.next()); - - chunk[size] = 0; //terminate string - - return std::string{chunk}; + + return std::string(recv ? recv : ""); } - - - template - static std::enable_if_t::value,T> - fromArgs(World *, FloatControlsIter& args, T, int) { return static_cast(args.next()); } - - template - static std::enable_if_t::value,T> - fromArgs(World *, FloatControlsIter& args, T, int) { return args.next(); } - - template - static std::enable_if_t::value,T> - fromArgs(World *, sc_msg_iter* args, T, int defVal) { return args->geti(defVal); } - template - static std::enable_if_t::value,T> - fromArgs(World *, sc_msg_iter* args, T, int) { return args->getf(); } + static auto fromArgs(World* w, FloatControlsIter& args, std::string, int) + { + // first is string size, then chars + index size = static_cast(args.next()); + char* chunk = + static_cast(FluidSCWrapper::getInterfaceTable()->fRTAlloc( + w, asUnsigned(size + 1))); + + if (!chunk) + { + std::cout << "ERROR: " << FluidSCWrapper::getName() + << ": RT memory allocation failed\n"; + return std::string{""}; + } + + for (index i = 0; i < size; ++i) + chunk[i] = static_cast(args.next()); + + chunk[size] = 0; // terminate string - static auto fromArgs(World *w, ArgType args, BufferT::type&, int) + return std::string{chunk}; + } + + + template + static std::enable_if_t::value, T> + fromArgs(World*, FloatControlsIter& args, T, int) { - typename LongT::type bufnum = static_cast(ParamReader::fromArgs(w, args, typename LongT::type(), -1)); - return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); + return static_cast(args.next()); } - - static auto fromArgs(World *w, ArgType args, InputBufferT::type&, int) + + template + static std::enable_if_t::value, T> + fromArgs(World*, FloatControlsIter& args, T, int) + { + return args.next(); + } + + template + static std::enable_if_t::value, T> + fromArgs(World*, sc_msg_iter* args, T, int defVal) + { + return args->geti(defVal); + } + + template + static std::enable_if_t::value, T> + fromArgs(World*, sc_msg_iter* args, T, int) + { + return args->getf(); + } + + static auto fromArgs(World* w, ArgType args, BufferT::type&, int) + { + typename LongT::type bufnum = static_cast( + ParamReader::fromArgs(w, args, typename LongT::type(), -1)); + return BufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) + : nullptr); + } + + static auto fromArgs(World* w, ArgType args, InputBufferT::type&, int) { typename LongT::type bufnum = static_cast(fromArgs(w, args, LongT::type(), -1)); return InputBufferT::type(bufnum >= 0 ? new SCBufferAdaptor(bufnum, w) : nullptr); } - - template - static std::enable_if_t::value,P> - fromArgs(World *w, ArgType args, P&, int) + + template + static std::enable_if_t::value, P> + fromArgs(World* w, ArgType args, P&, int) { - return {fromArgs(w, args, std::string{}, 0).c_str()}; + return {fromArgs(w, args, std::string{}, 0).c_str()}; } - }; - - - + + // Iterate over arguments via callbacks from params object template struct Setter { - static constexpr index argSize = C::getParameterDescriptors().template get().fixedSize; - - typename T::type operator()(World *w, ArgType args) + static constexpr index argSize = + C::getParameterDescriptors().template get().fixedSize; + + typename T::type operator()(World* w, ArgType args) { - //Just return default if there's nothing left to grab - if(args.remain() == 0) + // Just return default if there's nothing left to grab + if (args.remain() == 0) { - std::cout << "WARNING: " << getName() << " received fewer parameters than expected\n"; + std::cout << "WARNING: " << getName() + << " received fewer parameters than expected\n"; return C::getParameterDescriptors().template makeValue(); } - + ParamLiteralConvertor a; using LiteralType = typename ParamLiteralConvertor::LiteralType; for (index i = 0; i < argSize; i++) - a[i] = static_cast(ParamReader::fromArgs(w, args, a[0], 0)); + a[i] = static_cast( + ParamReader::fromArgs(w, args, a[0], 0)); return a.value(); } }; - + template using ArgumentSetter = Setter; template using ControlSetter = Setter; - - //CryingEmoji.png: SC API hides all the useful functions for sending - //replies back to the language with things like, uh, strings and stuff. - //We have Node_SendReply, which assumes you are sending an array of float, - //and must be called only from the RT thread. Thanks. - //So, we do in reverse what the SendReply Ugen does, and parse - //an array of floats as characters in the language. VomitEmoji.png - + + // CryingEmoji.png: SC API hides all the useful functions for sending + // replies back to the language with things like, uh, strings and stuff. + // We have Node_SendReply, which assumes you are sending an array of float, + // and must be called only from the RT thread. Thanks. + // So, we do in reverse what the SendReply Ugen does, and parse + // an array of floats as characters in the language. VomitEmoji.png + struct ToFloatArray { - static size_t allocSize(typename BufferT::type){ return 1; } - - template - static std::enable_if_t::value||std::is_floating_point::value,size_t> - allocSize(T){ return 1; } - - static index allocSize(std::string s){ return asSigned(s.size()) + 1; } //put null char at end when we send - - static index allocSize(FluidTensor s) + static index allocSize(typename BufferT::type) { return 1; } + + template + static std::enable_if_t< + std::is_integral::value || std::is_floating_point::value, index> + allocSize(T) + { + return 1; + } + + static index allocSize(std::string s) + { + return asSigned(s.size()) + 1; + } // put null char at end when we send + + static index allocSize(FluidTensor s) { index count = 0; - for(auto& str: s) count += (str.size() + 1); + for (auto& str : s) count += (str.size() + 1); return count; } - template - static index allocSize(FluidTensor s) { return asSigned(s.size()); } - - template - static std::tuple,index> allocSize(std::tuple&& t) + + template + static index allocSize(FluidTensor s) { - return allocSizeImpl(std::forward(t), std::index_sequence_for()); - }; - - template - static std::tuple,index> allocSizeImpl(std::tuple&& t,std::index_sequence) + return s.size(); + } + + template + static std::tuple, index> + allocSize(std::tuple&& t) { - index size{0}; - std::array res; - (void)std::initializer_list{(res[Is] = size,size += ToFloatArray::allocSize(std::get(t)),0)...}; - return std::make_tuple(res,size); //array of offsets into allocated buffer & total number of floats to alloc + return allocSizeImpl(std::forward(t), + std::index_sequence_for()); }; - - static void convert(float* f, typename BufferT::type buf) { f[0] = static_cast(buf.get())->bufnum(); } - - template - static std::enable_if_t::value||std::is_floating_point::value> - convert(float* f, T x) { f[0] = static_cast(x); } - + + template + static std::tuple, index> + allocSizeImpl(std::tuple&& t, std::index_sequence) + { + index size{0}; + std::array res; + (void) std::initializer_list{ + (res[Is] = size, size += ToFloatArray::allocSize(std::get(t)), + 0)...}; + return std::make_tuple(res, + size); // array of offsets into allocated buffer & + // total number of floats to alloc + }; + + static void convert(float* f, typename BufferT::type buf) + { + f[0] = static_cast(buf.get())->bufnum(); + } + + template + static std::enable_if_t::value || + std::is_floating_point::value> + convert(float* f, T x) + { + f[0] = static_cast(x); + } + static void convert(float* f, std::string s) { std::copy(s.begin(), s.end(), f); - f[s.size()] = 0; //terminate + f[s.size()] = 0; // terminate } - static void convert(float* f, FluidTensor s) + static void convert(float* f, FluidTensor s) { - for(auto& str: s) + for (auto& str : s) { std::copy(str.begin(), str.end(), f); f += str.size(); *f++ = 0; } } - template - static void convert(float* f, FluidTensor s) + template + static void convert(float* f, FluidTensor s) { - static_assert(std::is_convertible::value,"Can't convert this to float output"); - std::copy(s.begin(), s.end(), f); + static_assert(std::is_convertible::value, + "Can't convert this to float output"); + std::copy(s.begin(), s.end(), f); } - - template - static void convert(float* f,std::tuple&& t, std::array offsets, std::index_sequence) + + template + static void convert(float* f, std::tuple&& t, + std::array offsets, + std::index_sequence) { - (void)std::initializer_list{(convert(f + offsets[Is],std::get(t)),0)...}; + (void) std::initializer_list{ + (convert(f + offsets[Is], std::get(t)), 0)...}; } }; - - - //So, to handle a message to a plugin we will need to - // (1) Launch the invovation of the message on the SC NRT Queue using FIFO Message - // (2) Run the actual function (maybe asynchronously, in our own thread) - // (3) Launch an asynchronous command to send the reply back (in Stage 3) - - template + + + // So, to handle a message to a plugin we will need to + // (1) Launch the invovation of the message on the SC NRT Queue using FIFO + // Message (2) Run the actual function (maybe asynchronously, in our own + // thread) (3) Launch an asynchronous command to send the reply back (in Stage + // 3) + + template struct MessageDispatch { static constexpr size_t Message = N; - FluidSCWrapper* wrapper; - ArgTuple args; - Ret result; - std::string name; + FluidSCWrapper* wrapper; + ArgTuple args; + Ret result; + std::string name; }; - - //Sets up a single /u_cmd - template + + // Sets up a single /u_cmd + template struct SetupMessage { void operator()(const T& message) { - auto ft = getInterfaceTable(); - ft->fDefineUnitCmd(getName(), message.name, launchMessage); + auto ft = getInterfaceTable(); + ft->fDefineUnitCmd(getName(), message.name, launchMessage); } }; - template - static void launchMessage(Unit* u,sc_msg_iter* args) + template + static void launchMessage(Unit* u, sc_msg_iter* args) { FluidSCWrapper* x = static_cast(u); - using IndexList = typename Client::MessageSetType::template MessageDescriptorAt::IndexList; - launchMessageImpl(x,args,IndexList()); + using IndexList = + typename Client::MessageSetType::template MessageDescriptorAt< + N>::IndexList; + launchMessageImpl(x, args, IndexList()); } - template - static void launchMessageImpl(FluidSCWrapper* x,sc_msg_iter* inArgs,std::index_sequence) + template + static void launchMessageImpl(FluidSCWrapper* x, sc_msg_iter* inArgs, + std::index_sequence) { - using MessageDescriptor = typename Client::MessageSetType::template MessageDescriptorAt; + using MessageDescriptor = + typename Client::MessageSetType::template MessageDescriptorAt; using ArgTuple = typename MessageDescriptor::ArgumentTypes; using ReturnType = typename MessageDescriptor::ReturnType; using IndexList = typename MessageDescriptor::IndexList; - using MessageData = MessageDispatch; - - auto ft = getInterfaceTable(); - void* msgptr = ft->fRTAlloc(x->mWorld,sizeof(MessageData)); - MessageData* msg = new(msgptr) MessageData; - ArgTuple& args = msg->args; - (void)std::initializer_list{(std::get(args) = ParamReader::fromArgs(x->mWorld,inArgs,std::get(args),0),0)...}; - + using MessageData = MessageDispatch; + + auto ft = getInterfaceTable(); + void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); + MessageData* msg = new (msgptr) MessageData; + ArgTuple& args = msg->args; + (void) std::initializer_list{ + (std::get(args) = ParamReader::fromArgs( + x->mWorld, inArgs, std::get(args), 0), + 0)...}; + msg->name = '/' + Client::getMessageDescriptors().template name(); msg->wrapper = x; x->mDone = false; - ft->fDoAsynchronousCommand(x->mWorld, nullptr, getName(), msg, - [](World*, void* data) //NRT thread: invocation - { - MessageData* m = static_cast(data); - m->result = ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; - - if(!m->result.ok()) - { - printResult(m->wrapper, m->result); - return false; - } - return true; - }, - [](World* world, void* data) //RT thread: response - { - MessageData* m = static_cast(data); - MessageDescriptor::template forEachArg(m->args, world); - messageOutput(m->wrapper,m->name,m->result); - return true; - } - , nullptr, //NRT Thread: No-op - [](World* w, void* data) //RT thread: clean up - { - getInterfaceTable()->fRTFree(w,data); - }, - 0, nullptr); - } - - template //Call from NRT - static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, std::index_sequence) - { - return x->mClient.template invoke(x->mClient,std::get(args)...); - } - - template //call from RT - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult& result) - { - auto ft = getInterfaceTable(); - //allocate return values - size_t numArgs = ToFloatArray::allocSize(static_cast(result)); - float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); - //copy return data - ToFloatArray::convert(values,static_cast(result)); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); - } - - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult&) + ft->fDoAsynchronousCommand( + x->mWorld, nullptr, getName(), msg, + [](World*, void* data) // NRT thread: invocation + { + MessageData* m = static_cast(data); + m->result = + ReturnType{invokeImpl(m->wrapper, m->args, IndexList{})}; + + if (!m->result.ok()) + { + printResult(m->wrapper, m->result); + return false; + } + return true; + }, + [](World* world, void* data) // RT thread: response + { + MessageData* m = static_cast(data); + MessageDescriptor::template forEachArg(m->args, + world); + messageOutput(m->wrapper, m->name, m->result); + return true; + }, + nullptr, // NRT Thread: No-op + [](World* w, void* data) // RT thread: clean up + { getInterfaceTable()->fRTFree(w, data); }, + 0, nullptr); + } + + template // Call from NRT + static decltype(auto) invokeImpl(FluidSCWrapper* x, ArgsTuple& args, + std::index_sequence) { - auto ft = getInterfaceTable(); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + return x->mClient.template invoke(x->mClient, std::get(args)...); } - - template - static void messageOutput(FluidSCWrapper* x, const std::string& s, MessageResult>& result) + + template // call from RT + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult& result) { auto ft = getInterfaceTable(); - std::array offsets; - size_t numArgs; - std::tie(offsets,numArgs) = ToFloatArray::allocSize(static_cast>(result)); - float* values = static_cast(ft->fRTAlloc(x->mWorld,numArgs * sizeof(float))); - ToFloatArray::convert(values,std::tuple(result),offsets,std::index_sequence_for()); - ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), static_cast(numArgs), values); + // allocate return values + index numArgs = ToFloatArray::allocSize(static_cast(result)); + + float* values = static_cast( + ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + + // copy return data + ToFloatArray::convert(values, static_cast(result)); + + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + static_cast(numArgs), values); + } + + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult&) + { + auto ft = getInterfaceTable(); + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), 0, nullptr); + } + + template + static void messageOutput(FluidSCWrapper* x, const std::string& s, + MessageResult>& result) + { + auto ft = getInterfaceTable(); + std::array offsets; + index numArgs; + std::tie(offsets, numArgs) = + ToFloatArray::allocSize(static_cast>(result)); + + float* values = static_cast( + ft->fRTAlloc(x->mWorld, asUnsigned(numArgs) * sizeof(float))); + + ToFloatArray::convert(values, std::tuple(result), offsets, + std::index_sequence_for()); + + ft->fSendNodeReply(&x->mParent->mNode, -1, s.c_str(), + static_cast(numArgs), values); } - static void doVersion(Unit*, sc_msg_iter*) @@ -835,7 +907,7 @@ public: getName(name); getInterfaceTable(ft); impl::FluidSCWrapperBase::setup(ft, name); - Client::getMessageDescriptors().template iterate(); + Client::getMessageDescriptors().template iterate(); ft->fDefineUnitCmd(name, "version", doVersion); } @@ -855,40 +927,37 @@ public: "and SC sources are different versions" << std::endl; } - + return p; } - + static void printResult(FluidSCWrapper* x, Result& r) { if (!x) return; switch (r.status()) { - case Result::Status::kWarning: - { - if(x->mWorld->mVerbosity > 0) - std::cout << "WARNING: " << r.message().c_str() << '\n'; - break; - } - case Result::Status::kError: - { - std::cout << "ERROR: " << r.message().c_str() << '\n'; - break; - } - case Result::Status::kCancelled: - { - std::cout << "Task cancelled\n" << '\n'; - break; - } - default: { - } + case Result::Status::kWarning: { + if (x->mWorld->mVerbosity > 0) + std::cout << "WARNING: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kError: { + std::cout << "ERROR: " << r.message().c_str() << '\n'; + break; + } + case Result::Status::kCancelled: { + std::cout << "Task cancelled\n" << '\n'; + break; + } + default: { + } } } }; -template -void makeSCWrapper(const char *name, InterfaceTable *ft) +template +void makeSCWrapper(const char* name, InterfaceTable* ft) { FluidSCWrapper::setup(ft, name); }