Merge branch 'refactor/NRTSynths' into clients/inter_client_comms
commit
d96bec54b1
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,201 @@
|
|||||||
|
#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)...};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Packet>
|
||||||
|
struct ToOSCTypes
|
||||||
|
{
|
||||||
|
|
||||||
|
static index numTags(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>
|
||||||
|
numTags(T)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static index numTags(std::string)
|
||||||
|
{
|
||||||
|
return 1;;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static index numTags(FluidTensor<T, 1> s)
|
||||||
|
{
|
||||||
|
return s.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
static index numTags(std::tuple<Ts...>&&)
|
||||||
|
{
|
||||||
|
return std::tuple_size<std::tuple<Ts...>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void getTag(Packet& p, typename BufferT::type) { p.addtag('i'); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::enable_if_t<std::is_integral<std::decay_t<T>>::value>
|
||||||
|
getTag(Packet& p, T&&) { p.addtag('i'); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::enable_if_t<std::is_floating_point<std::decay_t<T>>::value>
|
||||||
|
getTag(Packet& p, T&&) { p.addtag('f'); }
|
||||||
|
|
||||||
|
static void getTag (Packet& p, std::string) { p.addtag('s'); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void getTag(Packet& p, FluidTensor<T, 1> x)
|
||||||
|
{
|
||||||
|
T dummy{};
|
||||||
|
for (int i = 0; i < x.rows(); i++)
|
||||||
|
getTag(p, dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts, size_t... Is>
|
||||||
|
static void getTag(Packet& p, std::tuple<Ts...>&& t)
|
||||||
|
{
|
||||||
|
ForEach(t,[&p](auto& x){getTag(p,x);});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void convert(Packet& p, typename BufferT::type buf)
|
||||||
|
{
|
||||||
|
p.addi(static_cast<SCBufferAdaptor*>(buf.get())->bufnum());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::enable_if_t<std::is_integral<T>::value>
|
||||||
|
convert(Packet& p, T x)
|
||||||
|
{
|
||||||
|
p.addi(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::enable_if_t<std::is_floating_point<T>::value>
|
||||||
|
convert(Packet& p, T x)
|
||||||
|
{
|
||||||
|
p.addf(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void convert(Packet& p, std::string s)
|
||||||
|
{
|
||||||
|
p.adds(s.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void convert(Packet& p, FluidTensor<T, 1> s)
|
||||||
|
{
|
||||||
|
for(auto& x: s) convert(p,x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts, size_t... Is>
|
||||||
|
static void convert(Packet& p, std::tuple<Ts...>&& t)
|
||||||
|
{
|
||||||
|
ForEach(t,[&p](auto& x){ convert(p,x);});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,268 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ArgsFromClient.hpp"
|
||||||
|
#include "ArgsToClient.hpp"
|
||||||
|
#include <scsynthsend.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
void* replyAddr{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
msg->replyAddr = copyReplyAddress(replyAddr);
|
||||||
|
///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: buffer swap (and possible completion messages)
|
||||||
|
{
|
||||||
|
MessageData* m = static_cast<MessageData*>(data);
|
||||||
|
MessageData::Descriptor::template forEachArg<typename BufferT::type,
|
||||||
|
impl::AssignBuffer>(m->args,
|
||||||
|
world);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[](World*, void* data) // NRT Thread: Send reply
|
||||||
|
{
|
||||||
|
MessageData* m = static_cast<MessageData*>(data);
|
||||||
|
if(m->result.status() != Result::Status::kError)
|
||||||
|
messageOutput(m->name, m->id, m->result, m->replyAddr);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
[](World*, 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, void* replyAddr)
|
||||||
|
{
|
||||||
|
index numTags = ToOSCTypes<small_scpacket>::numTags(static_cast<T>(result));
|
||||||
|
if(numTags > 2048)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
small_scpacket packet;
|
||||||
|
packet.adds(s.c_str());
|
||||||
|
packet.maketags(static_cast<int>(numTags) + 2);
|
||||||
|
packet.addtag(',');
|
||||||
|
packet.addtag('i');
|
||||||
|
ToOSCTypes<small_scpacket>::getTag(packet, static_cast<T>(result));
|
||||||
|
|
||||||
|
packet.addi(static_cast<int>(id));
|
||||||
|
ToOSCTypes<small_scpacket>::convert(packet, static_cast<T>(result));
|
||||||
|
|
||||||
|
if(replyAddr)
|
||||||
|
::SendReply(static_cast<ReplyAddress*>(replyAddr),packet.data(),static_cast<int>(packet.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void messageOutput(const std::string& s,index id, MessageResult<void>&, void* replyAddr)
|
||||||
|
{
|
||||||
|
small_scpacket packet;
|
||||||
|
packet.adds(s.c_str());
|
||||||
|
packet.maketags(2);
|
||||||
|
packet.addtag(',');
|
||||||
|
packet.addtag('i');
|
||||||
|
packet.addi(static_cast<int>(id));
|
||||||
|
|
||||||
|
if(replyAddr)
|
||||||
|
::SendReply(static_cast<ReplyAddress*>(replyAddr),packet.data(),static_cast<int>(packet.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
static void messageOutput(const std::string& s, index id, MessageResult<std::tuple<Ts...>>& result, void* replyAddr)
|
||||||
|
{
|
||||||
|
using T = std::tuple<Ts...>;
|
||||||
|
|
||||||
|
index numTags = ToOSCTypes<small_scpacket>::numTags(static_cast<T>(result));
|
||||||
|
if(numTags > 2048)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
small_scpacket packet;
|
||||||
|
packet.adds(s.c_str());
|
||||||
|
packet.maketags(static_cast<int>(numTags + 3));
|
||||||
|
packet.addtag(',');
|
||||||
|
packet.addtag('i');
|
||||||
|
ToOSCTypes<small_scpacket>::getTag(packet,static_cast<T>(result));
|
||||||
|
|
||||||
|
packet.addi(static_cast<int>(id));
|
||||||
|
ToOSCTypes<small_scpacket>::convert(packet, static_cast<T>(result));
|
||||||
|
|
||||||
|
if(replyAddr)
|
||||||
|
::SendReply(static_cast<ReplyAddress*>(replyAddr),packet.data(),static_cast<int>(packet.size()));
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,957 @@
|
|||||||
|
#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 <scsynthsend.h>
|
||||||
|
#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(const Params& p):mParams{p},mClient{mParams}
|
||||||
|
{}
|
||||||
|
|
||||||
|
Params mParams;
|
||||||
|
Client mClient;
|
||||||
|
bool mDone{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
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, const Params& params)
|
||||||
|
{
|
||||||
|
if(isNull(get(id)))
|
||||||
|
{
|
||||||
|
auto result = mCache.emplace(id,
|
||||||
|
std::make_shared<CacheEntry>(params));
|
||||||
|
|
||||||
|
return result.second ? (result.first)->second : WeakCacheEntryPointer(); //sob
|
||||||
|
}
|
||||||
|
else //client has screwed up
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: " << Wrapper::getName() << " 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, void* replyAddr, bool consumeID = true)
|
||||||
|
{
|
||||||
|
auto count = args->count;
|
||||||
|
auto pos = args->rdpos;
|
||||||
|
|
||||||
|
mID = args->geti();
|
||||||
|
|
||||||
|
if(!consumeID)
|
||||||
|
{
|
||||||
|
args->count = count;
|
||||||
|
args->rdpos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(replyAddr)
|
||||||
|
mReplyAddress = copyReplyAddress(replyAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~NRTCommand()
|
||||||
|
{
|
||||||
|
if(mReplyAddress) deleteReplyAddress(mReplyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
NRTCommand(){}
|
||||||
|
|
||||||
|
explicit NRTCommand(index id):mID{id}{}
|
||||||
|
|
||||||
|
bool stage2(World*) { return true; } //nrt
|
||||||
|
bool stage3(World*) { return true; } //rt
|
||||||
|
bool stage4(World*) { return false; } //nrt
|
||||||
|
void cleanup(World*) {} //rt
|
||||||
|
|
||||||
|
void sendReply(const char* name,bool success)
|
||||||
|
{
|
||||||
|
if(mReplyAddress)
|
||||||
|
{
|
||||||
|
std::string slash{"/"};
|
||||||
|
small_scpacket packet;
|
||||||
|
packet.adds((slash+name).c_str());
|
||||||
|
packet.maketags(3);
|
||||||
|
packet.addtag(',');
|
||||||
|
packet.addtag('i');
|
||||||
|
packet.addtag('i');
|
||||||
|
packet.addi(success);
|
||||||
|
packet.addi(static_cast<int>(mID));
|
||||||
|
|
||||||
|
::SendReply(static_cast<ReplyAddress*>(mReplyAddress),packet.data(), static_cast<int>(packet.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// protected:
|
||||||
|
index mID;
|
||||||
|
void* mReplyAddress{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CommandNew : public NRTCommand
|
||||||
|
{
|
||||||
|
CommandNew(World* world, sc_msg_iter* args,void* replyAddr)
|
||||||
|
: NRTCommand{world,args, replyAddr, !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, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
NRTCommand::sendReply(name(),mResult);
|
||||||
|
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
NRTCommand::sendReply(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;
|
||||||
|
NRTCommand::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;
|
||||||
|
ptr->mDone = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.checkProgress(r);
|
||||||
|
mSuccess = !(r.status() == Result::Status::kError);
|
||||||
|
if (!r.ok())
|
||||||
|
{
|
||||||
|
Wrapper::printResult(world,r);
|
||||||
|
if(r.status() == Result::Status::kError)
|
||||||
|
{
|
||||||
|
ptr->mDone = true;
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stage4(World*) //nrt
|
||||||
|
{
|
||||||
|
if(auto ptr = get(NRTCommand::mID).lock())
|
||||||
|
{
|
||||||
|
ptr->mParams.template forEachParamType<BufferT, impl::CleanUpBuffer>();
|
||||||
|
|
||||||
|
if(NRTCommand::mID >= 0 && NRTCommand::mReplyAddress)
|
||||||
|
{
|
||||||
|
NRTCommand::sendReply(name(),mSuccess);
|
||||||
|
}
|
||||||
|
ptr->mDone = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mSuccess;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void doProcessCallback(World* world, index id,size_t completionMsgSize,char* completionMessage,void* replyAddress)
|
||||||
|
{
|
||||||
|
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 launchCompletionFromNRT = [](FifoMsg* inmsg)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ft = getInterfaceTable();
|
||||||
|
FifoMsg fwd = *inmsg;
|
||||||
|
fwd.Set(inmsg->mWorld, runCompletion, tidyup, inmsg->mData);
|
||||||
|
if(inmsg->mWorld->mRunning)
|
||||||
|
ft->fSendMsgToRT(inmsg->mWorld,fwd);
|
||||||
|
};
|
||||||
|
|
||||||
|
FifoMsg msg;
|
||||||
|
msg.Set(world, launchCompletionFromNRT, nullptr, c);
|
||||||
|
|
||||||
|
if(world->mRunning) ft->fSendMsgFromRT(world,msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CommandProcess: public NRTCommand
|
||||||
|
{
|
||||||
|
CommandProcess(World* world, sc_msg_iter* args, void* replyAddr): NRTCommand{world, args, replyAddr}
|
||||||
|
{
|
||||||
|
auto& ar = *args;
|
||||||
|
|
||||||
|
if(auto ptr = get(NRTCommand::mID).lock())
|
||||||
|
{
|
||||||
|
ptr->mDone = false;
|
||||||
|
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(NRTCommand::mReplyAddress);
|
||||||
|
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
ptr->mDone = false;
|
||||||
|
mResult = client.process();
|
||||||
|
Wrapper::printResult(world,mResult);
|
||||||
|
|
||||||
|
bool error =mResult.status() == Result::Status::kError;
|
||||||
|
|
||||||
|
if(error) ptr->mDone = true;
|
||||||
|
return mSynchronous && !error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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>();
|
||||||
|
|
||||||
|
if(NRTCommand::mID >= 0 && mSynchronous)
|
||||||
|
NRTCommand::sendReply(name(), mResult.ok());
|
||||||
|
ptr->mDone = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool synchronous()
|
||||||
|
{
|
||||||
|
return mSynchronous;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCompletionMessage(size_t size, char* message)//, void* addr)
|
||||||
|
{
|
||||||
|
mCompletionMsgSize = size;
|
||||||
|
mCompletionMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private:
|
||||||
|
Result mResult;
|
||||||
|
bool mSynchronous;
|
||||||
|
size_t mCompletionMsgSize{0};
|
||||||
|
char* mCompletionMessage{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CommandProcessNew: public NRTCommand
|
||||||
|
{
|
||||||
|
CommandProcessNew(World* world, sc_msg_iter* args,void* replyAddr)
|
||||||
|
: mNew{world, args, replyAddr},
|
||||||
|
mProcess{mNew.mID,false}
|
||||||
|
{
|
||||||
|
mProcess.mSynchronous = args->geti();
|
||||||
|
mProcess.mReplyAddress = mNew.mReplyAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) //nrt
|
||||||
|
{
|
||||||
|
return mProcess.stage4(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup(World* world)
|
||||||
|
{
|
||||||
|
mProcess.mReplyAddress = nullptr;
|
||||||
|
mProcess.cleanup(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool synchronous()
|
||||||
|
{
|
||||||
|
return mProcess.synchronous();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addCompletionMessage(size_t size, char* message)
|
||||||
|
{
|
||||||
|
mProcess.addCompletionMessage(size, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CommandNew mNew;
|
||||||
|
CommandProcess mProcess;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct CommandCancel: public NRTCommand
|
||||||
|
{
|
||||||
|
CommandCancel(World* world, sc_msg_iter* args, void* replyAddr)
|
||||||
|
: NRTCommand{world, args, replyAddr}
|
||||||
|
{}
|
||||||
|
|
||||||
|
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, void* replyAddr)
|
||||||
|
: NRTCommand{world, args, replyAddr}
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
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);
|
||||||
|
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, replyAddr);
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
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 = ptr->mDone;
|
||||||
|
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*)
|
||||||
|
{
|
||||||
|
defineNRTCommand<CommandNew>();
|
||||||
|
DefineCommandIf<!IsRTQueryModel, CommandProcess>()();
|
||||||
|
DefineCommandIf<!IsRTQueryModel, CommandProcessNew>()();
|
||||||
|
DefineCommandIf<!IsRTQueryModel, CommandCancel>()();
|
||||||
|
|
||||||
|
DefineCommandIf<IsModel,CommandSetParams>()();
|
||||||
|
|
||||||
|
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,128 @@
|
|||||||
|
#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,true)}
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
mControls.reset(mInBuf + ControlOffset(this));
|
||||||
|
mDelegate.next(*this,mClient,mParams,mControls);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Delegate mDelegate;
|
||||||
|
FloatControlsIter mControls;
|
||||||
|
Params mParams{Client::getParameterDescriptors()};
|
||||||
|
Client mClient;
|
||||||
|
Wrapper* mWrapper{static_cast<Wrapper*>(this)};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,36 +1,43 @@
|
|||||||
FluidBufAmpGate : UGen {
|
FluidBufAmpGate : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking = 0|
|
||||||
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
|
||||||
indices = indices.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
source = source.asUGenInput;
|
||||||
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufAmpGateTrigger,-1, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq,maxSize, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, trig = 1, blocking = 0|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action |
|
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
|
||||||
server, this, action, [indices]
|
|
||||||
).process(
|
source = source ? -1;
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
|
indices = indices ? -1;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [indices]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 0],freeWhenDone,action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, freeWhenDone = true, action |
|
||||||
|
|
||||||
|
|
||||||
|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
|
||||||
|
|
||||||
|
source = source ? -1;
|
||||||
|
indices = indices ? -1;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [indices]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, 1],freeWhenDone,action
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [indices], blocking: 1
|
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,39 @@
|
|||||||
FluidBufAmpSlice : UGen {
|
FluidBufAmpSlice : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
indices = indices.asUGenInput;
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
||||||
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufAmpSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, trig = 1, blocking = 0|
|
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*process { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, action |
|
source = source.asUGenInput;
|
||||||
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
||||||
server, this, action, [indices]
|
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq
|
^this.new(server, nil, [indices]).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,0],freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, action|
|
*processBlocking { |server,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85, freeWhenDone = true, action |
|
||||||
|
|
||||||
|
source = source.asUGenInput;
|
||||||
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
||||||
|
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
^this.new(server, nil, [indices]).processList(
|
||||||
server, this, action, [indices], blocking: 1
|
[source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,1],freeWhenDone, action
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,38 @@
|
|||||||
FluidBufCompose : UGen {
|
FluidBufCompose : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
destination = destination.asUGenInput;
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
|
||||||
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
|
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1, source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking = 1|
|
|
||||||
^this.new1('control', source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action|
|
||||||
|
|
||||||
|
source = source.asUGenInput;
|
||||||
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action|
|
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
|
||||||
^FluidNRTProcess.new(
|
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
|
||||||
server, this, action, [destination], blocking:1
|
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
|
|
||||||
);
|
|
||||||
|
|
||||||
|
^this.new( server, nil, [destination]).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);//NB always blocking
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, freeWhenDone = true, action|
|
||||||
^process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
|
source = source.asUGenInput;
|
||||||
);
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination]
|
||||||
|
).processList([source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain, 1], freeWhenDone, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,46 @@
|
|||||||
FluidBufFlatten : UGen {
|
FluidBufFlatten : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, destination, axis = 1, trig = 1, blocking|
|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
*kr { |source, destination, axis = 1, trig = 1, blocking = 1|
|
||||||
destination = destination.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
|
source = source.asUGenInput;
|
||||||
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
|
destination = destination.asUGenInput;
|
||||||
^super.new1(rate, source, destination, axis, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr { |source, destination, axis = 1, trig = 1, blocking = 1|
|
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
|
||||||
^this.new1('control', source, destination, axis, trig, blocking);
|
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, destination, axis, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, source, destination, axis = 1, action|
|
*process { |server, source, destination, axis = 1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [destination], blocking:1
|
source = source.asUGenInput;
|
||||||
).process(
|
destination = destination.asUGenInput;
|
||||||
source, destination, axis
|
|
||||||
|
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination],
|
||||||
|
).processList(
|
||||||
|
[source, destination, axis,0],freeWhenDone,action
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, destination, axis = 1, action|
|
*processBlocking { |server, source, destination, axis = 1, freeWhenDone = true, action|
|
||||||
^process(
|
|
||||||
source, destination, axis
|
source = source.asUGenInput;
|
||||||
);
|
destination = destination.asUGenInput;
|
||||||
}
|
|
||||||
|
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination],
|
||||||
|
).processList(
|
||||||
|
[source, destination, axis,1],freeWhenDone,action
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,45 +1,51 @@
|
|||||||
FluidBufHPSS : UGen {
|
FluidBufHPSS : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking|
|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
harmonic = harmonic.asUGenInput;
|
|
||||||
percussive = percussive.asUGenInput;
|
|
||||||
residual = residual.asUGenInput;
|
|
||||||
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
|
|
||||||
|
|
||||||
//NB For wrapped versions of NRT classes, we set the params for maxima to
|
harmonic = harmonic ? -1;
|
||||||
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
|
percussive = percussive ? -1;
|
||||||
|
residual = residual ? -1;
|
||||||
|
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufHPSSTrigger, -1, source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize, trig, blocking
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
|
||||||
|
|
||||||
^this.multiNew(
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
'control', source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, trig, blocking
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
*process {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
harmonic = harmonic ? -1;
|
||||||
|
percussive = percussive ? -1;
|
||||||
|
residual = residual ? -1;
|
||||||
|
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}
|
^this.new(
|
||||||
).process(
|
server, nil, [harmonic, percussive, residual].select{|x| x!= -1}
|
||||||
source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize, harmFilterSize, percFilterSize,0], freeWhenDone,action
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking {|server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, harmonic = -1, percussive = -1, residual = -1, harmFilterSize = 17, percFilterSize = 31, maskingMode = 0, harmThreshFreq1 = 0.1, harmThreshAmp1 = 0, harmThreshFreq2 = 0.5, harmThreshAmp2 = 0, percThreshFreq1 = 0.1, percThreshAmp1 = 0, percThreshFreq2 = 0.5, percThreshAmp2 = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone=true, action|
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}, blocking:1
|
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
harmonic = harmonic ? -1;
|
||||||
|
percussive = percussive ? -1;
|
||||||
|
residual = residual ? -1;
|
||||||
|
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
|
||||||
|
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [harmonic, percussive, residual].select{|x| x!= -1}
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize, maxFFTSize,harmFilterSize, percFilterSize,1], freeWhenDone,action
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,50 @@
|
|||||||
FluidBufMelBands : UGen {
|
FluidBufMelBands : FluidBufProcessor {
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
|
||||||
|
|
||||||
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
features = features.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
|
source = source.asUGenInput;
|
||||||
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
|
features = features.asUGenInput;
|
||||||
|
|
||||||
//NB For wrapped versions of NRT classes, we set the params for maxima to
|
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
|
||||||
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
|
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
|
||||||
// same for maxNumBands which is passed numBands
|
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
^FluidProxyUgen.kr(\FluidBufMelBandsTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize, trig, blocking);
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [features]
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
|
source = source.asUGenInput;
|
||||||
|
features = features.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
|
||||||
|
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [features]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numBands = 40, minFreq = 20, maxFreq = 20000, normalize = 1, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [features], blocking:1
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
|
source = source.asUGenInput;
|
||||||
|
features = features.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
|
||||||
|
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [features]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,47 +1,36 @@
|
|||||||
FluidBufNMF : UGen {
|
FluidBufNMF : FluidBufProcessor //: UGen {
|
||||||
|
{
|
||||||
*new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1, blocking = 0|
|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
|
||||||
resynth = resynth.asUGenInput;
|
|
||||||
bases = bases.asUGenInput;
|
|
||||||
activations = activations.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
|
||||||
resynth = resynth ? -1;
|
resynth = resynth ? -1;
|
||||||
bases = bases ? -1;
|
bases = bases ? -1;
|
||||||
activations = activations ? -1;
|
activations = activations ? -1;
|
||||||
|
|
||||||
^super.new1(rate,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, trig = 1, blocking = 0|
|
|
||||||
^this.new1(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1,freeWhenDone = true, action|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
resynth = resynth ? -1;
|
||||||
|
bases = bases ? -1;
|
||||||
|
activations = activations ? -1;
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
^this.new(
|
||||||
server, this, action, [resynth, bases, activations].select{|x| x!= -1}
|
server,nil,[resynth, bases, activations].select{|x| x!= -1}
|
||||||
).process(
|
).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize,0],freeWhenDone,action);
|
||||||
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1,freeWhenDone = true, action|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
resynth = resynth ? -1;
|
||||||
|
bases = bases ? -1;
|
||||||
|
activations = activations ? -1;
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
^this.new(
|
||||||
server, this, action, [resynth, bases, activations].select{|x| x!= -1},blocking: 1
|
server,nil,[resynth, bases, activations].select{|x| x!= -1}
|
||||||
).process(
|
).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, 1],freeWhenDone,action);
|
||||||
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,48 @@
|
|||||||
FluidBufNMFCross : UGen{
|
FluidBufNMFCross : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
target = target.asUGenInput;
|
target = target.asUGenInput;
|
||||||
output = output.asUGenInput;
|
output = output.asUGenInput;
|
||||||
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
|
||||||
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
|
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
|
||||||
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
|
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufNMFCrossTrigger, -1, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
}
|
source = source.asUGenInput;
|
||||||
|
target = target.asUGenInput;
|
||||||
|
output = output.asUGenInput;
|
||||||
|
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
|
||||||
|
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
|
||||||
|
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
|
||||||
|
|
||||||
*process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
|
||||||
^FluidNRTProcess.new(
|
^this.new(
|
||||||
server, this, action, [output].select{|x| x!= -1}
|
server, nil, [output]
|
||||||
).process(
|
).processList(
|
||||||
source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize
|
[source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize,0],freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [output].select{|x| x!= -1}, blocking: 1
|
source = source.asUGenInput;
|
||||||
).process(
|
target = target.asUGenInput;
|
||||||
source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize
|
output = output.asUGenInput;
|
||||||
|
source.isNil.if {"FluidBufNMFCross: Invalid source buffer".throw};
|
||||||
|
target.isNil.if {"FluidBufNMFCross: Invalid target buffer".throw};
|
||||||
|
output.isNil.if {"FluidBufNMFCross: Invalid output buffer".throw};
|
||||||
|
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [output]
|
||||||
|
).processList(
|
||||||
|
[source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize,1],freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,47 @@
|
|||||||
FluidBufNNDSVD : UGen{
|
FluidBufNNDSVD : FluidBufProcessor{
|
||||||
*new1 { |rate, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking=0|
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
*kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
source = source.asUGenInput;
|
|
||||||
bases = bases.asUGenInput;
|
|
||||||
activations = activations.asUGenInput;
|
|
||||||
|
|
||||||
^super.new1(rate, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking)
|
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
||||||
|
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
|
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
|
source = source.asUGenInput;
|
||||||
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr1(\FluidBufNNDSVDTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
|
||||||
^this.new1(\control, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
|
|
||||||
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
||||||
^FluidNRTProcess.new(
|
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
server, this, action, [bases]
|
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
).process(
|
source = source.asUGenInput;
|
||||||
source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [bases]
|
||||||
|
).processList(
|
||||||
|
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,0],freeWhenDone, action
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [bases],blocking:1
|
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
||||||
).process(
|
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize
|
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
||||||
|
source = source.asUGenInput;
|
||||||
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [bases]
|
||||||
|
).processList(
|
||||||
|
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,1],freeWhenDone, action
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,48 @@
|
|||||||
FluidBufOnsetSlice : UGen {
|
FluidBufOnsetSlice : FluidBufProcessor {
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
indices = indices.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
|
source = source.asUGenInput;
|
||||||
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
//NB For wrapped versions of NRT classes, we set the params for maxima to
|
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
|
||||||
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
|
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufOnsetSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
}
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
|
|
||||||
|
source = source.asUGenInput;
|
||||||
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
|
||||||
^FluidNRTProcess.new(
|
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
|
||||||
server, this, action, [indices]
|
|
||||||
).process(
|
^this.new(
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
|
server, nil, [indices]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, metric = 0, threshold = 0.5, minSliceLength = 2, filterSize = 5, frameDelta = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [indices], blocking: 1
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
|
source = source.asUGenInput;
|
||||||
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
|
||||||
|
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [indices]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,39 +1,50 @@
|
|||||||
FluidBufPitch : UGen{
|
FluidBufPitch : FluidBufProcessor{
|
||||||
|
|
||||||
*new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
features = features.asUGenInput;
|
features = features.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
|
||||||
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
|
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
|
||||||
|
|
||||||
//NB For wrapped versions of NRT classes, we set the params for maxima to
|
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
|
||||||
//whatever has been passed in language-side (e.g maxFFTSize still exists as a parameter for the server plugin, but makes less sense here: it just needs to be set to a legal value)
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
|
|
||||||
}
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
|
|
||||||
|
source = source.asUGenInput;
|
||||||
|
features = features.asUGenInput;
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
|
||||||
^FluidNRTProcess.new(
|
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
|
||||||
server, this, action, [features]
|
|
||||||
).process(
|
^this.new(
|
||||||
source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
|
server, nil, [features]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, algorithm = 2, minFreq = 20, maxFreq = 10000, unit = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [features], blocking: 1
|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
|
||||||
).process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
|
source = source.asUGenInput;
|
||||||
|
features = features.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
|
||||||
|
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [features]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, maxFFTSize, 1], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,43 @@
|
|||||||
FluidBufScale : UGen {
|
FluidBufScale : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking = 1|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
destination = destination.asUGenInput;
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
|
||||||
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
|
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, trig = 1, blocking = 1|
|
^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking);
|
||||||
^this.new1('control', source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, trig, blocking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, action|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [destination], blocking:1
|
source = source.asUGenInput;
|
||||||
).process(
|
destination = destination.asUGenInput;
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh
|
|
||||||
);
|
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, 0], freeWhenDone, action
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, freeWhenDone = true, action|
|
||||||
^process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh
|
source = source.asUGenInput;
|
||||||
);
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh, 1], freeWhenDone, action
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,51 @@
|
|||||||
FluidBufStats : UGen{
|
FluidBufStats : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
stats = stats.asUGenInput;
|
stats = stats.asUGenInput;
|
||||||
weights = weights.asUGenInput;
|
weights = weights.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
|
||||||
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
|
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
|
||||||
weights = weights ? -1;
|
weights = weights ? -1;
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufStatsTrigger, -1, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, trig = 1, blocking = 0|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
|
|
||||||
}
|
source = source.asUGenInput;
|
||||||
|
stats = stats.asUGenInput;
|
||||||
|
weights = weights.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
|
||||||
|
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
|
||||||
|
weights = weights ? -1;
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action|
|
^this.new(
|
||||||
^FluidNRTProcess.new(
|
server, nil, [stats]
|
||||||
server, this, action, [stats]
|
).processList(
|
||||||
).process(
|
[source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights, 0], freeWhenDone, action
|
||||||
source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [stats], blocking: 1
|
source = source.asUGenInput;
|
||||||
).process(
|
stats = stats.asUGenInput;
|
||||||
source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights
|
weights = weights.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
|
||||||
|
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
|
||||||
|
weights = weights ? -1;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [stats]
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights, 1], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,29 +1,33 @@
|
|||||||
FluidBufThreadDemo : UGen{
|
FluidBufThreadDemo : FluidBufProcessor{
|
||||||
|
|
||||||
*new1 {|rate, result, time, trig = 1, blocking = 0 |
|
*kr {|result, time, trig = 1, blocking = 0|
|
||||||
result = result.asUGenInput;
|
|
||||||
result.isNil.if {this.class.name+": Invalid output buffer".throw};
|
|
||||||
^super.new1(rate, result, time, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
result = result.asUGenInput;
|
||||||
|
result.isNil.if {this.class.name+": Invalid output buffer".throw};
|
||||||
|
|
||||||
*kr {|result, time, trig = 1, blocking = 0|
|
^FluidProxyUgen.kr(\FluidBufThreadDemoTrigger, -1, result, time, trig, blocking);
|
||||||
^this.new1(\control, result, time, trig, blocking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, result, time = 1000, action|
|
*process { |server, result, time = 1000, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [result]
|
|
||||||
).process(
|
result ?? {this.class.name+": Invalid output buffer".throw};
|
||||||
result, time
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [result]
|
||||||
|
).processList(
|
||||||
|
[result.asUGenInput, time, 0], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, result, time = 1000, action|
|
*processBlocking { |server, result, time = 1000, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [result], blocking: 1
|
result ?? {this.class.name+": Invalid output buffer".throw};
|
||||||
).process(
|
|
||||||
result, time
|
^this.new(
|
||||||
|
server, nil, [result]
|
||||||
|
).processList(
|
||||||
|
[result.asUGenInput, time, 1], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,45 @@
|
|||||||
FluidBufThresh : UGen {
|
FluidBufThresh : FluidBufProcessor {
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking|
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
destination = destination.asUGenInput;
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
|
||||||
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
|
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1|
|
^FluidProxyUgen.kr(\FluidBufThreshTrigger, -1, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
|
||||||
^this.new1('control', source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, action|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action, [destination], blocking:1
|
source = source.asUGenInput;
|
||||||
).process(
|
destination = destination.asUGenInput;
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, threshold
|
|
||||||
|
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination],
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, destination, threshold, 0], freeWhenDone, action
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
|
||||||
^process(
|
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, threshold
|
source = source.asUGenInput;
|
||||||
);
|
destination = destination.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
|
||||||
|
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [destination],
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, destination, threshold, 1], freeWhenDone, action
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,43 @@
|
|||||||
FluidBufTransientSlice : UGen{
|
FluidBufTransientSlice : FluidBufProcessor {
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1, blocking = 0|
|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
*objectClassName{^\FluidBufTrSlice}
|
||||||
indices = indices.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1, blocking = 0|
|
||||||
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
|
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking);
|
source = source.asUGenInput;
|
||||||
}
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, trig = 1, blocking = 0|
|
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking);
|
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, action|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action,[indices]
|
source = source.asUGenInput;
|
||||||
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
|
||||||
|
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil,[indices]
|
||||||
|
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,0], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, minSliceLength = 1000, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action,[indices], blocking: 1
|
source = source.asUGenInput;
|
||||||
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
|
||||||
|
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil,[indices]
|
||||||
|
).processList([source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength,1], freeWhenDone, action
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,43 @@
|
|||||||
FluidBufTransients : UGen {
|
FluidBufTransients : FluidBufProcessor {
|
||||||
|
|
||||||
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0|
|
||||||
|
|
||||||
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0 |
|
source = source.asUGenInput;
|
||||||
|
transients = transients ? -1;
|
||||||
source = source.asUGenInput;
|
residual = residual ? -1;
|
||||||
transients = transients.asUGenInput;
|
|
||||||
residual = residual.asUGenInput;
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
|
||||||
|
|
||||||
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
|
^FluidProxyUgen.kr(\FluidBufTransientsTrigger, -1, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
|
||||||
|
|
||||||
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, trig = 1, blocking = 0|
|
source = source.asUGenInput;
|
||||||
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
|
transients = transients ? -1;
|
||||||
|
residual = residual ? -1;
|
||||||
|
|
||||||
}
|
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, action|
|
^this.new(
|
||||||
^FluidNRTProcess.new(
|
server, nil,[transients, residual].select{|x| x!= -1}
|
||||||
server, this, action,[transients, residual].select{|x| x!= -1}
|
).processList(
|
||||||
).process(
|
[source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,0],freeWhenDone,action
|
||||||
source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, transients = -1, residual = -1, order = 20, blockSize = 256, padSize = 128, skew = 0, threshFwd = 2, threshBack = 1.1, windowSize = 14, clumpLength = 25, freeWhenDone = true, action|
|
||||||
^FluidNRTProcess.new(
|
|
||||||
server, this, action,[transients, residual].select{|x| x!= -1}, blocking: 1
|
source = source.asUGenInput;
|
||||||
).process(
|
transients = transients ? -1;
|
||||||
source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength
|
residual = residual ? -1;
|
||||||
);
|
|
||||||
}
|
source.isNil.if {"FluidBufTransients: Invalid source buffer".throw};
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil,[transients, residual].select{|x| x!= -1}
|
||||||
|
).processList(
|
||||||
|
[source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength,1],freeWhenDone = true,action
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,91 +1,65 @@
|
|||||||
|
|
||||||
FluidDataSetExistsError : Exception{
|
FluidDataSet : FluidDataObject
|
||||||
}
|
{
|
||||||
|
*new{|server| ^super.new(server) }
|
||||||
FluidDataSet : FluidManipulationClient {
|
|
||||||
|
|
||||||
var <id;
|
addPointMsg{|label,buffer|
|
||||||
classvar serverCaches;
|
buffer = this.prEncodeBuffer(buffer);
|
||||||
|
^this.prMakeMsg(\addPoint,id,label.asSymbol,buffer);
|
||||||
|
}
|
||||||
|
|
||||||
*initClass {
|
addPoint{|label, buffer, action|
|
||||||
serverCaches = FluidServerCache.new;
|
actions[\addPoint] = [nil,action];
|
||||||
|
this.prSendMsg(this.addPointMsg(label,buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
*at{ |server, name|
|
getPointMsg{|label,buffer|
|
||||||
^serverCaches.tryPerform(\at, server, name)
|
buffer = this.prEncodeBuffer(buffer);
|
||||||
}
|
^this.prMakeMsg(\getPoint,id,label.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
|
||||||
|
}
|
||||||
|
|
||||||
*new { |server, name|
|
getPoint{|label, buffer, action|
|
||||||
if(this.at(server,name).notNil){
|
actions[\getPoint] = [nil,action];
|
||||||
FluidDataSetExistsError("A FluidDataset called % already exists.".format(name)).throw;
|
this.prSendMsg(this.getPointMsg(label,buffer));
|
||||||
^nil
|
}
|
||||||
}
|
|
||||||
^super.new(server,FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {|name|
|
updatePointMsg{|label,buffer|
|
||||||
this.baseinit(FluidManipulationClient.prServerString(name));
|
buffer = this.prEncodeBuffer(buffer);
|
||||||
id = name;
|
^this.prMakeMsg(\updatePoint,id,label.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
|
||||||
this.cache;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cache {
|
updatePoint{|label, buffer, action|
|
||||||
serverCaches.initCache(server);
|
actions[\updatePoint] = [nil,action];
|
||||||
serverCaches.put(server,id,this);
|
this.prSendMsg(this.updatePointMsg(label,buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
*asUGenInput { |input|
|
deletePointMsg{|label| ^this.prMakeMsg(\deletePoint,id,label.asSymbol);}
|
||||||
var ascii = input.asString.ascii;
|
|
||||||
^[ascii.size].addAll(ascii)
|
|
||||||
}
|
|
||||||
|
|
||||||
asString {
|
deletePoint{|label, buffer, action|
|
||||||
^"FluidDataSet(%)".format(id).asString;
|
actions[\deletePoint] = [nil,action];
|
||||||
}
|
this.prSendMsg(this.deletePointMsg(label));
|
||||||
|
}
|
||||||
|
|
||||||
asSymbol {
|
clearMsg { ^this.prMakeMsg(\clear,id); }
|
||||||
^id.asSymbol
|
|
||||||
}
|
|
||||||
|
|
||||||
addPoint{|label, buffer, action|
|
clear { |action|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
actions[\clear] = [nil,action];
|
||||||
this.prSendMsg(\addPoint,[label.asSymbol,buffer],action);
|
this.prSendMsg(this.clearMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPoint{|label, buffer, action|
|
mergeMsg{|sourceDataSet, overwrite = 0|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
^this.prMakeMsg(\merge,id,sourceDataSet.asUGenInput,overwrite);
|
||||||
this.prSendMsg(\getPoint,[label.asSymbol,buffer],action,outputBuffers:[buffer]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
updatePoint{|label, buffer, action|
|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
|
||||||
this.prSendMsg(\updatePoint,[label.asSymbol,buffer],action,outputBuffers:[buffer]);
|
|
||||||
}
|
|
||||||
|
|
||||||
deletePoint{|label, action|
|
|
||||||
this.prSendMsg(\deletePoint,[label.asSymbol],action);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear { |action|
|
|
||||||
this.prSendMsg(\clear,[],action);
|
|
||||||
}
|
|
||||||
|
|
||||||
merge{|sourceDataSet, overwrite = 0, action|
|
merge{|sourceDataSet, overwrite = 0, action|
|
||||||
this.prSendMsg(\merge,
|
actions[\merge] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, overwrite], action);
|
this.prSendMsg(this.mergeMsg(sourceDataSet,overwrite));
|
||||||
}
|
}
|
||||||
|
|
||||||
print { |action|
|
printMsg { ^this.prMakeMsg(\print,id); }
|
||||||
action ?? {action = postit};
|
|
||||||
this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free {
|
|
||||||
serverCaches.remove(server,id);
|
|
||||||
super.free;
|
|
||||||
}
|
|
||||||
|
|
||||||
*freeAll { |server|
|
print { |action=(postResponse)|
|
||||||
serverCaches.do(server,{|x|x.free;});
|
actions[\print] = [string(FluidMessageResponse,_,_),action];
|
||||||
|
this.prSendMsg(this.printMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,52 +1,84 @@
|
|||||||
|
|
||||||
FluidDataSetQuery : FluidManipulationClient {
|
FluidDataSetQuery : FluidDataObject {
|
||||||
|
|
||||||
*new {|server|
|
addColumnMsg { |column|
|
||||||
var uid = UniqueID.next;
|
^this.prMakeMsg(\addColumn,id,column);
|
||||||
^super.new(server,uid)!?{|inst|inst.init(uid);inst}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
init {|uid|
|
|
||||||
id = uid;
|
|
||||||
this.baseinit(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
addColumn{|column, action|
|
addColumn{|column, action|
|
||||||
this.prSendMsg(\addColumn, [column], action);
|
actions[\addColumn] = [nil,action];
|
||||||
|
this.prSendMsg(this.addColumnMsg(column));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addRangeMsg{|start,count|
|
||||||
|
^this.prMakeMsg(\addRange,id,start,count);
|
||||||
|
}
|
||||||
|
|
||||||
addRange{|start, count, action|
|
addRange{|start, count, action|
|
||||||
this.prSendMsg(\addRange, [start, count], action);
|
actions[\addRange] = [nil, action];
|
||||||
|
this.prSendMsg(this.addRangeMsg(start, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterMsg{|column, condition, value, action|
|
||||||
|
^this.prMakeMsg(\filter,id,column,condition.asSymbol,value);
|
||||||
|
}
|
||||||
|
|
||||||
filter{|column, condition, value, action|
|
filter{|column, condition, value, action|
|
||||||
this.prSendMsg(\filter, [column, condition.asSymbol, value], action);
|
actions[\filter] = [nil, action];
|
||||||
|
this.prSendMsg(this.filterMsg(column, condition, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
andMsg{ |column, condition, value|
|
||||||
|
^this.prMakeMsg(\and,id,column, condition.asSymbol, value);
|
||||||
|
}
|
||||||
|
|
||||||
and{|column, condition, value, action|
|
and{|column, condition, value, action|
|
||||||
this.prSendMsg(\and, [column, condition, value], action);
|
actions[\and] = [nil, action];
|
||||||
|
this.prSendMsg(this.andMsg(column,condition,value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orMsg{|column, condition, value|
|
||||||
|
^this.prMakeMsg(\or,id,column, condition.asSymbol, value)
|
||||||
|
}
|
||||||
|
|
||||||
or{|column, condition, value, action|
|
or{|column, condition, value, action|
|
||||||
this.prSendMsg(\or, [column, condition, value], action);
|
actions[\or] = [nil,action];
|
||||||
|
this.prSendMsg(this.orMsg(column, condition, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearMsg{
|
||||||
|
^this.prMakeMsg(\clear,id);
|
||||||
|
}
|
||||||
|
|
||||||
clear{|action|
|
clear{|action|
|
||||||
this.prSendMsg(\clear, [], action);
|
actions[\clear] = [nil, action];
|
||||||
|
this.prSendMsg(this.clearMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limitMsg{|rows|
|
||||||
|
^this.prMakeMsg(\limit,id,rows);
|
||||||
|
}
|
||||||
|
|
||||||
limit{|rows, action|
|
limit{|rows, action|
|
||||||
this.prSendMsg(\limit, [rows], action);
|
actions[\limit] = [nil,action];
|
||||||
|
this.prSendMsg(this.limitMsg(rows));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
transform{|sourceDataSet, destDataSet, action|
|
transform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\transform,[sourceDataSet.asSymbol, destDataSet.asSymbol],action);
|
actions[\transform] = [nil,action];
|
||||||
|
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
transformJoinMsg{|source1DataSet, source2DataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\transformJoin,id,source1DataSet.id, source2DataSet.id, destDataSet.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
transformJoin{|source1DataSet, source2DataSet, destDataSet, action|
|
transformJoin{|source1DataSet, source2DataSet, destDataSet, action|
|
||||||
this.prSendMsg(\transformJoin,[source1DataSet.asSymbol, source2DataSet.asSymbol, destDataSet.asSymbol],action);
|
actions[\transformJoin] = [nil,action];
|
||||||
|
this.prSendMsg(this.transformJoinMsg(source1DataSet, source2DataSet, destDataSet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
FluidDataSetWr : UGen {
|
FluidDataSetWr : FluidBufProcessor {
|
||||||
|
*kr { |dataset,labelPrefix = "", labelOffset = 0,buf, trig=1, blocking = 1|
|
||||||
|
var args;
|
||||||
|
buf ?? {"No input buffer provided".error};
|
||||||
|
labelPrefix = labelPrefix !? {[labelPrefix.asString.size] ++ labelPrefix.asString.ascii} ?? {0};
|
||||||
|
|
||||||
*new1 { |rate, dataset, labelPrefix = "", labelOffset = 0, buf, trig, blocking|
|
args = [-1] ++ dataset.asUGenInput ++labelPrefix ++ labelOffset.asInteger.asUGenInput ++buf.asUGenInput ++ trig ++ blocking;
|
||||||
buf ?? {"No input buffer provided".error};
|
|
||||||
^super.new1(rate,*(FluidManipulationClient.prServerString(dataset.asSymbol)
|
|
||||||
++ FluidDataSet.asUGenInput(labelPrefix.asSymbol) ++ labelOffset.asInteger.asUGenInput ++buf.asUGenInput ++ trig ++ blocking));
|
|
||||||
}
|
|
||||||
|
|
||||||
*kr { |dataset,labelPrefix = "", labelOffset = 0,buf, trig=1, blocking = 0|
|
^FluidProxyUgen.kr(\FluidDataSetWrTrigger,*args);
|
||||||
^this.new1(\control,dataset,labelPrefix,labelOffset, buf, trig, blocking)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,41 +1,60 @@
|
|||||||
FluidKDTree : FluidRTDataClient {
|
FluidKDTree : FluidRealTimeModel
|
||||||
|
{
|
||||||
|
|
||||||
*new {|server,numNeighbours = 1, radius = 0, lookupDataSet = ""|
|
var neighbours,radius,lookup;
|
||||||
var env;
|
|
||||||
var names = [\numNeighbours, \radius]
|
|
||||||
++ this.prServerString(lookupDataSet.asSymbol).collect{|x,i|
|
|
||||||
("lookupDataSet"++i).asSymbol;
|
|
||||||
};
|
|
||||||
|
|
||||||
var values = [numNeighbours, radius] ++ this.prServerString(lookupDataSet.asSymbol);
|
*new{ |server, numNeighbours = 1, radius = 0, lookupDataSet|
|
||||||
var params = [names,values].lace;
|
^super.new(server,[numNeighbours,radius,lookupDataSet ? -1])
|
||||||
|
.numNeighbours_(numNeighbours)
|
||||||
|
.radius_(radius)
|
||||||
|
.lookupDataSet_(lookupDataSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
numNeighbours_{|k|neighbours = k.asInteger; }
|
||||||
|
numNeighbours{ ^neighbours; }
|
||||||
|
|
||||||
/* env = Environment();
|
radius_{|r| radius = r.asUGenInput;}
|
||||||
synthControls[1..].do{|x|
|
radius{ ^radius; }
|
||||||
env.put(x,0);
|
|
||||||
};
|
|
||||||
env.put(\numNeighbours,1); */
|
|
||||||
|
|
||||||
^super.new1(server,params);
|
lookupDataSet_{|ds| lookup = ds ? -1; }
|
||||||
/* env,
|
lookupDataSet{|ds| ^ (lookup ? -1) }
|
||||||
[\numNeighbours]++lookupDataSet); */
|
|
||||||
}
|
prGetParams{^[this.numNeighbours,this.radius,this.lookupDataSet,-1,-1];}
|
||||||
|
|
||||||
|
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);}
|
||||||
|
|
||||||
fit{|dataSet,action|
|
fit{|dataSet,action|
|
||||||
this.prSendMsg(\fit, [dataSet.asSymbol], action);
|
actions[\fit] = [nil,action];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kNearestMsg{|buffer|
|
||||||
|
^this.prMakeMsg(\kNearest,id,this.prEncodeBuffer(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
kNearest{ |buffer, action|
|
kNearest{ |buffer, action|
|
||||||
this.prSendMsg(\kNearest,
|
actions[\kNearest] = [strings(FluidMessageResponse,_,_),action];
|
||||||
[buffer.asUGenInput], action,
|
this.prSendMsg(this.kNearestMsg(buffer));
|
||||||
[strings(FluidMessageResponse,_,_)]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kNearestDistMsg {|buffer|
|
||||||
|
^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
kNearestDist { |buffer, action|
|
kNearestDist { |buffer, action|
|
||||||
this.prSendMsg(\kNearestDist, [buffer.asUGenInput], action,
|
actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action];
|
||||||
[numbers(FluidMessageResponse,_,nil,_)]
|
this.prSendMsg(this.kNearestDistMsg(buffer));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer, numNeighbours = 1, lookupDataSet|
|
||||||
|
this.numNeighbours_(numNeighbours);
|
||||||
|
lookupDataSet = lookupDataSet ? -1;
|
||||||
|
this.lookupDataSet_(lookupDataSet);
|
||||||
|
this.lookupDataSet.asUGenInput.postln;
|
||||||
|
^FluidProxyUgen.kr('FluidKDTree/query', K2A.ar(trig),
|
||||||
|
id, this.numNeighbours, this.radius, this.lookupDataSet.asUGenInput,
|
||||||
|
this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,68 @@
|
|||||||
FluidKMeans : FluidRTDataClient {
|
FluidKMeans : FluidRealTimeModel {
|
||||||
|
|
||||||
|
var clusters, maxiter;
|
||||||
|
|
||||||
*new {|server, numClusters = 4, maxIter = 100|
|
*new {|server, numClusters = 4, maxIter = 100|
|
||||||
^super.new1(server,[\numClusters,numClusters,\maxIter,maxIter]);
|
^super.new(server,[numClusters,maxIter])
|
||||||
|
.numClusters_(numClusters)
|
||||||
|
.maxIter_(maxIter);
|
||||||
}
|
}
|
||||||
|
|
||||||
fit{|dataSet,action|
|
numClusters_{|n| clusters = n.asInteger}
|
||||||
this.prSendMsg(\fit,
|
numClusters{ ^clusters }
|
||||||
[dataSet.asSymbol], action,
|
|
||||||
[numbers(FluidMessageResponse,_,this.numClusters,_)]
|
maxIter_{|i| maxiter = i.asInteger}
|
||||||
);
|
maxIter{ ^maxiter }
|
||||||
}
|
|
||||||
|
prGetParams{^[this.numClusters,this.maxIter,-1,-1];}
|
||||||
|
|
||||||
|
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,id,dataSet.id);}
|
||||||
|
|
||||||
|
fit{|dataSet, action|
|
||||||
|
actions[\fit] = [
|
||||||
|
numbers( FluidMessageResponse, _, this.numClusters ,_),
|
||||||
|
action
|
||||||
|
];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
fitPredictMsg{|dataSet, labelSet|
|
||||||
|
^this.prMakeMsg(\fitPredict, id, dataSet.id, labelSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fitPredict{|dataSet, labelSet,action|
|
fitPredict{|dataSet, labelSet,action|
|
||||||
this.prSendMsg(\fitPredict,
|
actions[\fitPredict] = [
|
||||||
[dataSet.asSymbol,labelSet.asSymbol],
|
numbers(FluidMessageResponse, _, this.numClusters, _),
|
||||||
action,[numbers(FluidMessageResponse,_,this.numClusters,_)]
|
action
|
||||||
);
|
];
|
||||||
|
this.prSendMsg(this.fitPredictMsg(dataSet,labelSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
predict{ |dataSet, labelSet,action|
|
predictMsg{|dataSet, labelSet|
|
||||||
this.prSendMsg(\predict,
|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
|
||||||
[dataSet.asSymbol, labelSet.asSymbol], action,
|
}
|
||||||
[numbers(FluidMessageResponse,_,this.numClusters,_)]
|
|
||||||
);
|
predict{ |dataSet, labelSet, action|
|
||||||
|
actions[\predict] = [
|
||||||
|
numbers(FluidMessageResponse, _, this.numClusters, _),
|
||||||
|
action
|
||||||
|
];
|
||||||
|
this.prSendMsg(this.predictMsg(dataSet,labelSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictPointMsg{|buffer|
|
||||||
|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
predictPoint { |buffer, action|
|
predictPoint { |buffer, action|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
|
||||||
this.prSendMsg(\predictPoint,
|
this.prSendMsg(this.predictPointMsg(buffer))
|
||||||
[buffer], action,
|
|
||||||
[number(FluidMessageResponse,_,_)]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer|
|
||||||
|
^FluidProxyUgen.kr('FluidKMeans/query', K2A.ar(trig),
|
||||||
|
id, clusters, maxiter,
|
||||||
|
this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,47 @@
|
|||||||
FluidKNNClassifier : FluidRTDataClient {
|
FluidKNNClassifier : FluidRealTimeModel {
|
||||||
|
|
||||||
|
var <>numNeighbours, <>weight;
|
||||||
|
|
||||||
*new {|server, numNeighbours = 3, weight = 1|
|
*new {|server, numNeighbours = 3, weight = 1|
|
||||||
^super.new1(server,[\numNeighbours,numNeighbours,\weight,weight]);
|
^super.new(server,[numNeighbours,weight])
|
||||||
|
.numNeighbours_(numNeighbours)
|
||||||
|
.weight_(weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prGetParams{^[this.numNeighbours,this.weight,-1,-1];}
|
||||||
|
|
||||||
|
fitMsg{|dataSet, labelSet|
|
||||||
|
^this.prMakeMsg(\fit, id, dataSet.id, labelSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fit{|dataSet, labelSet, action|
|
fit{|dataSet, labelSet, action|
|
||||||
this.prSendMsg(\fit,[dataSet.asSymbol, labelSet.asSymbol], action);
|
actions[\fit] = [nil,action];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet, labelSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
predict{|dataSet, labelSet, action|
|
predictMsg{|dataSet, labelSet|
|
||||||
this.prSendMsg(\predict,
|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
|
||||||
[dataSet.asSymbol, labelSet.asSymbol],
|
}
|
||||||
action);
|
|
||||||
|
predict{|dataSet, labelSet, action|
|
||||||
|
actions[\predict] = [nil, action];
|
||||||
|
this.prSendMsg(this.predictMsg(dataSet, labelSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictPointMsg{|buffer|
|
||||||
|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
predictPoint {|buffer, action|
|
predictPoint {|buffer, action|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
|
||||||
this.prSendMsg(\predictPoint,
|
this.prSendMsg(this.predictPointMsg(buffer));
|
||||||
[buffer.asUGenInput], action,
|
|
||||||
[string(FluidMessageResponse,_,_)]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, this.numNeighbours, this.weight,
|
||||||
|
this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,46 @@
|
|||||||
FluidKNNRegressor : FluidRTDataClient {
|
FluidKNNRegressor : FluidRealTimeModel {
|
||||||
|
|
||||||
|
var <>numNeighbours, <>weight;
|
||||||
|
|
||||||
*new {|server, numNeighbours = 3, weight = 1|
|
*new {|server, numNeighbours = 3, weight = 1|
|
||||||
^super.new1(server,[\numNeighbours,numNeighbours,\weight,weight]);
|
^super.new(server,[numNeighbours,weight])
|
||||||
|
.numNeighbours_(numNeighbours)
|
||||||
|
.weight_(weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prGetParams{^[this.numNeighbours,this.weight,-1,-1];}
|
||||||
|
|
||||||
|
fitMsg{|sourceDataSet, targetDataSet|
|
||||||
|
^this.prMakeMsg(\fit,this.id,sourceDataSet.id,targetDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fit{|sourceDataSet, targetDataSet, action|
|
fit{|sourceDataSet, targetDataSet, action|
|
||||||
this.prSendMsg(\fit,
|
actions[\fit] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
|
this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet));
|
||||||
action
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictMsg{ |sourceDataSet, targetDataSet|
|
||||||
|
^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
predict{ |sourceDataSet, targetDataSet,action|
|
predict{ |sourceDataSet, targetDataSet,action|
|
||||||
this.prSendMsg(\predict,
|
actions[\predict] = [nil, action];
|
||||||
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
|
this.prSendMsg(this.predictMsg(sourceDataSet, targetDataSet));
|
||||||
action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictPointMsg { |buffer|
|
||||||
|
^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
predictPoint { |buffer, action|
|
predictPoint { |buffer, action|
|
||||||
buffer = this.prEncodeBuffer(buffer);
|
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
|
||||||
this.prSendMsg(\predictPoint, [buffer], action,
|
this.prSendMsg(this.predictPointMsg(buffer));
|
||||||
[number(FluidMessageResponse,_,_)]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, this.numNeighbours, this.weight,
|
||||||
|
this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,81 +1,54 @@
|
|||||||
FluidLabelSetExistsError : Exception{
|
FluidLabelSet : FluidDataObject {
|
||||||
}
|
|
||||||
|
|
||||||
FluidLabelSet : FluidManipulationClient {
|
*new{|server| ^super.new(server) }
|
||||||
|
|
||||||
var <id;
|
addLabelMsg{|identifier,label|
|
||||||
classvar serverCaches;
|
^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol);
|
||||||
|
}
|
||||||
|
|
||||||
*initClass {
|
addLabel{|identifier, label, action|
|
||||||
serverCaches = FluidServerCache.new;
|
actions[\addLabel] = [nil, action];
|
||||||
|
this.prSendMsg(this.addLabelMsg(identifier,label));
|
||||||
}
|
}
|
||||||
|
|
||||||
*at{ |server, name|
|
updateLabelMsg{|identifier, label|
|
||||||
^serverCaches.tryPerform(\at, server, name)
|
^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
*new { |server,name|
|
updateLabel{|identifier, label, action|
|
||||||
serverCaches.at(server,name) !? {
|
actions[\updateLabel] = [nil,action];
|
||||||
FluidLabelSetExistsError("A FluidLabelSet called % already exists.".format(name)).throw;
|
this.prSendMsg(this.updateLabelMsg(identifier,label));
|
||||||
};
|
|
||||||
^super.new(server,FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init { |name|
|
getLabelMsg{|identifier|
|
||||||
this.baseinit(FluidManipulationClient.prServerString(name));
|
^this.prMakeMsg(\getLabel, id, identifier.asSymbol);
|
||||||
id = name;
|
}
|
||||||
this.cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache {
|
getLabel{|identifier, action|
|
||||||
serverCaches.initCache(server);
|
actions[\getLabel] = [string(FluidMessageResponse,_,_),action];
|
||||||
serverCaches.put(server,id,this);
|
this.prSendMsg(this.getLabelMsg(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
asString {
|
deleteLabelMsg{|identifier, action|
|
||||||
^"FluidLabelSet(%)".format(id).asString;
|
^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
asSymbol {
|
deleteLabel{|identifier, action|
|
||||||
^id
|
actions[\deleteLabel] = [nil, action];
|
||||||
|
this.prSendMsg(this.deleteLabelMsg(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
*asUGenInput { |input|
|
clearMsg { ^this.prMakeMsg(\clear,id); }
|
||||||
var ascii = input.asString.ascii;
|
|
||||||
^[ascii.size].addAll(ascii)
|
|
||||||
}
|
|
||||||
|
|
||||||
addLabel{|id, label, action|
|
clear { |action|
|
||||||
this.prSendMsg(\addLabel, [id.asString, label.asString],action);
|
actions[\clear] = [nil,action];
|
||||||
}
|
this.prSendMsg(this.clearMsg);
|
||||||
|
}
|
||||||
updateLabel{|id, label, action|
|
|
||||||
this.prSendMsg(\updateLabel, [id.asString, label.asString],action);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLabel{|id, action|
|
printMsg { ^this.prMakeMsg(\print,id); }
|
||||||
this.prSendMsg(\getLabel, [id.asString], action,[string(FluidMessageResponse,_,_)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteLabel{|id, action|
|
|
||||||
this.prSendMsg(\deleteLabel, [id.asString],action);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear {|action|
|
|
||||||
this.prSendMsg(\clear,[], action);
|
|
||||||
}
|
|
||||||
|
|
||||||
free {|action|
|
|
||||||
serverCaches.remove(server, id);
|
|
||||||
super.free;
|
|
||||||
}
|
|
||||||
|
|
||||||
*freeAll {|server|
|
|
||||||
serverCaches.do(server,{|x| x.free;});
|
|
||||||
}
|
|
||||||
|
|
||||||
print { |action|
|
print { |action=(postResponse)|
|
||||||
action ?? {action = postit};
|
actions[\print] = [string(FluidMessageResponse,_,_),action];
|
||||||
this.prSendMsg(\print,[], action, [string(FluidMessageResponse,_,_)]);
|
this.prSendMsg(this.printMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,103 +1,151 @@
|
|||||||
FluidMLPRegressor : FluidRTDataClient {
|
FluidMLPRegressor : FluidRealTimeModel {
|
||||||
|
|
||||||
const <identity = 0;
|
const <identity = 0;
|
||||||
const <sigmoid = 1;
|
const <sigmoid = 1;
|
||||||
const <relu = 2;
|
const <relu = 2;
|
||||||
const <tanh = 3;
|
const <tanh = 3;
|
||||||
|
|
||||||
|
var <>hidden, <>activation, <>outputActivation, <>tapIn, <>tapOut, <>maxIter, <>learnRate, <>momentum, <>batchSize, <>validation;
|
||||||
|
|
||||||
*new {|server, hidden = #[3,3] , activation = 2, outputActivation = 0, tapIn = 0, tapOut = -1,maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
|
*new {|server, hidden = #[3,3] , activation = 2, outputActivation = 0, tapIn = 0, tapOut = -1,maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
|
||||||
var hiddenCtrlLabels;
|
|
||||||
hidden = [hidden.size]++hidden;
|
^super.new(server, [hidden.size] ++ hidden ++ [activation, outputActivation, tapIn, tapOut, maxIter, learnRate, momentum, batchSize, validation])
|
||||||
|
.hidden_(hidden)
|
||||||
hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i};
|
.activation_(activation)
|
||||||
|
.outputActivation_(outputActivation)
|
||||||
^super.new1(server,
|
.tapIn_(tapIn)
|
||||||
[hiddenCtrlLabels,hidden].lace ++
|
.tapOut_(tapOut)
|
||||||
[
|
.maxIter_(maxIter)
|
||||||
\activation,activation,
|
.learnRate_(learnRate)
|
||||||
\outputActivation, outputActivation,
|
.momentum_(momentum)
|
||||||
\tapIn, tapIn,
|
.batchSize_(batchSize)
|
||||||
\tapOut, tapOut,
|
.validation_(validation);
|
||||||
\maxIter, maxIter,
|
|
||||||
\learnRate,learnRate,
|
|
||||||
\momentum, momentum,
|
|
||||||
\batchSize,batchSize,
|
|
||||||
\validation,validation,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clear{ |action|
|
prGetParams{
|
||||||
this.prSendMsg(\clear,action:action);
|
^[this.hidden.size] ++ this.hidden ++ [this.activation, this.outputActivation, this.tapIn, this.tapOut, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation, -1, -1]
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMsg{ ^this.prMakeMsg(\clear, id) }
|
||||||
|
|
||||||
|
clear{ |action|
|
||||||
|
actions[\clear] = [nil, action];
|
||||||
|
this.prSendMsg(this.clearMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fitMsg{|sourceDataSet, targetDataSet|
|
||||||
|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetDataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
fit{|sourceDataSet, targetDataSet, action|
|
fit{|sourceDataSet, targetDataSet, action|
|
||||||
this.prSendMsg(\fit,
|
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
|
this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet));
|
||||||
action,numbers(FluidMessageResponse,_,1,_)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
predict{ |sourceDataSet, targetDataSet, action|
|
predictMsg{|sourceDataSet, targetDataSet|
|
||||||
this.prSendMsg(\predict,
|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id);
|
||||||
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
|
}
|
||||||
action);
|
|
||||||
|
predict{|sourceDataSet, targetDataSet, action|
|
||||||
|
actions[\predict] = [nil,action];
|
||||||
|
this.prSendMsg(this.predictMsg(sourceDataSet,targetDataSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
predictPointMsg { |sourceBuffer, targetBuffer|
|
||||||
|
^this.prMakeMsg(\predictPoint,id,
|
||||||
|
this.prEncodeBuffer(sourceBuffer),
|
||||||
|
this.prEncodeBuffer(targetBuffer),
|
||||||
|
["/b_query", targetBuffer.asUGenInput]);
|
||||||
|
}
|
||||||
|
|
||||||
predictPoint { |sourceBuffer, targetBuffer, action|
|
predictPoint { |sourceBuffer, targetBuffer, action|
|
||||||
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
|
actions[\predictPoint] = [nil,{action.value(targetBuffer)}];
|
||||||
targetBuffer = this.prEncodeBuffer(targetBuffer);
|
this.predictPointMsg(sourceBuffer, targetBuffer);
|
||||||
this.prSendMsg(\predictPoint,
|
this.prSendMsg(this.predictPointMsg(sourceBuffer, targetBuffer));
|
||||||
[sourceBuffer.asUGenInput, targetBuffer.asUGenInput], action,outputBuffers:[targetBuffer]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer, tapIn = 0, tapOut = -1|
|
||||||
|
var params;
|
||||||
|
tapIn = tapIn ? this.tapIn;
|
||||||
|
tapOut = tapOut ? this.tapOut;
|
||||||
|
|
||||||
|
this.tapIn_(tapIn).tapOut_(tapOut);
|
||||||
|
|
||||||
|
params = this.prGetParams.drop(-2) ++ [this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer)];
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, *params);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FluidMLPClassifier : FluidRTDataClient {
|
FluidMLPClassifier : FluidRealTimeModel {
|
||||||
|
|
||||||
const <identity = 0;
|
const <identity = 0;
|
||||||
const <sigmoid = 1;
|
const <sigmoid = 1;
|
||||||
const <relu = 2;
|
const <relu = 2;
|
||||||
const <tanh = 3;
|
const <tanh = 3;
|
||||||
|
|
||||||
|
var <>hidden, <>activation, <> maxIter, <>learnRate, <> momentum, <>batchSize, <>validation;
|
||||||
|
|
||||||
*new {|server, hidden = #[3,3] , activation = 2, maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
|
*new {|server, hidden = #[3,3] , activation = 2, maxIter = 1000, learnRate = 0.0001, momentum = 0.9, batchSize = 50, validation = 0.2|
|
||||||
var hiddenCtrlLabels;
|
^super.new(server,[hidden.size] ++ hidden ++ [activation, maxIter, learnRate, momentum, batchSize, validation])
|
||||||
hidden = [hidden.size]++hidden;
|
.hidden_(hidden)
|
||||||
|
.activation_(activation)
|
||||||
hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i};
|
.maxIter_(maxIter)
|
||||||
|
.learnRate_(learnRate)
|
||||||
^super.new1(server,
|
.momentum_(momentum)
|
||||||
[hiddenCtrlLabels,hidden].lace ++
|
.batchSize_(batchSize)
|
||||||
[
|
.validation_(validation);
|
||||||
\activation,activation,
|
|
||||||
\maxIter, maxIter,
|
|
||||||
\learnRate,learnRate,
|
|
||||||
\momentum, momentum,
|
|
||||||
\batchSize,batchSize,
|
|
||||||
\validation,validation,
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prGetParams{
|
||||||
|
^[ this.hidden.size] ++ this.hidden ++ [this.activation, this.maxIter, this.learnRate, this.momentum, this.batchSize, this.validation, -1, -1];
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMsg{ ^this.prMakeMsg(\clear,id) }
|
||||||
|
|
||||||
clear{ |action|
|
clear{ |action|
|
||||||
this.prSendMsg(\clear,action:action);
|
actions[\clear] = [nil,action];
|
||||||
|
this.prSendMsg(this.clearMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fitMsg{|sourceDataSet, targetLabelSet|
|
||||||
|
^this.prMakeMsg(\fit,id,sourceDataSet.id, targetLabelSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
fit{|sourceDataSet, targetLabelSet, action|
|
fit{|sourceDataSet, targetLabelSet, action|
|
||||||
this.prSendMsg(\fit,
|
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
[sourceDataSet.asSymbol, targetLabelSet.asSymbol],
|
this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet));
|
||||||
action,numbers(FluidMessageResponse,_,1,_)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictMsg{|sourceDataSet, targetLabelSet|
|
||||||
|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
predict{ |sourceDataSet, targetLabelSet, action|
|
predict{ |sourceDataSet, targetLabelSet, action|
|
||||||
this.prSendMsg(\predict,
|
actions[\predict]=[nil,action];
|
||||||
[sourceDataSet.asSymbol, targetLabelSet.asSymbol],
|
this.prSendMsg(this.predictMsg(sourceDataSet,targetLabelSet));
|
||||||
action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predictPointMsg { |sourceBuffer|
|
||||||
|
^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer))
|
||||||
|
}
|
||||||
|
|
||||||
predictPoint { |sourceBuffer, action|
|
predictPoint { |sourceBuffer, action|
|
||||||
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
|
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
|
||||||
this.prSendMsg(\predictPoint,
|
this.prSendMsg(this.predictPointMsg(sourceBuffer));
|
||||||
[sourceBuffer], action, string(FluidMessageResponse,_,_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer|
|
||||||
|
|
||||||
|
var params = this.prGetParams.drop(-2) ++ [this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer)];
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, *params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,68 @@
|
|||||||
FluidNormalize : FluidRTDataClient {
|
FluidNormalize : FluidRealTimeModel {
|
||||||
|
|
||||||
|
var <>min, <>max, <>invert;
|
||||||
|
|
||||||
*new {|server, min = 0, max = 1, invert = 0|
|
*new {|server, min = 0, max = 1, invert = 0|
|
||||||
^super.new1(server,[\min,min,\max,max, \invert, invert]);
|
^super.new(server,[min,max,invert])
|
||||||
|
.min_(min).max_(max).invert_(invert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prGetParams{
|
||||||
|
^[this.min,this.max,this.invert,-1,-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fitMsg{|dataSet|
|
||||||
|
^this.prMakeMsg(\fit,id,dataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fit{|dataSet, action|
|
fit{|dataSet, action|
|
||||||
this.prSendMsg(\fit,[dataSet.asSymbol], action);
|
actions[\fit] = [nil,action];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
transform{|sourceDataSet, destDataSet, action|
|
transform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\transform,
|
actions[\transform] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
|
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fitTransformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fitTransform{|sourceDataSet, destDataSet, action|
|
fitTransform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\fitTransform,
|
actions[\fitTransform] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
|
this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformPointMsg{|sourceBuffer, destBuffer|
|
||||||
|
^this.prMakeMsg(\transformPoint,id,
|
||||||
|
this.prEncodeBuffer(sourceBuffer),
|
||||||
|
this.prEncodeBuffer(destBuffer),
|
||||||
|
["/b_query",destBuffer.asUGenInput]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
transformPoint{|sourceBuffer, destBuffer, action|
|
transformPoint{|sourceBuffer, destBuffer, action|
|
||||||
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
|
||||||
destBuffer = this.prEncodeBuffer(destBuffer);
|
this.prSendMsg(this.transformPointMsg(sourceBuffer, destBuffer));
|
||||||
this.prSendMsg(\transformPoint,
|
|
||||||
[sourceBuffer, destBuffer], action,
|
|
||||||
outputBuffers:[destBuffer]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer,min,max,invert|
|
||||||
|
|
||||||
|
min = min ? this.min;
|
||||||
|
max = max ? this.max;
|
||||||
|
invert = invert ? this.invert;
|
||||||
|
|
||||||
|
this.min_(min).max_(max).invert_(invert);
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, this.min, this.max, this.invert, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,62 @@
|
|||||||
FluidPCA : FluidRTDataClient {
|
FluidPCA : FluidRealTimeModel{
|
||||||
|
|
||||||
*new {|server, numDimensions = 2|
|
var <>numDimensions;
|
||||||
^super.new1(server,[\numDimensions,numDimensions]);
|
|
||||||
}
|
*new {|server, numDimensions = 2|
|
||||||
|
^super.new(server,[numDimensions]).numDimensions_(numDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
prGetParams{
|
||||||
|
^[numDimensions,-1,-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
fitMsg{|dataSet|
|
||||||
|
^this.prMakeMsg(\fit,id, dataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
fit{|dataSet, action|
|
fit{|dataSet, action|
|
||||||
this.prSendMsg(\fit,[dataSet.asSymbol], action);
|
actions[\fit] = [nil, action];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
transformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\transform, id, sourceDataSet.id, destDataSet.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
transform{|sourceDataSet, destDataSet, action|
|
transform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\transform,[sourceDataSet.asSymbol, destDataSet.asSymbol], action, [numbers(FluidMessageResponse,_,1,_)]);
|
actions[\transform] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
|
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
fitTransformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fitTransform{|sourceDataSet, destDataSet, action|
|
fitTransform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\fitTransform,[sourceDataSet.asSymbol, destDataSet.asSymbol], action, [numbers(FluidMessageResponse,_,1,_)]);
|
actions[\fitTransform] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
|
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
transformPointMsg{|sourceBuffer, destBuffer|
|
||||||
|
^this.prMakeMsg(\transformPoint,id,
|
||||||
|
this.prEncodeBuffer(sourceBuffer),
|
||||||
|
this.prEncodeBuffer(destBuffer),
|
||||||
|
["/b_query",destBuffer.asUGenInput]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
transformPoint{|sourceBuffer, destBuffer, action|
|
transformPoint{|sourceBuffer, destBuffer, action|
|
||||||
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
|
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
|
||||||
destBuffer = this.prEncodeBuffer(destBuffer);
|
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
|
||||||
this.prSendMsg(\transformPoint,[sourceBuffer, destBuffer], action, outputBuffers:[destBuffer]);
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer,numDimensions|
|
||||||
|
|
||||||
|
numDimensions = numDimensions ? this.numDimensions;
|
||||||
|
this.numDimensions_(numDimensions);
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, this.numDimensions, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
FluidProxyUgen : UGen {
|
||||||
|
|
||||||
|
var <>pluginname;
|
||||||
|
|
||||||
|
*kr { |pluginname...args|
|
||||||
|
^this.new1('control', pluginname,*args)
|
||||||
|
}
|
||||||
|
|
||||||
|
init { |pluginname...args|
|
||||||
|
this.pluginname = pluginname;
|
||||||
|
inputs = args;
|
||||||
|
rate = 'control';
|
||||||
|
}
|
||||||
|
|
||||||
|
name{
|
||||||
|
^pluginname.asString;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll{ |trig = 10, label, trigid = -1|
|
||||||
|
^super.poll(trig, label ? this.name, trigid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidServerCache {
|
||||||
|
|
||||||
|
var <cache;
|
||||||
|
|
||||||
|
*new{ ^super.new.init }
|
||||||
|
|
||||||
|
init{
|
||||||
|
cache = IdentityDictionary.new;
|
||||||
|
}
|
||||||
|
|
||||||
|
do { |server, func|
|
||||||
|
cache[server]!?{cache[server].do{|x|func.value(x)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
doAll {|func|
|
||||||
|
cache.do{|subCache|
|
||||||
|
subCache.do{|item|
|
||||||
|
func.value(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postln{
|
||||||
|
cache.postln;
|
||||||
|
}
|
||||||
|
|
||||||
|
at { |server,id|
|
||||||
|
^cache[server].tryPerform(\at,id)
|
||||||
|
}
|
||||||
|
|
||||||
|
includesKey{|server,key|
|
||||||
|
^cache[server].tryPerform(\includesKey,key)
|
||||||
|
}
|
||||||
|
|
||||||
|
put {|server,id,x|
|
||||||
|
cache[server][id] = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove { |server,id|
|
||||||
|
cache[server]!? {cache[server].removeAt(id)};
|
||||||
|
}
|
||||||
|
|
||||||
|
initCache {|server|
|
||||||
|
cache[server] ?? {
|
||||||
|
cache[server] = IdentityDictionary.new;
|
||||||
|
NotificationCenter.register(server,\newAllocators,this,
|
||||||
|
{
|
||||||
|
this.clearCache(server);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCache { |server|
|
||||||
|
cache[server] !?
|
||||||
|
{
|
||||||
|
var bundle = [];
|
||||||
|
cache[server].values.do{|i|
|
||||||
|
if(i.respondsTo(\freeMsg)){
|
||||||
|
bundle = bundle.add(i.freeMsg); //server objects
|
||||||
|
}{
|
||||||
|
i.free; //OSCFunc
|
||||||
|
}
|
||||||
|
};
|
||||||
|
server.listSendBundle(nil,bundle);
|
||||||
|
cache.removeAt(server);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,305 @@
|
|||||||
|
FluidServerObject
|
||||||
|
{
|
||||||
|
classvar serverCaches;
|
||||||
|
classvar count;
|
||||||
|
classvar persistent = true;
|
||||||
|
var <server,<id;
|
||||||
|
|
||||||
|
*initClass {
|
||||||
|
// serverCaches = FluidServerCache.new;
|
||||||
|
serverCaches = IdentityDictionary.new;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*initCache {|server|
|
||||||
|
serverCaches[this] ?? {serverCaches[this] = FluidServerCache.new};
|
||||||
|
serverCaches[this].initCache(server);
|
||||||
|
NotificationCenter.register(server,\newAllocators,this,{ count = 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
*newMsg{|id, params|
|
||||||
|
params = params !? {params.collect(_.asUGenInput)};
|
||||||
|
// ("Newms"++params).postln;
|
||||||
|
^['/cmd',this.objectClassName ++ '/new',id] ++ params
|
||||||
|
}
|
||||||
|
|
||||||
|
*new{ |server, id, params, action, callNew = true|
|
||||||
|
var newObj;
|
||||||
|
server ?? {server = Server.default};
|
||||||
|
if(server.serverRunning.not){"Server not running".warn};
|
||||||
|
id !? { id = id.asInteger }
|
||||||
|
?? { id = count; count = count + 1; };
|
||||||
|
newObj = super.newCopyArgs(server,id,action);
|
||||||
|
// params.postln;
|
||||||
|
if(callNew) {server.listSendMsg(this.newMsg(id,params))};
|
||||||
|
^newObj.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
cache {
|
||||||
|
this.class.initCache(server);
|
||||||
|
serverCaches[this.class].put(server,this.id,this);
|
||||||
|
}
|
||||||
|
|
||||||
|
uncache{
|
||||||
|
serverCaches[this.class].remove(server,id);
|
||||||
|
}
|
||||||
|
|
||||||
|
prMakeMsg{|msg,id...args|
|
||||||
|
^['/cmd',"%/%".format(this.class.objectClassName,msg),id].addAll(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeMsg {
|
||||||
|
var msg;
|
||||||
|
id ?? {" % already freed".format(this.class.name).warn; ^nil};
|
||||||
|
this.uncache;
|
||||||
|
msg = this.prMakeMsg(\free,id);
|
||||||
|
id = nil;
|
||||||
|
^msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
free{
|
||||||
|
var msg = this.freeMsg;
|
||||||
|
msg !? {server.listSendMsg(msg)} ?? {^nil};
|
||||||
|
}
|
||||||
|
|
||||||
|
*freeAll{|server|
|
||||||
|
serverCaches[this] !? {|cache|
|
||||||
|
cache.clearCache(server ? Server.default);
|
||||||
|
};
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
asUGenInput{ ^id }
|
||||||
|
|
||||||
|
asString {
|
||||||
|
^"%(%)".format(this.class.name,id).asString;
|
||||||
|
}
|
||||||
|
|
||||||
|
asSymbol {
|
||||||
|
^id.asSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
*objectClassName { ^this.name.asSymbol }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidBufProcessor : FluidServerObject
|
||||||
|
{
|
||||||
|
var <processAction;
|
||||||
|
var <outputBuffers;
|
||||||
|
var <freeWhenDone;
|
||||||
|
classvar responder;
|
||||||
|
classvar count;
|
||||||
|
|
||||||
|
*cmdPeriod {
|
||||||
|
serverCaches[this] !? {|cache|
|
||||||
|
cache.doAll{|processor| processor !? { processor.free;} };
|
||||||
|
serverCaches[this] = nil;
|
||||||
|
};
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*initCache {|server|
|
||||||
|
// "initcache".postln;
|
||||||
|
// this.done.postln;
|
||||||
|
super.initCache(server);
|
||||||
|
CmdPeriod.add(this);
|
||||||
|
if(serverCaches[this].includesKey(server,\processResponder).not)
|
||||||
|
{
|
||||||
|
serverCaches[this].put(server,\processResponder,OSCFunc({|m|
|
||||||
|
var id = m.last.asInteger;
|
||||||
|
// "I'm in the pizza hut".postln;
|
||||||
|
serverCaches[this].at(server,id) !? {|p|
|
||||||
|
// "I'm in the taco bell".postln ;
|
||||||
|
p!?{
|
||||||
|
p.processAction!?{|a|
|
||||||
|
var bufs = p.outputBuffers;
|
||||||
|
|
||||||
|
bufs = bufs.collect{|b|
|
||||||
|
if(b.isKindOf(Buffer))
|
||||||
|
{b}
|
||||||
|
{Buffer.cachedBufferAt(server,b)};
|
||||||
|
};
|
||||||
|
a.valueArray(valueArray(bufs));
|
||||||
|
};
|
||||||
|
if(p.freeWhenDone){p.free};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},this.done ,server.addr).fix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*new {|server,id,outputBuffers|
|
||||||
|
^super.new(server,id, nil, nil,false).init(outputBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
init{ |ob|
|
||||||
|
outputBuffers = ob;
|
||||||
|
}
|
||||||
|
|
||||||
|
*done {
|
||||||
|
^"/%/process".format(this.objectClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait {
|
||||||
|
var condition = Condition.new;
|
||||||
|
OSCFunc({
|
||||||
|
condition.unhang;
|
||||||
|
},this.class.done,server.addr).oneShot;
|
||||||
|
condition.hang;
|
||||||
|
}
|
||||||
|
|
||||||
|
processMsg {|params|
|
||||||
|
var msg;
|
||||||
|
var completionMsg = outputBuffers !? {
|
||||||
|
[["/sync"]] ++ outputBuffers.collect{|b| ["/b_query", b.asUGenInput]}
|
||||||
|
} ?? {[]};
|
||||||
|
|
||||||
|
// completionMsg.postln;
|
||||||
|
id ?? {Error("% already freed".format(this.class.name)).throw};
|
||||||
|
msg = this.prMakeMsg(\processNew,id).addAll(params).add(completionMsg);
|
||||||
|
// msg.postln;
|
||||||
|
^msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
processList { |params,shouldFree,action|
|
||||||
|
freeWhenDone = shouldFree;
|
||||||
|
processAction = action;
|
||||||
|
params = params.collect(_.asUGenInput);
|
||||||
|
server.listSendMsg(this.processMsg(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelMsg{
|
||||||
|
id ?? {Error("% already freed".format(this.class.name)).throw};
|
||||||
|
^this.prMakeMsg(\cancel, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel{
|
||||||
|
server.listSendMsg(this.cancelMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
kr{ ^FluidProxyUgen.kr(this.objectClassName ++ "Monitor",id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidOSCPatternInversion : OSCMessageDispatcher
|
||||||
|
{
|
||||||
|
value {|msg, time, addr, recvPort|
|
||||||
|
var msgpath = msg[0];
|
||||||
|
active.keysValuesDo({|key, func|
|
||||||
|
if(msgpath.matchOSCAddressPattern(key), {func.value(msg, time, addr, recvPort);});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FluidDataObject : FluidServerObject
|
||||||
|
{
|
||||||
|
classvar postResponse;
|
||||||
|
|
||||||
|
var <actions;
|
||||||
|
|
||||||
|
*initClass{
|
||||||
|
postResponse = _.postln;
|
||||||
|
}
|
||||||
|
|
||||||
|
*initCache{ |server|
|
||||||
|
super.initCache(server);
|
||||||
|
if(serverCaches[this].includesKey(server,\messageResponder).not)
|
||||||
|
{
|
||||||
|
serverCaches[this].put(server,\messageResponder,OSCFunc.new({|m|
|
||||||
|
var id = m[1].asInteger;
|
||||||
|
var method;
|
||||||
|
serverCaches[this].at(server,id) !? { |p|
|
||||||
|
method = m[0].asString.findRegexp("/"++this.name++"/(.*)")[1][1].asSymbol;
|
||||||
|
p.actions[method] !? {|a|
|
||||||
|
//two items: parser and action
|
||||||
|
var parser = a[0];
|
||||||
|
var action = a[1];
|
||||||
|
var result = FluidMessageResponse.collectArgs(parser,m[2..]);
|
||||||
|
action.value(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},'/' ++ this.objectClassName ++ '/*',server.addr, dispatcher:FluidOSCPatternInversion.new).fix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*new{|server...args|
|
||||||
|
// args.flatten.postln;
|
||||||
|
^super.new(server,params:args.flatten).init;
|
||||||
|
}
|
||||||
|
|
||||||
|
*cachedInstanceAt{|server,id|
|
||||||
|
this.initCache;
|
||||||
|
^serverCaches[this].at(server,id);
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
actions = IdentityDictionary.new;
|
||||||
|
}
|
||||||
|
|
||||||
|
prEncodeBuffer { |buf| buf !? {^buf.asUGenInput} ?? {^-1} }
|
||||||
|
|
||||||
|
prSendMsg {|msg| server !? {server.listSendMsg(msg)};}
|
||||||
|
|
||||||
|
colsMsg { ^this.prMakeMsg(\cols,id);}
|
||||||
|
|
||||||
|
cols{ |action=(postResponse)|
|
||||||
|
actions[\cols] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
|
this.prSendMsg(this.colsMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
readMsg { |filename| ^this.prMakeMsg(\read,id,filename.asString);}
|
||||||
|
|
||||||
|
read{|filename, action|
|
||||||
|
actions[\read] = [nil,action];
|
||||||
|
this.prSendMsg(this.readMsg(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
writeMsg {|filename|
|
||||||
|
// ^['/cmd',this.class.name ++ '/write',id,filename.asString]
|
||||||
|
^this.prMakeMsg(\write,id,filename.asString);
|
||||||
|
}
|
||||||
|
|
||||||
|
write{|filename, action|
|
||||||
|
actions[\write] = [nil,action];
|
||||||
|
this.prSendMsg(this.writeMsg(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeMsg{
|
||||||
|
// ^['/cmd',this.class.name ++ '/size',id]
|
||||||
|
^this.prMakeMsg(\size,id);
|
||||||
|
}
|
||||||
|
|
||||||
|
size {|action=(postResponse)|
|
||||||
|
actions[\size] = [numbers(FluidMessageResponse,_,1,_),action];
|
||||||
|
this.prSendMsg(this.sizeMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidModelObject : FluidDataObject
|
||||||
|
{
|
||||||
|
prGetParams{
|
||||||
|
"Subclass should provide this".throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
prUpdateStateMsg{
|
||||||
|
var params = this.prGetParams.value.collect(_.asUGenInput);
|
||||||
|
^this.prMakeMsg(\setParams,id) ++ params;
|
||||||
|
}
|
||||||
|
|
||||||
|
prSendMsg {|msg|
|
||||||
|
//These need to happen sequentially, but not simultaneously
|
||||||
|
//sending as a bundle makes reasoning about timing w/r/t other
|
||||||
|
//commands more awkward, unless we set the offet to 0 (in which case,
|
||||||
|
//noisy 'late' messages)
|
||||||
|
super.prSendMsg(this.prUpdateStateMsg);
|
||||||
|
super.prSendMsg(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidRealTimeModel : FluidModelObject
|
||||||
|
{
|
||||||
|
*new{ |server, params|
|
||||||
|
^super.new(server,params++[-1,-1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,29 +1,58 @@
|
|||||||
FluidStandardize : FluidRTDataClient {
|
FluidStandardize : FluidRealTimeModel {
|
||||||
*new {|server, invert = 0|
|
|
||||||
^super.new1(server,[\invert, invert]);
|
var <>invert;
|
||||||
|
|
||||||
|
*new {|server, invert = 0|
|
||||||
|
^super.new(server,[invert]).invert_(invert);
|
||||||
}
|
}
|
||||||
|
|
||||||
fit{|dataSet, action|
|
prGetParams{
|
||||||
this.prSendMsg(\fit, [dataSet.asSymbol], action);
|
^[this.invert, -1, 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
fitMsg{|dataSet|
|
||||||
|
^this.prMakeMsg(\fit,id,dataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fit{|dataSet, action|
|
||||||
|
actions[\fit] = [nil, action];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\transform,id,sourceDataSet.id,destDataSet.id);
|
||||||
|
}
|
||||||
|
|
||||||
transform{|sourceDataSet, destDataSet, action|
|
transform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\transform,
|
actions[\transform] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
|
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fitTransformMsg{|sourceDataSet, destDataSet|
|
||||||
|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
fitTransform{|sourceDataSet, destDataSet, action|
|
fitTransform{|sourceDataSet, destDataSet, action|
|
||||||
this.prSendMsg(\fitTransform,
|
actions[\fitTransform] = [nil,action];
|
||||||
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
|
this.prSendMsg(this.fitTransformMsg(sourceDataSet, destDataSet));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
transformPointMsg{|sourceBuffer, destBuffer|
|
||||||
|
^this.prMakeMsg(\transformPoint, id, this.prEncodeBuffer(sourceBuffer), this.prEncodeBuffer(destBuffer),["/b_query",destBuffer.asUGenInput]);
|
||||||
|
}
|
||||||
|
|
||||||
transformPoint{|sourceBuffer, destBuffer, action|
|
transformPoint{|sourceBuffer, destBuffer, action|
|
||||||
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
|
actions[\transformPoint] = [nil, {action.value(destBuffer)}];
|
||||||
destBuffer = this.prEncodeBuffer(destBuffer);
|
this.prSendMsg(this.transformPointMsg(sourceBuffer,destBuffer));
|
||||||
this.prSendMsg(\transformPoint,
|
|
||||||
[sourceBuffer, destBuffer], action, outputBuffers:[destBuffer]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer,invert|
|
||||||
|
|
||||||
|
invert = invert ? this.invert;
|
||||||
|
this.invert_(invert);
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr(this.class.name.asString++'/query', K2A.ar(trig),
|
||||||
|
id, this.invert, this.prEncodeBuffer(inputBuffer), this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,122 +1,228 @@
|
|||||||
// load a source
|
// load a source folder
|
||||||
b = Buffer.read(s,"/Volumes/machins/projets/newsfeed/sons/textes/Audio/synth/fromtexttospeech-AmE-George.wav")
|
~loader = FluidLoadFolder(File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/");
|
||||||
b.play
|
~loader.play;
|
||||||
|
|
||||||
//slightly oversegment with novelty
|
//slightly oversegment with novelty
|
||||||
//segments should still make sense but might cut a few elements in 2 or 3
|
//segments should still make sense but might cut a few elements in 2 or 3
|
||||||
~originalslices = Buffer(s);
|
~slicer = FluidSliceCorpus({ |src,start,num,dest| FluidBufNoveltySlice.kr(src,start,num,indices:dest, feature: 1, kernelSize: 29, threshold: 0.1, filterSize: 5, hopSize: 128, blocking: 1)});
|
||||||
FluidBufNoveltySlice.process(s, b, indices: ~originalslices, feature: 1, kernelSize: 29, threshold: 0.05, filterSize: 5, hopSize: 128, action: {~originalslices.numFrames.postln;})
|
~slicer.play(s, ~loader.buffer,~loader.index);
|
||||||
|
|
||||||
//test the segmentation by looping them
|
//test the segmentation by looping them
|
||||||
(
|
(
|
||||||
{
|
~originalindices = Array.newFrom(~slicer.index.keys).sort{|a,b| ~slicer.index[a][\bounds][0]< ~slicer.index[b][\bounds][0]}.collect{|x|~slicer.index[x][\bounds]};
|
||||||
BufRd.ar(1, b,
|
d = {arg start=0, end = 44100;
|
||||||
Phasor.ar(0,1,
|
BufRd.ar(1, ~loader.buffer, Phasor.ar(0,1,start,end,start),0,1);
|
||||||
BufRd.kr(1, ~originalslices,
|
|
||||||
MouseX.kr(0, BufFrames.kr(~originalslices) - 1), 0, 1),
|
|
||||||
BufRd.kr(1, ~originalslices,
|
|
||||||
MouseX.kr(1, BufFrames.kr(~originalslices)), 0, 1),
|
|
||||||
BufRd.kr(1,~originalslices,
|
|
||||||
MouseX.kr(0, BufFrames.kr(~originalslices) - 1), 0, 1)), 0, 1);
|
|
||||||
}.play;
|
}.play;
|
||||||
|
|
||||||
|
w = Window.new(bounds:Rect(100,100,400,60)).front;
|
||||||
|
b = ControlSpec(0, ~originalindices.size - 1, \linear, 1); // min, max, mapping, step
|
||||||
|
c = StaticText(w, Rect(340, 20, 50, 20)).align_(\center);
|
||||||
|
a = Slider(w, Rect(10, 20, 330, 20))
|
||||||
|
.action_({var val = b.map(a.value).asInteger;
|
||||||
|
c.string_(val.asString);
|
||||||
|
d.set(\start,~originalindices[val][0], \end, ~originalindices[val][1]);
|
||||||
|
});
|
||||||
)
|
)
|
||||||
|
|
||||||
//analyse each segment with MFCCs in a dataset
|
//analyse each segment with 20 MFCCs in a dataset and spectralshapes in another one
|
||||||
~originalslices.getn(0,~originalslices.numFrames, {|x|~originalslicesarray = x; if ((x.last != b.numFrames), {~originalslicesarray = ~originalslicesarray ++ (b.numFrames)}); });//retrieve the indices and add the file boundary at the end if not there already
|
(
|
||||||
|
~featuresbuf = 4.collect{Buffer.new};
|
||||||
|
~statsbuf = 4.collect{Buffer.new};
|
||||||
|
~flatbuf = 4.collect{Buffer.new};
|
||||||
|
~slicesMFCC = FluidDataSet(s);
|
||||||
|
~slicesShapes = FluidDataSet(s);
|
||||||
|
~extractor = FluidProcessSlices({|src,start,num,data|
|
||||||
|
var features, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice;
|
||||||
|
label = data.key;
|
||||||
|
voice = data.value[\voice];
|
||||||
|
features = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1, numCoeffs: 20, features:~featuresbuf[voice],trig:1,blocking: 1);
|
||||||
|
stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1);
|
||||||
|
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
|
||||||
|
writer = FluidDataSetWr.kr(~slicesMFCC,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
|
||||||
|
features = FluidBufSpectralShape.kr(src,startFrame:start,numFrames:num,numChans:1, features:~featuresbuf[voice],trig:Done.kr(writer),blocking: 1);
|
||||||
|
stats = FluidBufStats.kr(~featuresbuf[voice],stats:~statsbuf[voice],trig:Done.kr(features),blocking: 1);
|
||||||
|
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
|
||||||
|
writer = FluidDataSetWr.kr(~slicesShapes,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1);
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
//iterates through the
|
|
||||||
//a few buffers and our dataset - with back and forth from the language
|
|
||||||
(
|
(
|
||||||
~mfccs = Buffer(s);
|
t = Main.elapsedTime;
|
||||||
~stats = Buffer(s);
|
~extractor.play(s,~loader.buffer, ~slicer.index, action:{(Main.elapsedTime - t).postln;"Analysis done".postln});
|
||||||
~flat = Buffer(s);
|
|
||||||
~slices = FluidDataSet(s,\slices);
|
|
||||||
|
|
||||||
Routine{
|
|
||||||
s.sync;
|
|
||||||
(~originalslicesarray.size - 1).do{|i|
|
|
||||||
FluidBufMFCC.process(s, b, startFrame: ~originalslicesarray[i], numFrames: (~originalslicesarray[i+1] - ~originalslicesarray[i]), numChans: 1,features: ~mfccs, numCoeffs: 20, action: {
|
|
||||||
FluidBufStats.process(s, ~mfccs, startChan: 1, stats: ~stats, action: {
|
|
||||||
FluidBufFlatten.process(s, ~stats, ~flat, action: {
|
|
||||||
~slices.addPoint(i.asSymbol, ~flat);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}.play;
|
|
||||||
)
|
)
|
||||||
|
|
||||||
~slices.print
|
~slicesMFCC.print
|
||||||
|
~slicesShapes.print
|
||||||
|
|
||||||
//run a window over consecutive segments, forcing them in 2 classes, and merging the consecutive segments of similar class
|
//run a window over consecutive segments, forcing them in 2 classes, and merging the consecutive segments of similar class
|
||||||
//we overlap the analysis with the last (original) slice to check for continuity
|
//we overlap the analysis with the last (original) slice to check for continuity
|
||||||
(
|
(
|
||||||
~winSize = 4;//the number of consecutive items to split in 2 classes;
|
~winSize = 4;//the number of consecutive items to split in 2 classes;
|
||||||
|
~curated = FluidDataSet(s);
|
||||||
~query = FluidDataSetQuery(s);
|
~query = FluidDataSetQuery(s);
|
||||||
~kmeans = FluidKMeans(s,2,100);
|
~stan = FluidStandardize(s);
|
||||||
~windowDS = FluidDataSet(s,\windowDS);
|
~kmeans = FluidKMeans(s,2,1000);
|
||||||
~windowLS = FluidLabelSet(s,\windowLS);
|
~windowDS = FluidDataSet(s);
|
||||||
|
~windowLS = FluidLabelSet(s);
|
||||||
)
|
)
|
||||||
|
|
||||||
(
|
//curate stats (MFCCs)
|
||||||
Routine{
|
~query.clear
|
||||||
~indices = [0];
|
~query.addRange((0*20)+1,10);
|
||||||
~head = 0;
|
~query.transform(~slicesMFCC,~curated);
|
||||||
|
|
||||||
~sliceDict = Dictionary.new(4);
|
|
||||||
~tempDict = Dictionary.new(4);
|
|
||||||
|
|
||||||
~slices.dump{|x|~sliceDict = x;};
|
|
||||||
s.sync;
|
|
||||||
|
|
||||||
while ( {~head <= (~originalslicesarray.size - ~winSize)},
|
|
||||||
{
|
|
||||||
var step = ~winSize - 1;
|
|
||||||
var nbass = [];
|
|
||||||
//run a process on ~winSize items from ~head (with an overlap of 1)
|
|
||||||
//copy the items to a subdataset
|
|
||||||
~winSize.do{|i|
|
|
||||||
~tempDict.put((i.asString), ~sliceDict["data"][(i+~head).asString]);//here one could curate which stats to take
|
|
||||||
"whichslices:%\n".postf(i+~head);
|
|
||||||
};
|
|
||||||
~windowDS.load(Dictionary.newFrom([\cols, 133, \data, ~tempDict]));
|
|
||||||
s.sync;
|
|
||||||
"% - loaded\n".postf(~head);
|
|
||||||
|
|
||||||
//kmeans 2 and retrieve ordered array of class assignations
|
//OR
|
||||||
~kmeans.fitPredict(~windowDS, ~windowLS, {|x| nbass = x;});
|
//curate stats (moments)
|
||||||
s.sync;
|
~query.clear
|
||||||
"% - fitted1: ".postf(~head); nbass.postln;
|
~query.addRange(0,3);
|
||||||
|
~query.transform(~slicesShapes,~curated);
|
||||||
|
|
||||||
// if (nbass.includes(0.0), { Routine{~kmeans.fitPredict(~windowDS, ~windowLS, {|x| nbass = x; "% - fitted2: ".postf(~head); nbass.postln; s.sync;});}.play; });
|
//OR
|
||||||
|
//curate both
|
||||||
|
~query.clear
|
||||||
|
~query.addColumn(0);//add col 0 (mean of mfcc0 as 'loudness')
|
||||||
|
~query.transform(~slicesMFCC,~curated);//mfcc0 as loudness
|
||||||
|
~query.clear;
|
||||||
|
~query.addRange(0,3);//add some spectral moments
|
||||||
|
~query.transformJoin(~slicesShapes, ~curated, ~curated);//join in centroids
|
||||||
|
|
||||||
~windowLS.dump{|x|~assignments = x.at("data").asSortedArray.flop[1].flatten;};
|
//optionally standardize in place
|
||||||
s.sync;
|
~stan.fitTransform(~curated, ~curated);
|
||||||
"% - assigned ".postf(~head);
|
|
||||||
|
|
||||||
~assignments.postln;
|
~curated.print
|
||||||
|
|
||||||
step.do{|i|
|
//retrieve the dataset as dictionary
|
||||||
if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])});
|
~curated.dump{|x|~sliceDict = x;};
|
||||||
};
|
|
||||||
|
|
||||||
~head = ~head + step;
|
~originalslicesarray = (~originalindices.flatten ++ ~loader.buffer.numFrames).asSet.asArray.sort
|
||||||
"-----------------".postln;
|
~orginalkeys = Array.newFrom(~slicer.index.keys).sort{|a,b| ~slicer.index[a][\bounds][0]< ~slicer.index[b][\bounds][0]}
|
||||||
});
|
|
||||||
|
//the windowed function, recursive to deal with sync dependencies
|
||||||
|
(
|
||||||
|
~windowedFunct = {arg head, winSize, overlap;
|
||||||
|
var nbass = [], assignments = [], tempDict = ();
|
||||||
|
//check the size of everything to not overrun
|
||||||
|
winSize = (~originalslicesarray.size - head).min(winSize);
|
||||||
|
//copy the items to a subdataset from hear
|
||||||
|
winSize.do{|i|
|
||||||
|
tempDict.put((i.asString), ~sliceDict["data"][(~orginalkeys[(i+head)]).asString]);//here one could curate which stats to take
|
||||||
|
// "whichslices:%\n".postf(i+head);
|
||||||
|
};
|
||||||
|
~windowDS.load(Dictionary.newFrom([\cols, ~sliceDict["cols"].asInteger, \data, tempDict]), action: {
|
||||||
|
// "% - loaded\n".postf(head);
|
||||||
|
|
||||||
|
//kmeans 2 and retrieve ordered array of class assignations
|
||||||
|
~kmeans.fitPredict(~windowDS, ~windowLS, action: {|x|
|
||||||
|
nbass = x;
|
||||||
|
// "% - fitted1: ".postf(head); nbass.postln;
|
||||||
|
|
||||||
|
if (nbass.includes(winSize.asFloat), {
|
||||||
|
~kmeans.fitPredict(~windowDS, ~windowLS, {|x|
|
||||||
|
nbass = x;
|
||||||
|
// "% - fitted2: ".postf(head); nbass.postln;
|
||||||
|
if (nbass.includes(winSize.asFloat), {
|
||||||
|
~kmeans.fitPredict(~windowDS, ~windowLS, {|x|
|
||||||
|
nbass = x;
|
||||||
|
// "% - fitted3: ".postf(head); nbass.postln;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
~windowLS.dump{|x|
|
||||||
|
var assignments = x.at("data").asSortedArray.flop[1].flatten;
|
||||||
|
"% - assigned ".postf(head);
|
||||||
|
|
||||||
//leftovers
|
assignments.postln;
|
||||||
if ( (~originalslicesarray.size - ~head) > 1, {
|
|
||||||
//run a process on (a.size - ~head) items from ~head
|
(winSize-1).do{|i|
|
||||||
(~originalslicesarray.size - ~head - 1).do{|i|
|
if (assignments[i+1] != assignments[i], {
|
||||||
if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])});
|
~newindices= ~newindices ++ (~originalslicesarray[head+i+1]).asInteger;
|
||||||
// (~head+i).postln;
|
~newkeys = ~newkeys ++ (~orginalkeys[head+i+1]);
|
||||||
};
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
//if we still have some frames to do, do them
|
||||||
|
if (((winSize + head) < ~originalslicesarray.size), {
|
||||||
|
"-----------------".postln;
|
||||||
|
~windowedFunct.value(head + winSize - overlap, winSize, overlap);
|
||||||
|
}, {~newindices = (~newindices ++ ~loader.buffer.numFrames); "done".postln;});//if we're done close the books
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
~indices.postln;
|
}
|
||||||
}.play;
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{var i = 8;BufRd.ar(1,b,Line.ar(~originalslicesarray[i],~originalslicesarray[i+1],(~originalslicesarray[i+1] - ~originalslicesarray[i])/b.sampleRate, doneAction: 2))}.play;
|
//the job
|
||||||
{var i = 4;BufRd.ar(1,b,Line.ar(~indices[i],~indices[i+1],(~indices[i+1] - ~indices[i])/b.sampleRate, doneAction: 2))}.play;
|
|
||||||
|
//test 1 - start at the begining, consider 4 items at a time, make 2 clusters, overlap 1
|
||||||
|
~newindices = [~originalslicesarray[0]]; ~newkeys = [~orginalkeys[0]];
|
||||||
|
~windowedFunct.value(0, 4, 1);
|
||||||
|
|
||||||
|
//OPTIONAL: try again with more clusters (3) and a wider window (6) and more overlap (2)
|
||||||
|
~newindices = [~originalslicesarray[0]]; ~newkeys = [~orginalkeys[0]];
|
||||||
|
~kmeans.numClusters = 3;
|
||||||
|
~windowedFunct.value(0,6,2);
|
||||||
|
|
||||||
|
//compare sizes
|
||||||
|
~orginalkeys.size
|
||||||
|
~newkeys.size;
|
||||||
|
|
||||||
//export to reaper
|
//export to reaper
|
||||||
|
(
|
||||||
|
//first create a new file that ends with rpp - it will overwrite if the file exists
|
||||||
|
f = File.new("/tmp/clusteredslices-" ++ Date.getDate.stamp ++".rpp","w+");
|
||||||
|
|
||||||
|
if (f.isOpen , {
|
||||||
|
var path, prevpath ="", sr, count, dur;
|
||||||
|
//write the header
|
||||||
|
f.write("<REAPER_PROJECT 0.1 \"5.99/OSX64\" 1603037150\n\n");
|
||||||
|
|
||||||
|
//a first track with the originalslicearray
|
||||||
|
//write the track header
|
||||||
|
f.write("<TRACK\nNAME \"novelty output\"\n");
|
||||||
|
// iterate through the items in the track
|
||||||
|
~orginalkeys.do{|v, i|
|
||||||
|
path = ~slicer.index[v][\path];
|
||||||
|
if (path != prevpath, {
|
||||||
|
sr = ~slicer.index[v][\sr];
|
||||||
|
prevpath = path;
|
||||||
|
count = 0;
|
||||||
|
});
|
||||||
|
dur = ~originalslicesarray[i+1] - ~originalslicesarray[i];
|
||||||
|
if ( dur > 0, {
|
||||||
|
f.write("<ITEM\nPOSITION " ++ (~originalslicesarray[i] / sr) ++ "\nLENGTH " ++ (dur / sr) ++ "\nNAME \"" ++ v ++ "\"\nSOFFS " ++ (count / sr) ++ "\n<SOURCE WAVE\nFILE \"" ++ path ++ "\"\n>\n>\n");
|
||||||
|
});
|
||||||
|
count = count + dur;
|
||||||
|
};
|
||||||
|
//write the track footer
|
||||||
|
f.write(">\n");
|
||||||
|
|
||||||
|
// a second track with the new ~indices
|
||||||
|
prevpath = "";
|
||||||
|
//write the track header
|
||||||
|
f.write("<TRACK\nNAME \"clustered output\"\n");
|
||||||
|
// iterate through the items in the track
|
||||||
|
~newkeys.do{|v, i|
|
||||||
|
path = ~slicer.index[v][\path];
|
||||||
|
if (path != prevpath, {
|
||||||
|
sr = ~slicer.index[v][\sr];
|
||||||
|
prevpath = path;
|
||||||
|
count = 0;
|
||||||
|
});
|
||||||
|
dur = ~newindices[i+1] - ~newindices[i];
|
||||||
|
if (dur > 0, {
|
||||||
|
f.write("<ITEM\nPOSITION " ++ (~newindices[i] / sr) ++ "\nLENGTH " ++ (dur / sr) ++ "\nNAME \"" ++ v ++ "\"\nSOFFS " ++ (count / sr) ++ "\n<SOURCE WAVE\nFILE \"" ++ path ++ "\"\n>\n>\n");
|
||||||
|
});
|
||||||
|
count = count + dur;
|
||||||
|
};
|
||||||
|
//write the track footer
|
||||||
|
f.write(">\n");
|
||||||
|
|
||||||
|
//write the footer
|
||||||
|
f.write(">\n");
|
||||||
|
f.close;
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
(then open the time-stamped reaper file clusterdslice in the folder tmp)
|
||||||
@ -0,0 +1,330 @@
|
|||||||
|
// Lookup in a KDTree using melbands
|
||||||
|
// Demonstration of a massive parallel approach to batch process swiftly in SC
|
||||||
|
|
||||||
|
s.options.numBuffers = 16384 //The method below for doing the analysus quickly needs lots of buffers
|
||||||
|
s.reboot
|
||||||
|
|
||||||
|
//Step 0: Make a corpus
|
||||||
|
|
||||||
|
//We'll jam together some random flucoma sounds for illustrative purposes
|
||||||
|
//Get some files
|
||||||
|
(
|
||||||
|
~audioexamples_path = File.realpath(FluidBufMelBands.class.filenameSymbol).dirname.withTrailingSlash +/+ "../AudioFiles/*.wav";
|
||||||
|
~allTheSounds = SoundFile.collect(~audioexamples_path);
|
||||||
|
~testSounds = ~allTheSounds;
|
||||||
|
~testSounds.do{|f| f.path.postln}; // print out the files that are loaded
|
||||||
|
)
|
||||||
|
|
||||||
|
//Load the files into individual buffers:
|
||||||
|
(
|
||||||
|
~audio_buffers = ~testSounds.collect{|f|
|
||||||
|
Buffer.readChannel(
|
||||||
|
server: s,
|
||||||
|
path:f.path,
|
||||||
|
channels:[0],
|
||||||
|
action:{("Loaded" + f.path).postln;}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
)
|
||||||
|
|
||||||
|
//Do a segmentation of each buffer, in parallel
|
||||||
|
(
|
||||||
|
fork{
|
||||||
|
~index_buffers = ~audio_buffers.collect{Buffer.new};
|
||||||
|
s.sync;
|
||||||
|
~count = ~audio_buffers.size;
|
||||||
|
~audio_buffers.do{|src,i|
|
||||||
|
FluidBufOnsetSlice.process(
|
||||||
|
server:s,
|
||||||
|
source:src,
|
||||||
|
indices:~index_buffers[i],
|
||||||
|
metric: 9,
|
||||||
|
threshold:0.2,
|
||||||
|
minSliceLength: 17,
|
||||||
|
action:{
|
||||||
|
(~testSounds[i].path ++ ":" + ~index_buffers[i].numFrames + "slices").postln;
|
||||||
|
~count = ~count - 1;
|
||||||
|
if(~count == 0){"Done slicing".postln};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// we now have an array of index buffers, one per source buffer, each containing the segmentation points as a frame positions
|
||||||
|
// this allows us to make an array of sizes
|
||||||
|
~index_buffers.collect{|b| b.numFrames}.sum
|
||||||
|
|
||||||
|
//For each of these segments, let's make a datapoint using the mean melbands.
|
||||||
|
// There's a number of ways of skinning this cat w/r/t telling the server what to do, but here we want to minimize traffic between language and server, and also produce undertsandable code
|
||||||
|
|
||||||
|
//First, we'll grab the onset points as language-side arrays, then scroll through each slice getting the mean melbands
|
||||||
|
(
|
||||||
|
// - a dataset to keep the mean melbands in
|
||||||
|
~mels = FluidDataSet(s);
|
||||||
|
// - a dictionary to keep the slice points in for later playback
|
||||||
|
~slices = Dictionary();
|
||||||
|
//The code below (as well as needing lots of buffers), creates lots of threads and we need a big ass scheduling queue
|
||||||
|
~clock = TempoClock(queueSize:8192);
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Do the Mel analysis in a cunning parallel fashion
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var counter, remaining;
|
||||||
|
var condition = Condition.new; // used to create a test condition to pause the routine ...
|
||||||
|
var index_arrays = Dictionary();
|
||||||
|
|
||||||
|
"Process started. Please wait.".postln;
|
||||||
|
|
||||||
|
~total_slice_count = ~index_buffers.collect{|b| b.numFrames}.sum + ~index_buffers.size; //we get an extra slice in buffer
|
||||||
|
~featurebuffers = ~total_slice_count.collect{Buffer.new}; // create a buffer per slice
|
||||||
|
|
||||||
|
//Make our dictionary FluidDataSet-shaped
|
||||||
|
~slices.put("cols",3);//[bufnum,start,end] for each slice
|
||||||
|
~slices.put("data",Dictionary());
|
||||||
|
|
||||||
|
//Collect each set of onsets into a language side array and store them in a dict
|
||||||
|
~index_buffers.do{|b,i| // iterate over the 4 buffers
|
||||||
|
{
|
||||||
|
b.loadToFloatArray( // load to language side array
|
||||||
|
action:{|indices|
|
||||||
|
//Glue the first and last samples of the buffer on to the index list, and place in dictionary wiht the
|
||||||
|
//Buffer object as a key
|
||||||
|
|
||||||
|
index_arrays.put(~audio_buffers[i], Array.newFrom([0] ++ indices ++ (~audio_buffers[i].numFrames - 1)));
|
||||||
|
|
||||||
|
if(i==(~index_buffers.size-1)) {condition.unhang};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}.fork(stackSize:~total_slice_count);
|
||||||
|
};
|
||||||
|
condition.hang; //Pause until all the callbacks above have completed
|
||||||
|
"Arrays loaded. Starting on the analysis, please wait.".postln;
|
||||||
|
|
||||||
|
//For each of these lists of points, we want to scroll over the indices in pairs and get some mel bands
|
||||||
|
counter = 0;
|
||||||
|
remaining = ~total_slice_count;
|
||||||
|
|
||||||
|
s.sync;
|
||||||
|
|
||||||
|
// now iterate over Dict and calc melbands
|
||||||
|
|
||||||
|
index_arrays.keysValuesDo{|buffer, indices|
|
||||||
|
indices.doAdjacentPairs{|start,end,num|
|
||||||
|
var analysis = Routine({|counter|
|
||||||
|
|
||||||
|
FluidBufMelBands.processBlocking(
|
||||||
|
server:s,
|
||||||
|
source:buffer,
|
||||||
|
startFrame:start,
|
||||||
|
numFrames:(end-1) - start,
|
||||||
|
features:~featurebuffers[counter],
|
||||||
|
action:{
|
||||||
|
remaining = remaining - 1;
|
||||||
|
if(remaining == 0) { ~numMelBands = ~featurebuffers[0].numChannels;condition.unhang };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
~slices["data"].put(counter,[buffer.bufnum,start,end]);
|
||||||
|
|
||||||
|
//I'm spawning new threads to wait for the analysis callback from the server. The final callback will un-hang this thread
|
||||||
|
analysis.value(counter); //Done differently to other blocks because I need to pass in the value of counter
|
||||||
|
counter = counter + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
condition.hang;
|
||||||
|
"Analysis of % slices done.\n".postf(~total_slice_count);
|
||||||
|
}.fork(clock:~clock);
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Run stats on each mel buffer
|
||||||
|
|
||||||
|
// create a stats buffer for each of the slices
|
||||||
|
~statsbuffers = ~total_slice_count.collect{Buffer.new}; // create n Slices buffers - to be filled with (40 mel bands * 7 stats)
|
||||||
|
|
||||||
|
// run stats on all the buffers
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var remaining = ~total_slice_count;
|
||||||
|
~featurebuffers.do{|buffer,i|
|
||||||
|
FluidBufStats.processBlocking(
|
||||||
|
server:s,
|
||||||
|
source:buffer,
|
||||||
|
stats:~statsbuffers[i],
|
||||||
|
action:{
|
||||||
|
remaining = remaining - 1;
|
||||||
|
if(remaining == 0) { "done".postln};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}.fork(clock:~clock);
|
||||||
|
)
|
||||||
|
|
||||||
|
~featurebuffers.size
|
||||||
|
|
||||||
|
//Flatten each stats buffer into a data point
|
||||||
|
~flatbuffers = ~total_slice_count.collect{Buffer.new};// create an array of flatten stats
|
||||||
|
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var remaining = ~total_slice_count;
|
||||||
|
~statsbuffers.do{|buffer,i|
|
||||||
|
FluidBufFlatten.processBlocking(
|
||||||
|
server:s,
|
||||||
|
source:buffer,
|
||||||
|
destination:~flatbuffers[i],
|
||||||
|
action:{
|
||||||
|
remaining = remaining - 1;
|
||||||
|
if(remaining == 0) { "Got flat points".postln; };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}.fork(clock:~clock);
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
//Ram each flat point into a data set. At this point we have more data than we need, but we'll prune in moment
|
||||||
|
(
|
||||||
|
"Filling dataset".postln;
|
||||||
|
~mels.clear;
|
||||||
|
|
||||||
|
// ~flatbuffers = flatbuffers;
|
||||||
|
~flatbuffers.do{|buf,i|
|
||||||
|
~mels.addPoint(i,buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
~mels.print;
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Prune & standardise
|
||||||
|
|
||||||
|
// Tidy up the temp arrays of buffers we do not need anymore
|
||||||
|
|
||||||
|
(
|
||||||
|
"Cleaning".postln;
|
||||||
|
(~featurebuffers ++ ~statsbuffers ++ ~flatbuffers).do{|buf| buf.free};
|
||||||
|
)
|
||||||
|
|
||||||
|
//Above we sneakily made a dictionary of slice data for playback (bufnum,start,end). Let's throw it in a dataset
|
||||||
|
~slicedata = FluidDataSet(s); // will hold slice data (bufnum,start,end) for playback
|
||||||
|
|
||||||
|
//dict -> dataset
|
||||||
|
(
|
||||||
|
~slicedata.load(~slices);
|
||||||
|
~slicedata.print;
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step 1. Let's prune and standardize before fitting to a tree
|
||||||
|
(
|
||||||
|
~meanmels = FluidDataSet(s);//will hold pruned mel data
|
||||||
|
~stdmels = FluidDataSet(s);//will standardised, pruned mel data
|
||||||
|
~standardizer = FluidStandardize(s);
|
||||||
|
~pruner = FluidDataSetQuery(s);
|
||||||
|
~tree = FluidKDTree(s,numNeighbours:10,lookupDataSet:~slicedata);//we have to supply the lookup data set when we make the tree (boo!)
|
||||||
|
)
|
||||||
|
|
||||||
|
//Prune, standardize and fit KDTree
|
||||||
|
(
|
||||||
|
{
|
||||||
|
~meanmels.clear;
|
||||||
|
~stdmels.clear;
|
||||||
|
~pruner.addRange(0,~numMelBands).transform(~mels,~meanmels); //prune with a 'query' -- so this is dropping all but ~meanmels
|
||||||
|
~standardizer.fitTransform(~meanmels,~stdmels);
|
||||||
|
~tree.fit(~stdmels,{"KDTree ready".postln});
|
||||||
|
}.fork(clock:~clock);
|
||||||
|
)
|
||||||
|
|
||||||
|
~meanmels.print
|
||||||
|
|
||||||
|
//Step 2: Set the FluidStandardizer and FluidKDTree up for listening
|
||||||
|
//set the buffers and busses needed
|
||||||
|
(
|
||||||
|
~stdInputPoint = Buffer.alloc(s,40);
|
||||||
|
~stdOutputPoint = Buffer.alloc(s,40);
|
||||||
|
~treeOutputPoint = Buffer.alloc(s,3 * 10);//numNeighbours x triples of bufnum,start,end
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// let's play a random sound (to make sure we understand our data structure!
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var randPoint, buf, start, stop, dur;
|
||||||
|
|
||||||
|
randPoint = ~slices["data"].keys.asArray.scramble[0]; // this good way of getting - but recast as strong
|
||||||
|
|
||||||
|
buf= ~slices["data"][randPoint][0];
|
||||||
|
start = ~slices["data"][randPoint][1];
|
||||||
|
stop = ~slices["data"][randPoint][2];
|
||||||
|
|
||||||
|
dur = stop - start;
|
||||||
|
|
||||||
|
BufRd.ar(1,buf, Line.ar(start,stop,dur/s.sampleRate, doneAction: 2), 0, 2);
|
||||||
|
}.play
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Query KD tree
|
||||||
|
|
||||||
|
// a target sound from outside our dataset
|
||||||
|
~inBuf = Buffer.readChannel(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", numFrames:15000, channels:[0]);
|
||||||
|
~inBuf.play
|
||||||
|
|
||||||
|
//OR one from within
|
||||||
|
~inBuf = Buffer.alloc(s,15000);
|
||||||
|
~randomSlice = ~slices["data"].keys.asArray.scramble[0];
|
||||||
|
~audio_buffers[~slices["data"][~randomSlice][0]].copyData(~inBuf,srcStartAt: ~slices["data"][~randomSlice][1], numSamples: 15000.min(~slices["data"][~randomSlice][2] - (~slices["data"][~randomSlice][1])));
|
||||||
|
~inBuf.play
|
||||||
|
|
||||||
|
// now try getting a point, playing it, grabbing nearest neighbour and playing it ...
|
||||||
|
|
||||||
|
(
|
||||||
|
~inBufMels = Buffer(s);
|
||||||
|
~inBufStats = Buffer(s);
|
||||||
|
~inBufFlat = Buffer(s);
|
||||||
|
~inBufComp = Buffer(s);
|
||||||
|
~inBufStand = Buffer(s);
|
||||||
|
)
|
||||||
|
|
||||||
|
// FluidBuf Compose is buf version of dataSetQuery
|
||||||
|
|
||||||
|
(
|
||||||
|
FluidBufMelBands.process(s, ~inBuf, features: ~inBufMels, action: {
|
||||||
|
FluidBufStats.process(s, ~inBufMels, stats:~inBufStats, action: {
|
||||||
|
FluidBufFlatten.process(s, ~inBufStats, ~inBufFlat, action: {
|
||||||
|
FluidBufCompose.process(s, ~inBufFlat, numFrames: ~numMelBands, destination: ~inBufComp, action: {
|
||||||
|
~standardizer.transformPoint(~inBufComp, ~inBufStand, {
|
||||||
|
~tree.kNearest(~inBufStand,{ |a|a.postln;~nearest = a;})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// playback nearest in order
|
||||||
|
(
|
||||||
|
fork{
|
||||||
|
~nearest.do{|i|
|
||||||
|
var buf, start, stop, dur;
|
||||||
|
|
||||||
|
buf= ~slices["data"][i.asInteger][0];
|
||||||
|
start = ~slices["data"][i.asInteger][1];
|
||||||
|
stop = ~slices["data"][i.asInteger][2];
|
||||||
|
dur = (stop - start)/ s.sampleRate;
|
||||||
|
{BufRd.ar(1,buf, Line.ar(start,stop,dur, doneAction: 2), 0, 2);}.play;
|
||||||
|
|
||||||
|
i.postln;
|
||||||
|
dur.wait;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,161 @@
|
|||||||
|
// Make:
|
||||||
|
// - A kmeans
|
||||||
|
// - a datasetquery
|
||||||
|
// - a normalizer
|
||||||
|
// - a standardizer
|
||||||
|
// - 3 DataSets of example points R-G-B descriptions
|
||||||
|
// - 3 DataSets for the scaled versions
|
||||||
|
// - 1 summative dataset and a LabelSet for predicted labels
|
||||||
|
|
||||||
|
(
|
||||||
|
~classifier = FluidKMeans(s,5, 1000);
|
||||||
|
~query = FluidDataSetQuery(s);
|
||||||
|
~stan = FluidStandardize(s);
|
||||||
|
~norm = FluidNormalize(s);
|
||||||
|
~sourceR = FluidDataSet(s);
|
||||||
|
~sourceG = FluidDataSet(s);
|
||||||
|
~sourceB = FluidDataSet(s);
|
||||||
|
~scaledR = FluidDataSet(s);
|
||||||
|
~scaledG = FluidDataSet(s);
|
||||||
|
~scaledB = FluidDataSet(s);
|
||||||
|
~composited = FluidDataSet(s);
|
||||||
|
~labels = FluidLabelSet(s);
|
||||||
|
)
|
||||||
|
|
||||||
|
//Make some random, but clustered test points, each descriptor category in a separate dataset
|
||||||
|
(
|
||||||
|
~sourceR.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, 1.0.sum3rand]}.flatten))]));
|
||||||
|
~sourceG.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, 1.0.rand2]}.flatten))]));
|
||||||
|
~sourceB.load(Dictionary.newFrom([\cols, 1, \data, (Dictionary.newFrom(40.collect{|x| [x, (0.5.sum3rand).squared + [0.75,-0.1].choose]}.flatten))]));
|
||||||
|
)
|
||||||
|
|
||||||
|
//here we manipulate
|
||||||
|
|
||||||
|
//assemble the scaled dataset
|
||||||
|
(
|
||||||
|
~query.addColumn(0, {
|
||||||
|
~query.transformJoin(~sourceB, ~sourceG, ~composited, {
|
||||||
|
~query.transformJoin(~sourceR, ~composited, ~composited);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
~composited.print
|
||||||
|
|
||||||
|
//Fit the classifier to the example DataSet and labels, and then run prediction on the test data into our mapping label set
|
||||||
|
~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict = x;};~composited.dump{|x|~compodict=x;};});
|
||||||
|
|
||||||
|
//Visualise:
|
||||||
|
(
|
||||||
|
w = Window("sourceClasses", Rect(128, 64, 820, 120));
|
||||||
|
w.drawFunc = {
|
||||||
|
Pen.use{
|
||||||
|
~compodict["data"].keysValuesDo{|key, colour|
|
||||||
|
Pen.fillColor = Color.fromArray((colour * 0.5 + 0.5 ).clip(0,1) ++ 1);
|
||||||
|
Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict["data"].at(key).asInteger[0] * 20 + 10),15,15));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
w.refresh;
|
||||||
|
w.front;
|
||||||
|
)
|
||||||
|
|
||||||
|
// standardize our colours and rerun
|
||||||
|
(
|
||||||
|
~stan.fitTransform(~sourceR, ~scaledR, {
|
||||||
|
~stan.fitTransform(~sourceG, ~scaledG, {
|
||||||
|
~stan.fitTransform(~sourceB, ~scaledB, {
|
||||||
|
//assemble
|
||||||
|
~query.addColumn(0, {
|
||||||
|
~query.transformJoin(~scaledB, ~scaledG, ~composited, {
|
||||||
|
~query.transformJoin(~scaledR, ~composited, ~composited, {
|
||||||
|
//fit
|
||||||
|
~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
//Visualise:
|
||||||
|
(
|
||||||
|
w = Window("stanClasses", Rect(128, 204, 820, 120));
|
||||||
|
w.drawFunc = {
|
||||||
|
Pen.use{
|
||||||
|
~compodict2["data"].keysValuesDo{|key, colour|
|
||||||
|
Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1);
|
||||||
|
Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
w.refresh;
|
||||||
|
w.front;
|
||||||
|
)
|
||||||
|
|
||||||
|
//now let's normalise instead
|
||||||
|
(
|
||||||
|
~norm.fitTransform(~sourceR, ~scaledR, {
|
||||||
|
~norm.fitTransform(~sourceG, ~scaledG, {
|
||||||
|
~norm.fitTransform(~sourceB, ~scaledB, {
|
||||||
|
//assemble
|
||||||
|
~query.addColumn(0, {
|
||||||
|
~query.transformJoin(~scaledB, ~scaledG, ~composited, {
|
||||||
|
~query.transformJoin(~scaledR, ~composited, ~composited, {
|
||||||
|
//fit
|
||||||
|
~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
//Visualise:
|
||||||
|
(
|
||||||
|
w = Window("normClasses", Rect(128, 344, 820, 120));
|
||||||
|
w.drawFunc = {
|
||||||
|
Pen.use{
|
||||||
|
~compodict2["data"].keysValuesDo{|key, colour|
|
||||||
|
Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1);
|
||||||
|
Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
w.refresh;
|
||||||
|
w.front;
|
||||||
|
)
|
||||||
|
|
||||||
|
// let's mess up with the scaling of one dimension: let's multiply the range of Red by 10
|
||||||
|
~norm.min = -10;
|
||||||
|
~norm.max = 10;
|
||||||
|
(
|
||||||
|
~norm.fitTransform(~sourceR, ~scaledR, {
|
||||||
|
//assemble
|
||||||
|
~query.addColumn(0, {
|
||||||
|
~query.transformJoin(~scaledB, ~scaledG, ~composited, {
|
||||||
|
~query.transformJoin(~scaledR, ~composited, ~composited, {
|
||||||
|
//fit
|
||||||
|
~classifier.fitPredict(~composited,~labels,{~labels.dump{|x|~labeldict2 = x;};~composited.dump{|x|~compodict2=x;};});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
//Visualise:
|
||||||
|
(
|
||||||
|
w = Window("norm10rClasses", Rect(128, 484, 820, 120));
|
||||||
|
w.drawFunc = {
|
||||||
|
Pen.use{
|
||||||
|
~compodict2["data"].keysValuesDo{|key, colour|
|
||||||
|
Pen.fillColor = Color.fromArray((colour * 0.25 + 0.5 ).clip(0,1) ++ 1);
|
||||||
|
Pen.fillRect( Rect( (key.asFloat * 20 + 10), (~labeldict2["data"].at(key).asInteger[0] * 20 + 10),15,15));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
w.refresh;
|
||||||
|
w.front;
|
||||||
|
)
|
||||||
@ -1,59 +0,0 @@
|
|||||||
(
|
|
||||||
b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
|
|
||||||
c = Buffer.new(s);
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
|
||||||
// with basic params
|
|
||||||
Routine{
|
|
||||||
var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh;
|
|
||||||
startTime = Main.elapsedTime;
|
|
||||||
prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999
|
|
||||||
target = 10; //number of slices desired
|
|
||||||
tolerance = 0; // the acceptable error in the number of slices yield
|
|
||||||
maxIters = 100; //max number of iterations acceptable
|
|
||||||
|
|
||||||
//makes a first iteration
|
|
||||||
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames});
|
|
||||||
//makes a second iteration
|
|
||||||
if ( (prevVal < target), {
|
|
||||||
curThresh = (prevThresh * 0.5).max(0.000001);
|
|
||||||
}, {
|
|
||||||
curThresh = (prevThresh * 2).min(0.999999);
|
|
||||||
});
|
|
||||||
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
|
|
||||||
|
|
||||||
//makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached
|
|
||||||
iters = 2;
|
|
||||||
while ( {
|
|
||||||
(iters < maxIters) && ((curVal - target).abs > tolerance)
|
|
||||||
}, {
|
|
||||||
iters = iters + 1;
|
|
||||||
dVal = curVal - prevVal;
|
|
||||||
dThresh = curThresh - prevThresh;
|
|
||||||
|
|
||||||
prevThresh = curThresh;
|
|
||||||
prevVal = curVal;
|
|
||||||
|
|
||||||
if ( (dVal == 0), {
|
|
||||||
//if we have not change results between the last 2 passes, make the variation of threshold bigger
|
|
||||||
curThresh = (dThresh + curThresh).min(0.999999).max(0.000001);
|
|
||||||
},{
|
|
||||||
//if we have
|
|
||||||
curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001);
|
|
||||||
});
|
|
||||||
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//depending on the outcome, gives the right info back
|
|
||||||
|
|
||||||
if ( iters >= maxIters, {
|
|
||||||
// failed
|
|
||||||
"Failed to find a suitable threshold in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
|
|
||||||
}, {
|
|
||||||
// succeeded
|
|
||||||
"Found % as a suitable threshold for % slices in % seconds and % iterations.\n".postf(curThresh, curVal, (Main.elapsedTime - startTime).round(0.01), iters);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}.play
|
|
||||||
)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue