#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"; } } template static const char* argTypeToString(Optional&) { return argTypeToString(T{}); } static const char* argTypeToString(const 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(const BufferT::type&) { return "buffer"; } static const char* argTypeToString(const InputBufferT::type&) { return "buffer"; } template static std::enable_if_t::value,const char*> argTypeToString(const 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(const BufferT::type&, char tag) { return tag == 'i'; } static bool argTypeOK(const InputBufferT::type&, char tag) { return tag == 'i'; } template static std::enable_if_t::value,bool> argTypeOK(const P&, char tag) { return tag == 'i'; } template static bool argTypeOK(const Optional&, char tag) { return argTypeOK(T{},tag); } 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 static auto fromArgs(World* w, sc_msg_iter& args, Optional, int) { return Optional{fromArgs(w,args,T{},int{})}; } }; 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; }; }; } }