Refactor SC wrapper
Split into distinct files Use Async commands for NRT processing (again)nix
parent
85ec2e624f
commit
0b33b1ecaf
File diff suppressed because it is too large
Load Diff
@ -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 <typename ArgType> 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<impl::FloatControlsIter>
|
||||
{
|
||||
|
||||
using Controls = impl::FloatControlsIter;
|
||||
|
||||
static auto fromArgs(Unit* /*x*/, Controls& args, std::string, int)
|
||||
{
|
||||
// first is string size, then chars
|
||||
index size = static_cast<index>(args.next());
|
||||
std::string res;
|
||||
res.resize(asUnsigned(size));
|
||||
for (index i = 0; i < size; ++i)
|
||||
res[asUnsigned(i)] = static_cast<char>(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<index>(args.next());
|
||||
Container res(size);
|
||||
for (index i = 0; i < size; ++i)
|
||||
res[i] = static_cast<Value>(args.next());
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_integral<T>::value, T>
|
||||
fromArgs(Unit*, Controls& args, T, int)
|
||||
{
|
||||
return static_cast<index>(args.next());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_floating_point<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<typename LongT::type>(
|
||||
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<LongT::type>(fromArgs(x, args, LongT::type(), -1));
|
||||
return InputBufferT::type(fetchBuffer(x, bufnum));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static std::enable_if_t<IsSharedClient<P>::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<sc_msg_iter>
|
||||
{
|
||||
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 <typename T>
|
||||
static std::enable_if_t<std::is_integral<T>::value, const char*>
|
||||
argTypeToString(T&)
|
||||
{
|
||||
return "integer";
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_floating_point<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 <typename P>
|
||||
static std::enable_if_t<IsSharedClient<P>::value,const char*>
|
||||
argTypeToString(P&)
|
||||
{
|
||||
return "shared_object"; //not ideal
|
||||
}
|
||||
|
||||
static bool argTypeOK(std::string&, char tag)
|
||||
{
|
||||
return tag == 's';
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_integral<T>::value
|
||||
|| std::is_floating_point<T>::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 <typename P>
|
||||
static std::enable_if_t<IsSharedClient<P>::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 <typename T>
|
||||
static std::enable_if_t<std::is_integral<T>::value, T>
|
||||
fromArgs(World*, sc_msg_iter& args, T, int defVal)
|
||||
{
|
||||
return args.geti(defVal);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_floating_point<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<typename LongT::type>(
|
||||
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<LongT::type>(fromArgs(x, args, LongT::type(), -1));
|
||||
return InputBufferT::type(fetchBuffer(x, bufnum));
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
static std::enable_if_t<IsSharedClient<P>::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<index>(args.geti());
|
||||
Container res(size);
|
||||
for (index i = 0; i < size; ++i)
|
||||
res[i] = static_cast<Value>(args.geti());
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Wrapper>
|
||||
struct ClientParams{
|
||||
// Iterate over arguments via callbacks from params object
|
||||
template <typename ArgType, size_t N, typename T>
|
||||
struct Setter
|
||||
{
|
||||
static constexpr index argSize =
|
||||
Wrapper::Client::getParameterDescriptors().template get<N>().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<typename Context, typename Client = typename Wrapper::Client, size_t Number = N>
|
||||
std::enable_if_t<!impl::IsNamedShared_v<Client> || 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<N>();
|
||||
}
|
||||
|
||||
ParamLiteralConvertor<T, argSize> a;
|
||||
using LiteralType =
|
||||
typename ParamLiteralConvertor<T, argSize>::LiteralType;
|
||||
|
||||
for (index i = 0; i < argSize; i++)
|
||||
a[i] = static_cast<LiteralType>(
|
||||
ParamReader<ArgType>::fromArgs(x, args, a[0], 0));
|
||||
|
||||
return a.value();
|
||||
}
|
||||
|
||||
template<typename Context, typename Client = typename Wrapper::Client, size_t Number = N>
|
||||
std::enable_if_t<impl::IsNamedShared_v<Client> && 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<N>();
|
||||
}
|
||||
|
||||
index id = ParamReader<ArgType>::fromArgs(x,args,index{},0);
|
||||
return std::to_string(id);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ArgType, size_t N, typename T>
|
||||
struct Getter
|
||||
{
|
||||
static constexpr index argSize =
|
||||
Wrapper::Client::getParameterDescriptors().template get<N>().fixedSize;
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
|
||||
struct ToFloatArray
|
||||
{
|
||||
static index allocSize(typename BufferT::type) { return 1; }
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<
|
||||
std::is_integral<T>::value || std::is_floating_point<T>::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<std::string, 1> s)
|
||||
{
|
||||
index count = 0;
|
||||
for (auto& str : s) count += (str.size() + 1);
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static index allocSize(FluidTensor<T, 1> s)
|
||||
{
|
||||
return s.size();
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
static std::tuple<std::array<index, sizeof...(Ts)>, index>
|
||||
allocSize(std::tuple<Ts...>&& t)
|
||||
{
|
||||
return allocSizeImpl(std::forward<decltype(t)>(t),
|
||||
std::index_sequence_for<Ts...>());
|
||||
};
|
||||
|
||||
template <typename... Ts, size_t... Is>
|
||||
static std::tuple<std::array<index, sizeof...(Ts)>, index>
|
||||
allocSizeImpl(std::tuple<Ts...>&& t, std::index_sequence<Is...>)
|
||||
{
|
||||
index size{0};
|
||||
std::array<index, sizeof...(Ts)> res;
|
||||
(void) std::initializer_list<int>{
|
||||
(res[Is] = size, size += ToFloatArray::allocSize(std::get<Is>(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<SCBufferAdaptor*>(buf.get())->bufnum();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<std::is_integral<T>::value ||
|
||||
std::is_floating_point<T>::value>
|
||||
convert(float* f, T x)
|
||||
{
|
||||
f[0] = static_cast<float>(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<std::string, 1> s)
|
||||
{
|
||||
for (auto& str : s)
|
||||
{
|
||||
std::copy(str.begin(), str.end(), f);
|
||||
f += str.size();
|
||||
*f++ = 0;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
static void convert(float* f, FluidTensor<T, 1> s)
|
||||
{
|
||||
static_assert(std::is_convertible<T, float>::value,
|
||||
"Can't convert this to float output");
|
||||
std::copy(s.begin(), s.end(), f);
|
||||
}
|
||||
|
||||
template <typename... Ts, size_t... Is>
|
||||
static void convert(float* f, std::tuple<Ts...>&& t,
|
||||
std::array<index, sizeof...(Ts)> offsets,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
(void) std::initializer_list<int>{
|
||||
(convert(f + offsets[Is], std::get<Is>(t)), 0)...};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "SCBufferAdaptor.hpp"
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
namespace impl {
|
||||
|
||||
template <size_t N, typename T>
|
||||
struct AssignBuffer
|
||||
{
|
||||
void operator()(const typename BufferT::type& p, World* w)
|
||||
{
|
||||
if (auto b = static_cast<SCBufferAdaptor*>(p.get())) b->assignToRT(w);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N, typename T>
|
||||
struct CleanUpBuffer
|
||||
{
|
||||
void operator()(const typename BufferT::type& p)
|
||||
{
|
||||
if (auto b = static_cast<SCBufferAdaptor*>(p.get())) b->cleanUp();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <SC_ReplyImpl.hpp>
|
||||
|
||||
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<ReplyAddress*>(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<ReplyAddress*>(inreply));
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void deleteReplyAddress(void* inreply)
|
||||
{
|
||||
if(! inreply) return;
|
||||
delete (ReplyAddress*)inreply;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "NonRealtime.hpp"
|
||||
#include "Realtime.hpp"
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
|
||||
template <typename Client> class FluidSCWrapper;
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename Client,typename Wrapper>
|
||||
struct BaseChooser
|
||||
{
|
||||
template<bool>struct Choose
|
||||
{
|
||||
using type = NonRealTime<Client,Wrapper>;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Choose<true>
|
||||
{
|
||||
using type = RealTime<Client,Wrapper>;
|
||||
};
|
||||
|
||||
using RT = typename Client::isRealTime;
|
||||
|
||||
static constexpr bool UseRealTime = RT::value && !IsModel_t<Client>::value;
|
||||
|
||||
using type = typename Choose<UseRealTime>::type;
|
||||
};
|
||||
|
||||
template <typename Client,typename Wrapper>
|
||||
using BaseChooser_t = typename BaseChooser<Client,Wrapper>::type;
|
||||
|
||||
|
||||
template <typename Client>
|
||||
using FluidSCWrapperBase = BaseChooser_t<Client,FluidSCWrapper<Client>>;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,261 @@
|
||||
#pragma once
|
||||
|
||||
#include "ArgsFromClient.hpp"
|
||||
#include "ArgsToClient.hpp"
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
|
||||
template<typename FluidSCWrapper, typename Client>
|
||||
struct FluidSCMessaging{
|
||||
|
||||
static auto getInterfaceTable(){ return FluidSCWrapper::getInterfaceTable(); }
|
||||
static auto getName(){ return FluidSCWrapper::getName(); }
|
||||
|
||||
|
||||
template <size_t N>
|
||||
struct MessageDispatchCmd
|
||||
{
|
||||
using Descriptor = typename Client::MessageSetType::template MessageDescriptorAt<N>;
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
template <size_t N, typename T>
|
||||
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<N>,(void*)messageName.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Message>
|
||||
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<ArgTuple>::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<sc_msg_iter>::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<sc_msg_iter>::argTypeToString(x);
|
||||
if(i < (std::tuple_size<ArgTuple>::value - 1 ) )
|
||||
{
|
||||
std::cout << " ,";
|
||||
}
|
||||
i++;
|
||||
});
|
||||
report << ")\nReceived: (";
|
||||
i = 0;
|
||||
for(auto t: tags)
|
||||
{
|
||||
report << ParamReader<sc_msg_iter>::oscTagToString(t);
|
||||
if( i < ( tags.size() - 1 ) )
|
||||
{
|
||||
report << ", ";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
report << ")\n";
|
||||
}
|
||||
|
||||
return willContinue;
|
||||
}
|
||||
|
||||
|
||||
template<size_t N>
|
||||
static void doMessage(World* inWorld, void* inUserData, struct sc_msg_iter* args, void* replyAddr)
|
||||
{
|
||||
using MessageData = MessageDispatchCmd<N>;
|
||||
|
||||
auto msg = new MessageData();
|
||||
|
||||
msg->id = args->geti();
|
||||
///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<sc_msg_iter>::fromArgs(inWorld, *args,thisarg,0);
|
||||
});
|
||||
|
||||
size_t completionMsgSize{args ? args->getbsize() : 0};
|
||||
assert(completionMsgSize <= std::numeric_limits<int>::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<MessageData*>(data);
|
||||
using ReturnType = typename MessageData::ReturnType;
|
||||
|
||||
if(auto ptr = FluidSCWrapper::get(m->id).lock())
|
||||
{
|
||||
m->result =
|
||||
ReturnType{invokeImpl<N>(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: response
|
||||
{
|
||||
MessageData* m = static_cast<MessageData*>(data);
|
||||
MessageData::Descriptor::template forEachArg<typename BufferT::type,
|
||||
impl::AssignBuffer>(m->args,
|
||||
world);
|
||||
|
||||
if(m->result.status() != Result::Status::kError)
|
||||
messageOutput(m->name, m->id, m->result, world);
|
||||
else
|
||||
{
|
||||
auto ft = getInterfaceTable();
|
||||
ft->fSendNodeReply(ft->fGetNode(world,0),-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<MessageData*>(data);
|
||||
delete m;
|
||||
},
|
||||
static_cast<int>(completionMsgSize), completionMsgData);
|
||||
}
|
||||
|
||||
template <size_t N, typename ArgsTuple, size_t... Is> // Call from NRT
|
||||
static decltype(auto) invokeImpl(Client& x, ArgsTuple& args,
|
||||
std::index_sequence<Is...>)
|
||||
{
|
||||
return x.template invoke<N>(x, std::get<Is>(args)...);
|
||||
}
|
||||
|
||||
template <typename T> // call from RT
|
||||
static void messageOutput(const std::string& s, index id, MessageResult<T>& result, World* world)
|
||||
{
|
||||
auto ft = getInterfaceTable();
|
||||
|
||||
// allocate return values
|
||||
index numArgs = ToFloatArray::allocSize(static_cast<T>(result));
|
||||
|
||||
if(numArgs > 2048)
|
||||
{
|
||||
std::cout << "ERROR: Message response too big to send (" << asUnsigned(numArgs) * sizeof(float) << " bytes)." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
float* values = new float[asUnsigned(numArgs)];
|
||||
|
||||
// copy return data
|
||||
ToFloatArray::convert(values, static_cast<T>(result));
|
||||
|
||||
ft->fSendNodeReply(ft->fGetNode(world,0), static_cast<int>(id), s.c_str(),
|
||||
static_cast<int>(numArgs), values);
|
||||
|
||||
delete[] values;
|
||||
}
|
||||
|
||||
static void messageOutput(const std::string& s,index id, MessageResult<void>&, World* world)
|
||||
{
|
||||
auto ft = getInterfaceTable();
|
||||
ft->fSendNodeReply(ft->fGetNode(world,0), static_cast<int>(id), s.c_str(), 0, nullptr);
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
static void messageOutput(const std::string& s, index id, MessageResult<std::tuple<Ts...>>& result, World* world)
|
||||
{
|
||||
std::array<index, sizeof...(Ts)> offsets;
|
||||
|
||||
index numArgs;
|
||||
|
||||
std::tie(offsets, numArgs) =
|
||||
ToFloatArray::allocSize(static_cast<std::tuple<Ts...>>(result));
|
||||
|
||||
if(numArgs > 2048)
|
||||
{
|
||||
std::cout << "ERROR: Message response too big to send (" << asUnsigned(numArgs) * sizeof(float) << " bytes)." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
float* values = new float[asUnsigned(numArgs)];
|
||||
ToFloatArray::convert(values, std::tuple<Ts...>(result), offsets,
|
||||
std::index_sequence_for<Ts...>());
|
||||
|
||||
auto ft = getInterfaceTable();
|
||||
ft->fSendNodeReply(ft->fGetNode(world,0), id, s.c_str(),
|
||||
static_cast<int>(numArgs), values);
|
||||
|
||||
delete[] values;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <clients/nrt/FluidSharedInstanceAdaptor.hpp>
|
||||
#include <clients/common/FluidNRTClientWrapper.hpp>
|
||||
#include <clients/common/SharedClientUtils.hpp>
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
namespace impl {
|
||||
/// Named, shared clients already have a lookup table in their adaptor class
|
||||
template <typename T>
|
||||
struct IsNamedShared
|
||||
{
|
||||
using type = std::false_type;
|
||||
};
|
||||
|
||||
//TODO: make less tied to current implementation
|
||||
template <typename T>
|
||||
struct IsNamedShared<NRTThreadingAdaptor<NRTSharedInstanceAdaptor<T>>>
|
||||
{
|
||||
using type = std::true_type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using IsNamedShared_t = typename IsNamedShared<T>::type;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsNamedShared_v = IsNamedShared_t<T>::value;
|
||||
|
||||
/// Models don't, but still need to survive CMD-.
|
||||
template<typename T>
|
||||
struct IsModel
|
||||
{
|
||||
using type = std::false_type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsModel<NRTThreadingAdaptor<ClientWrapper<T>>>
|
||||
{
|
||||
using type = typename ClientWrapper<T>::isModelObject;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IsModel<ClientWrapper<T>>
|
||||
{
|
||||
using type = typename ClientWrapper<T>::isModelObject;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using IsModel_t = typename IsModel<T>::type;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool IsModel_v = IsModel_t<T>::value;
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,940 @@
|
||||
#pragma once
|
||||
|
||||
#include "BufferFuncs.hpp"
|
||||
#include "Meta.hpp"
|
||||
#include "SCBufferAdaptor.hpp"
|
||||
#include "CopyReplyAddress.hpp"
|
||||
#include "Messaging.hpp"
|
||||
#include "RealTimeBase.hpp"
|
||||
|
||||
#include <clients/common/FluidBaseClient.hpp>
|
||||
#include <SC_PlugIn.hpp>
|
||||
#include <SC_ReplyImpl.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
namespace impl {
|
||||
|
||||
/// Non Real Time Processor
|
||||
|
||||
template <typename Client, typename Wrapper>
|
||||
class NonRealTime : public SCUnit
|
||||
{
|
||||
using Params = typename Client::ParamSetType;
|
||||
|
||||
template<typename T,typename...Args>
|
||||
static T* rtalloc(World* world,Args&&...args)
|
||||
{
|
||||
void* space = getInterfaceTable()->fRTAlloc(world, sizeof(T));
|
||||
return new (space) T{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
/// Instance cache
|
||||
struct CacheEntry
|
||||
{
|
||||
|
||||
CacheEntry(Params& p):mParams{p},mClient{mParams}
|
||||
{}
|
||||
|
||||
Params mParams;
|
||||
Client mClient;
|
||||
};
|
||||
|
||||
using CacheEntryPointer = std::shared_ptr<CacheEntry>;
|
||||
using WeakCacheEntryPointer = std::weak_ptr<CacheEntry>; //could use weak_type in 17
|
||||
using Cache = std::map<index,CacheEntryPointer>;
|
||||
|
||||
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, CacheEntry&& entry)
|
||||
{
|
||||
if(isNull(get(id)))
|
||||
{
|
||||
auto result = mCache.emplace(id,
|
||||
std::make_shared<CacheEntry>(std::move(entry)));
|
||||
|
||||
return result.second ? (result.first)->second : WeakCacheEntryPointer(); //sob
|
||||
}
|
||||
else //client has screwed up
|
||||
{
|
||||
std::cout << "ERROR: 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 <size_t N, typename T>
|
||||
using ParamsFromOSC = typename ClientParams<Wrapper>::template Setter<sc_msg_iter, N, T>;
|
||||
|
||||
template <size_t N, typename T>
|
||||
using ParamsFromSynth = typename ClientParams<Wrapper>::template Setter<impl::FloatControlsIter, N, T>;
|
||||
|
||||
|
||||
struct NRTCommand
|
||||
{
|
||||
NRTCommand(World*, sc_msg_iter* args, bool consumeID = true)
|
||||
{
|
||||
auto count = args->count;
|
||||
auto pos = args->rdpos;
|
||||
|
||||
mID = args->geti();
|
||||
|
||||
if(!consumeID)
|
||||
{
|
||||
args->count = count;
|
||||
args->rdpos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
NRTCommand(){}
|
||||
|
||||
explicit NRTCommand(index id):mID{id}{}
|
||||
|
||||
bool stage2(World*) { std::cout << "Nope\n"; return true; } //nrt
|
||||
bool stage3(World*) { return true; } //rt
|
||||
bool stage4(World*) { return true; } //nrt
|
||||
void cleanup(World*) {} //rt
|
||||
|
||||
void sendReply(World* world, const char* name,bool success)
|
||||
{
|
||||
if(mID < 0) return;
|
||||
auto ft = getInterfaceTable();
|
||||
auto root = ft->fGetNode(world,0);
|
||||
float res[2] = {static_cast<float>(success),static_cast<float>(mID)};
|
||||
std::string slashname = std::string{'/'} + name;
|
||||
ft->fSendNodeReply(root,0, slashname.c_str(), 2, res);
|
||||
}
|
||||
// protected:
|
||||
index mID;
|
||||
};
|
||||
|
||||
struct CommandNew : public NRTCommand
|
||||
{
|
||||
CommandNew(World* world, sc_msg_iter* args)
|
||||
: NRTCommand{world,args, !IsNamedShared_v<Client>},
|
||||
mParams{Client::getParameterDescriptors()}
|
||||
{
|
||||
mParams.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, *args);
|
||||
}
|
||||
|
||||
CommandNew(index id, World*, FloatControlsIter& args, Unit* x)
|
||||
:NRTCommand{id},
|
||||
mParams{Client::getParameterDescriptors()}
|
||||
{
|
||||
mParams.template setParameterValuesRT<ParamsFromSynth>(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, CacheEntry{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);
|
||||
}
|
||||
return mResult;
|
||||
}
|
||||
|
||||
bool stage3(World* w)
|
||||
{
|
||||
NRTCommand::sendReply(w, name(), mResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mResult;
|
||||
Params mParams;
|
||||
};
|
||||
|
||||
struct CommandFree: public NRTCommand
|
||||
{
|
||||
using NRTCommand::NRTCommand;
|
||||
|
||||
|
||||
template<bool b>
|
||||
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<true>{
|
||||
void operator()(index)
|
||||
{}
|
||||
};
|
||||
|
||||
static const char* name()
|
||||
{
|
||||
static std::string cmd = std::string(Wrapper::getName()) + "/free";
|
||||
return cmd.c_str();
|
||||
}
|
||||
|
||||
bool stage2(World*)
|
||||
{
|
||||
CancelCheck<IsRTQueryModel>()(NRTCommand::mID);
|
||||
remove(NRTCommand::mID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stage3(World* w)
|
||||
{
|
||||
NRTCommand::sendReply(w, 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;
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
client.checkProgress(r);
|
||||
mSuccess = !(r.status() == Result::Status::kError);
|
||||
if (!r.ok())
|
||||
{
|
||||
Wrapper::printResult(world,r);
|
||||
if(r.status() == Result::Status::kError) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stage3(World* world)
|
||||
{
|
||||
if(auto ptr = get(NRTCommand::mID).lock())
|
||||
{
|
||||
auto& params = ptr->mParams;
|
||||
params.template forEachParamType<BufferT, AssignBuffer>(world);
|
||||
//NRTCommand::sendReply(world, name(), true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool stage4(World*) //nrt
|
||||
{
|
||||
if(auto ptr = get(NRTCommand::mID).lock())
|
||||
{
|
||||
ptr->mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cleanup(World* world) {
|
||||
if(auto ptr = get(NRTCommand::mID).lock() && NRTCommand::mID >= 0)
|
||||
NRTCommand::sendReply(world, name(), mSuccess);
|
||||
if(mReplyAddress)
|
||||
{
|
||||
ReplyAddress* r = static_cast<ReplyAddress*>(mReplyAddress);
|
||||
delete r;
|
||||
}
|
||||
} //rt
|
||||
|
||||
bool mSuccess;
|
||||
void* mReplyAddress{nullptr};
|
||||
|
||||
};
|
||||
|
||||
static void doProcessCallback(World* world, index id,size_t completionMsgSize,char* completionMessage,void* replyAddress)
|
||||
{
|
||||
std::cout << "In callback\n";
|
||||
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 runCompletion = [](FifoMsg* msg){
|
||||
std::cout << "In FIFOMsg\n";
|
||||
Context* c = static_cast<Context*>(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<Context*>(msg->mData);
|
||||
delete c;
|
||||
};
|
||||
|
||||
FifoMsg msg;
|
||||
msg.Set(world, runCompletion, tidyup, c);
|
||||
ft->fSendMsgToRT(world,msg);
|
||||
}
|
||||
|
||||
struct CommandProcess: public NRTCommand
|
||||
{
|
||||
CommandProcess(World* world, sc_msg_iter* args): NRTCommand{world, args}
|
||||
{
|
||||
auto& ar = *args;
|
||||
|
||||
if(auto ptr = get(NRTCommand::mID).lock())
|
||||
{
|
||||
ptr->mParams.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, ar);
|
||||
mSynchronous = static_cast<bool>(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<ParamsFromOSC>(nullptr, world, *mOSCData);
|
||||
// mSynchronous = static_cast<bool>(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(mReplyAddr);
|
||||
|
||||
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())
|
||||
{
|
||||
mResult = client.process();
|
||||
Wrapper::printResult(world,mResult);
|
||||
return mSynchronous && mResult.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
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<BufferT, AssignBuffer>(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<BufferT, impl::CleanUpBuffer>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cleanup(World* world) {
|
||||
if(auto ptr = get(NRTCommand::mID).lock() && NRTCommand::mID >= 0 && mSynchronous)
|
||||
NRTCommand::sendReply(world, name(), mResult.ok());
|
||||
if(mReplyAddr)
|
||||
deleteReplyAddress(mReplyAddr);
|
||||
// getInterfaceTable()->fRTFree(world,mReplyAddr);
|
||||
} //rt
|
||||
|
||||
bool synchronous()
|
||||
{
|
||||
return mSynchronous;
|
||||
}
|
||||
|
||||
void addCompletionMessage(size_t size, char* message, void* addr)
|
||||
{
|
||||
mCompletionMsgSize = size;
|
||||
mCompletionMessage = message;
|
||||
mReplyAddr = copyReplyAddress(addr);
|
||||
// ReplyAddress* rr = static_cast<ReplyAddress*>(addr);
|
||||
// if(addr)mReplyAddr = *rr;
|
||||
}
|
||||
|
||||
// private:
|
||||
Result mResult;
|
||||
bool mSynchronous;
|
||||
size_t mCompletionMsgSize{0};
|
||||
char* mCompletionMessage{nullptr};
|
||||
void* mReplyAddr{nullptr};
|
||||
// sc_msg_iter* mOSCData;
|
||||
};
|
||||
|
||||
struct CommandProcessNew: public NRTCommand
|
||||
{
|
||||
CommandProcessNew(World* world, sc_msg_iter* args)
|
||||
: mNew{world,args},
|
||||
mProcess{mNew.mID,false}
|
||||
{
|
||||
mProcess.mSynchronous = args->geti();
|
||||
}
|
||||
|
||||
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) //rt
|
||||
{
|
||||
return mProcess.stage4(world);
|
||||
}
|
||||
|
||||
void cleanup(World* world)
|
||||
{
|
||||
mProcess.cleanup(world);
|
||||
}
|
||||
|
||||
bool synchronous()
|
||||
{
|
||||
return mProcess.synchronous();
|
||||
}
|
||||
|
||||
void addCompletionMessage(size_t size, char* message,void* addr)
|
||||
{
|
||||
mProcess.addCompletionMessage(size, message,addr);
|
||||
}
|
||||
|
||||
private:
|
||||
CommandNew mNew;
|
||||
CommandProcess mProcess;
|
||||
};
|
||||
|
||||
|
||||
struct CommandCancel: public NRTCommand
|
||||
{
|
||||
CommandCancel(World* world, sc_msg_iter* args): NRTCommand{world, args}
|
||||
{}
|
||||
|
||||
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)
|
||||
: NRTCommand{world, args}
|
||||
{
|
||||
auto& ar = *args;
|
||||
if(auto ptr = get(NRTCommand::mID).lock())
|
||||
{
|
||||
ptr->mParams.template setParameterValuesRT<ParamsFromOSC>(nullptr, world, ar);
|
||||
Result result = validateParameters(ptr->mParams);
|
||||
ptr->mClient.setParams(ptr->mParams);
|
||||
NRTCommand::sendReply(world, name(), result.ok());
|
||||
} else printNotFound(NRTCommand::mID);
|
||||
}
|
||||
|
||||
static const char* name()
|
||||
{
|
||||
static std::string cmd = std::string(Wrapper::getName()) + "/setParams";
|
||||
return cmd.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Command>
|
||||
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<Command*>(d)->stage2(w); },
|
||||
[](World* w, void* d) { return static_cast<Command*>(d)->stage3(w); },
|
||||
[](World* w, void* d) { return static_cast<Command*>(d)->stage4(w); },
|
||||
[](World* w, void* d)
|
||||
{
|
||||
auto cmd = static_cast<Command*>(d);
|
||||
cmd->cleanup(w);
|
||||
cmd->~Command();
|
||||
getInterfaceTable()->fRTFree(w,d);
|
||||
},
|
||||
static_cast<int>(completionMsgSize), completionMsgData);
|
||||
}
|
||||
|
||||
|
||||
static auto runAsyncCommand(World* world, CommandProcess* cmd, void* replyAddr,
|
||||
size_t completionMsgSize, char* completionMsgData)
|
||||
{
|
||||
if(!cmd->synchronous())
|
||||
{
|
||||
cmd->addCompletionMessage(completionMsgSize,completionMsgData,replyAddr);
|
||||
return runAsyncCommand<CommandProcess>(world, cmd, replyAddr, 0, nullptr);
|
||||
}
|
||||
else return runAsyncCommand<CommandProcess>(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,replyAddr);
|
||||
return runAsyncCommand<CommandProcessNew>(world, cmd, replyAddr, 0, nullptr);
|
||||
}
|
||||
else return runAsyncCommand<CommandProcessNew>(world, cmd, replyAddr, completionMsgSize, completionMsgData);
|
||||
}
|
||||
|
||||
|
||||
template<typename Command>
|
||||
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);
|
||||
//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<int>::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<index>(0.02 / controlDur());
|
||||
set_calc_function<NRTProgressUnit, &NRTProgressUnit::next>();
|
||||
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
|
||||
}
|
||||
|
||||
void next(int)
|
||||
{
|
||||
if (0 == mCounter++)
|
||||
{
|
||||
index id = static_cast<index>(mInBuf[0][0]);
|
||||
if(auto ptr = get(id).lock())
|
||||
{
|
||||
if(ptr->mClient.done()) mDone = 1;
|
||||
out0(0) = static_cast<float>(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<index>(mInBuf[0][0]);
|
||||
if(mID == -1) mID = count();
|
||||
auto cmd = NonRealTime::rtalloc<CommandNew>(mWorld,mID,mWorld, mControlsIterator, this);
|
||||
runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr);
|
||||
set_calc_function<NRTTriggerUnit, &NRTTriggerUnit::next>();
|
||||
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
|
||||
}
|
||||
|
||||
~NRTTriggerUnit()
|
||||
{
|
||||
if(auto ptr = get(mID).lock())
|
||||
{
|
||||
auto cmd = NonRealTime::rtalloc<CommandFree>(mWorld,mID);
|
||||
runAsyncCommand(mWorld, cmd, nullptr, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void next(int)
|
||||
{
|
||||
index triggerInput = static_cast<index>(mInBuf[static_cast<index>(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)
|
||||
{
|
||||
mDone = 0;
|
||||
client.resetDone();
|
||||
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<CommandProcess>(mWorld,mID,blocking);
|
||||
runAsyncCommand(mWorld,cmd, nullptr,0, nullptr);
|
||||
mRunCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mDone = mRunCount && client.done() ;
|
||||
out0(0) = mDone ? 1 : static_cast<float>(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<Client,Wrapper>;
|
||||
|
||||
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<index>(in0(1));
|
||||
if(auto ptr = get(id).lock())
|
||||
{
|
||||
auto& client = ptr->mClient;
|
||||
mDelegate.init(*this,client,mControls);
|
||||
set_calc_function<NRTModelQueryUnit, &NRTModelQueryUnit::next>();
|
||||
Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
|
||||
}else printNotFound(id);
|
||||
}
|
||||
|
||||
void next(int)
|
||||
{
|
||||
index id = static_cast<index>(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 <size_t N, typename T>
|
||||
using SetupMessageCmd = typename FluidSCMessaging<Wrapper,Client>::template SetupMessageCmd<N,T>;
|
||||
|
||||
|
||||
template<bool, typename CommandType>
|
||||
struct DefineCommandIf
|
||||
{
|
||||
void operator()() { }
|
||||
};
|
||||
|
||||
|
||||
template<typename CommandType>
|
||||
struct DefineCommandIf<true, CommandType>
|
||||
{
|
||||
void operator()() {
|
||||
std::cout << CommandType::name() << std::endl;
|
||||
defineNRTCommand<CommandType>();
|
||||
}
|
||||
};
|
||||
|
||||
template<bool, typename UnitType>
|
||||
struct RegisterUnitIf
|
||||
{
|
||||
void operator()(InterfaceTable*) {}
|
||||
};
|
||||
|
||||
template<typename UnitType>
|
||||
struct RegisterUnitIf<true, UnitType>
|
||||
{
|
||||
void operator()(InterfaceTable* ft) { registerUnit<UnitType>(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* name)
|
||||
{
|
||||
defineNRTCommand<CommandNew>();
|
||||
DefineCommandIf<!IsRTQueryModel, CommandProcess>()();
|
||||
DefineCommandIf<!IsRTQueryModel, CommandProcessNew>()();
|
||||
DefineCommandIf<!IsRTQueryModel, CommandCancel>()();
|
||||
|
||||
DefineCommandIf<IsModel,CommandSetParams>()();
|
||||
// DefineCommandIf<IsRTQueryModel,CommandGetParams>()();
|
||||
|
||||
defineNRTCommand<CommandFree>();
|
||||
RegisterUnitIf<!IsRTQueryModel,NRTProgressUnit>()(ft);
|
||||
RegisterUnitIf<!IsRTQueryModel,NRTTriggerUnit>()(ft);
|
||||
|
||||
RegisterUnitIf<IsRTQueryModel,NRTModelQueryUnit>()(ft);
|
||||
Client::getMessageDescriptors().template iterate<SetupMessageCmd>();
|
||||
}
|
||||
|
||||
|
||||
void init(){};
|
||||
|
||||
private:
|
||||
static Result validateParameters(ParamSetType& p)
|
||||
{
|
||||
auto results = p.constrainParameterValues();
|
||||
for (auto& r : results)
|
||||
{
|
||||
if (!r.ok()) return r;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <size_t N, typename T>
|
||||
struct AssignBuffer
|
||||
{
|
||||
void operator()(const typename BufferT::type& p, World* w)
|
||||
{
|
||||
if (auto b = static_cast<SCBufferAdaptor*>(p.get())) b->assignToRT(w);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N, typename T>
|
||||
struct CleanUpBuffer
|
||||
{
|
||||
void operator()(const typename BufferT::type& p)
|
||||
{
|
||||
if (auto b = static_cast<SCBufferAdaptor*>(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<Wrapper*>(this)};
|
||||
Result mResult;
|
||||
};
|
||||
|
||||
//initialize static cache
|
||||
template<typename Client, typename Wrapper>
|
||||
using Cache = typename NonRealTime<Client,Wrapper>::Cache;
|
||||
|
||||
template<typename Client, typename Wrapper>
|
||||
Cache<Client,Wrapper> NonRealTime<Client,Wrapper>::mCache{};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
|
||||
#include <SC_PlugIn.hpp>
|
||||
|
||||
namespace fluid{
|
||||
namespace client{
|
||||
namespace impl{
|
||||
template <typename Client, class Wrapper>
|
||||
struct RealTimeBase
|
||||
{
|
||||
using HostVector = FluidTensorView<float, 1>;
|
||||
using Params = typename Client::ParamSetType;
|
||||
template<typename T, bool>
|
||||
struct doExpectedCount;
|
||||
|
||||
template<typename T>
|
||||
struct doExpectedCount<T, false>
|
||||
{
|
||||
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<typename T>
|
||||
struct doExpectedCount<T, true>
|
||||
{
|
||||
static void count(const T& d,FloatControlsIter& c,Result& status)
|
||||
{
|
||||
if(!status.ok()) return;
|
||||
|
||||
if(c.remain())
|
||||
{
|
||||
index statedSize = static_cast<index>(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<size_t N, typename T>
|
||||
struct ExpectedCount{
|
||||
void operator ()(const T& descriptor,FloatControlsIter& c, Result& status)
|
||||
{
|
||||
doExpectedCount<T,IsSharedClientRef<typename T::type>::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<ExpectedCount>(
|
||||
std::forward<FloatControlsIter&>(controls),
|
||||
std::forward<Result&>(countScan));
|
||||
return countScan;
|
||||
}
|
||||
|
||||
// static index ControlOffset(Unit* unit) { return unit->mSpecialIndex + 1; }
|
||||
// static index ControlSize(Unit* unit) { return static_cast<index>(unit->mNumInputs) - unit->mSpecialIndex - 1 -(IsModel_t<Client>::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<int>(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<Client>::value ? !mPrevTrig && unit.in0(0) > 0 : false;
|
||||
bool shouldProcess = IsModel_t<Client>::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<int>::max());
|
||||
if (mInputConnections[asUnsigned(i)])
|
||||
mAudioInputs[asUnsigned(i)].reset(const_cast<float*>(unit.in(static_cast<int>(i))), 0,
|
||||
unit.fullBufferSize());
|
||||
}
|
||||
|
||||
for (index i = 0; i < client.audioChannelsOut(); ++i)
|
||||
{
|
||||
assert(i <= std::numeric_limits<int>::max());
|
||||
if (mOutputConnections[asUnsigned(i)])
|
||||
mOutputs[asUnsigned(i)].reset(unit.out(static_cast<int>(i)), 0,
|
||||
unit.fullBufferSize());
|
||||
}
|
||||
|
||||
for (index i = 0; i < client.controlChannelsOut(); ++i)
|
||||
{
|
||||
assert(i <= std::numeric_limits<int>::max());
|
||||
mOutputs[asUnsigned(i)].reset(unit.out(static_cast<int>(i)), 0, 1);
|
||||
}
|
||||
client.process(mAudioInputs, mOutputs, mContext);
|
||||
}
|
||||
private:
|
||||
std::vector<bool> mInputConnections;
|
||||
std::vector<bool> mOutputConnections;
|
||||
std::vector<HostVector> mAudioInputs;
|
||||
std::vector<HostVector> mOutputs;
|
||||
FluidContext mContext;
|
||||
bool mPrevTrig;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
|
||||
#include "ArgsFromClient.hpp"
|
||||
#include "Meta.hpp"
|
||||
#include "RealTimeBase.hpp"
|
||||
#include <clients/common/FluidBaseClient.hpp>
|
||||
#include <SC_PlugIn.hpp>
|
||||
|
||||
// Real Time Processor
|
||||
namespace fluid {
|
||||
namespace client {
|
||||
namespace impl {
|
||||
|
||||
template <typename Client, class Wrapper>
|
||||
class RealTime : public SCUnit
|
||||
{
|
||||
|
||||
using Delegate = impl::RealTimeBase<Client,Wrapper>;
|
||||
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<index>(unit->mNumInputs)
|
||||
- unit->mSpecialIndex
|
||||
- 1
|
||||
- (IsModel_t<Client>::value ? 1 : 0);
|
||||
}
|
||||
|
||||
static void setup(InterfaceTable* ft, const char* name)
|
||||
{
|
||||
ft->fDefineUnitCmd(name, "latency", doLatency);
|
||||
registerUnit<RealTime>(ft,name);
|
||||
}
|
||||
|
||||
static void doLatency(Unit* unit, sc_msg_iter*)
|
||||
{
|
||||
float l[]{
|
||||
static_cast<float>(static_cast<RealTime*>(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)}
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
// auto& client = mClient;
|
||||
mDelegate.init(*this,mClient,mControls);
|
||||
mCalcFunc = make_calc_function<RealTime, &RealTime::next>();
|
||||
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<int>(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<RealTime, &RealTime::next>();
|
||||
// Wrapper::getInterfaceTable()->fClearUnitOutputs(this, 1);
|
||||
}
|
||||
|
||||
void next(int)
|
||||
{
|
||||
// auto& client = mWrapper->client();
|
||||
// auto& params = mWrapper->params();
|
||||
// const Unit* unit = this;
|
||||
mControls.reset(mInBuf + ControlOffset(this));
|
||||
mDelegate.next(*this,mClient,mParams,mControls);
|
||||
// bool trig = IsModel_t<Client>::value ? !mPrevTrig && in0(0) > 0 : false;
|
||||
// bool shouldProcess = IsModel_t<Client>::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<int>::max());
|
||||
// if (mOutputConnections[asUnsigned(i)])
|
||||
// mOutputs[asUnsigned(i)].reset(out(static_cast<int>(i)), 0,
|
||||
// fullBufferSize());
|
||||
// }
|
||||
// for (index i = 0; i < client.controlChannelsOut(); ++i)
|
||||
// {
|
||||
// assert(i <= std::numeric_limits<int>::max());
|
||||
// mOutputs[asUnsigned(i)].reset(out(static_cast<int>(i)), 0, 1);
|
||||
// }
|
||||
// client.process(mAudioInputs, mOutputs, mContext);
|
||||
// // }
|
||||
}
|
||||
private:
|
||||
Delegate mDelegate;
|
||||
FloatControlsIter mControls;
|
||||
Params mParams{Client::getParameterDescriptors()};
|
||||
Client mClient;
|
||||
// std::vector<bool> mInputConnections;
|
||||
// std::vector<bool> mOutputConnections;
|
||||
// std::vector<HostVector> mAudioInputs;
|
||||
// std::vector<HostVector> mOutputs;
|
||||
// FluidContext mContext;
|
||||
|
||||
Wrapper* mWrapper{static_cast<Wrapper*>(this)};
|
||||
// bool mPrevTrig;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue