Merge branch 'refactor/NRTSynths' into clients/inter_client_comms

nix
Owen Green 5 years ago
commit d96bec54b1

File diff suppressed because it is too large Load Diff

@ -25,29 +25,7 @@ under the European Unions Horizon 2020 research and innovation programme
namespace fluid {
namespace client {
/**
A descendent of SndBuf that will populate itself
from the NRT mirror buffers given a world and a bufnum
**/
struct NRTBuf
{
NRTBuf(SndBuf* b) : mBuffer(b) {}
NRTBuf(World* world, index bufnum, bool rt = false)
: NRTBuf(rt ? World_GetBuf(world, static_cast<uint32>(bufnum))
: World_GetNRTBuf(world, static_cast<uint32>(bufnum)))
{
if (mBuffer && !static_cast<bool>(mBuffer->samplerate))
mBuffer->samplerate = world->mFullRate.mSampleRate;
}
protected:
SndBuf* mBuffer;
};
/**
A combination of SndBuf and client::BufferAdaptor (which, in turn, exposes
FluidTensorView<float,2>), for simple transfer of data
/*
Given a World* and a buffer number, this will populate its SndBuf stuff
from the NRT mirror buffers, and create a FluidTensorView wrapper of
appropriate dimensions.
@ -60,24 +38,30 @@ protected:
nSamps = rows
nChans = columns
**/
class SCBufferAdaptor : public NRTBuf, public client::BufferAdaptor
class SCBufferAdaptor;
std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b);
class SCBufferAdaptor :public client::BufferAdaptor
{
public:
// SCBufferAdaptor() = delete;
SCBufferAdaptor(const SCBufferAdaptor&) = delete;
SCBufferAdaptor& operator=(const SCBufferAdaptor&) = delete;
SCBufferAdaptor(SCBufferAdaptor&&) = default;
SCBufferAdaptor(SndBuf* buf, World* world, bool local)
: NRTBuf{buf}, mWorld{world}, mLocal{local}
: mBuffer{buf}, mWorld{world}, mLocal{local}
{}
SCBufferAdaptor(index bufnum, World* world, bool rt = false)
: NRTBuf(world, bufnum, rt), mBufnum(bufnum), mWorld(world)
{}
SCBufferAdaptor(index bufnum, World* world)
: mBuffer{World_GetNRTBuf(world, static_cast<uint32>(bufnum))}, mBufnum(bufnum), mWorld(world)
{
if (mBuffer && !static_cast<bool>(mBuffer->samplerate))
mBuffer->samplerate = world->mFullRate.mSampleRate;
}
~SCBufferAdaptor() { cleanUp(); }
// ~SCBufferAdaptor() { cleanUp(); }
void assignToRT(World* rtWorld)
{
@ -90,11 +74,8 @@ public:
void cleanUp()
{
if (mOldData)
{
boost::alignment::aligned_free(mOldData);
mOldData = nullptr;
}
boost::alignment::aligned_free(mOldData);
mOldData = nullptr;
}
// No locks in (vanilla) SC, so no-ops for these
@ -178,24 +159,24 @@ public:
std::string asString() const override { return std::to_string(bufnum()); }
const Result resize(index frames, index channels, double sampleRate) override
{
SndBuf* thisThing = mBuffer;
{
if(mLocal) // don't try and resize
{
if(frames > thisThing->frames || channels > thisThing->channels)
if(frames > mBuffer->frames || channels > mBuffer->channels)
{
return {Result::Status::kError, "Local buffer must be presized adequetly, need ",
frames, "frames, ", channels, " channels." };
frames, " frames, ", channels, " channels." };
}
else return {};
}
mOldData = thisThing->data;
mOldData = mBuffer->data;
int allocResult =
mWorld->ft->fBufAlloc(mBuffer, static_cast<int>(channels),
static_cast<int>(frames), sampleRate);
if(mBuffer->data == mOldData) mOldData = nullptr;
Result r;
if (allocResult != kSCErr_None)
@ -210,8 +191,9 @@ public:
void realTime(bool rt) { mRealTime = rt; }
protected:
SndBuf* mBuffer;
bool mRealTime{false};
float* mOldData{0};
float* mOldData{nullptr};
index mBufnum;
World* mWorld;
bool mLocal{false};

@ -24,7 +24,7 @@ class DataSetWriterClient : public FluidBaseClient, OfflineIn, OfflineOut
{
public:
FLUID_DECLARE_PARAMS(
DataSetClientRef::makeParam("dataSet", "DataSet Name"),
DataSetClientRef::makeParam("dataSet", "DataSet ID"),
StringParam("labelPrefix","Label Prefix"),
LongParam("labelOffset", "Label Counter Offset", 0),
BufferParam("buf", "Data Buffer")

@ -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|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
*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;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
^FluidProxyUgen.kr(\FluidBufAmpGateTrigger,-1, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq,maxSize, trig, blocking);
}
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
*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 |
^super.new1(rate, 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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, trig, blocking);
}
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
*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 |
source = source ? -1;
indices = indices ? -1;
^FluidNRTProcess.new(
server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
^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 |
^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
);
}
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
);
}
}

@ -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;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufAmpSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
}
source = source.asUGenInput;
indices = indices.asUGenInput;
*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 |
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
source = source.asUGenInput;
indices = indices.asUGenInput;
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
}
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
*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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq, trig, blocking);
^this.new(server, nil, [indices]).processList(
[source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq,0],freeWhenDone, action
);
}
*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 |
*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 |
^FluidNRTProcess.new(
server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq
);
}
source = source.asUGenInput;
indices = indices.asUGenInput;
*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|
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
^FluidNRTProcess.new(
server, this, action, [indices], blocking: 1
).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,1],freeWhenDone, action
);
}
}

@ -1,40 +1,58 @@
FluidBufAudioTransport : UGen{
*new1 { |rate, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking=0|
FluidBufAudioTransport : FluidBufProcessor {
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
source1 = source1.asUGenInput;
source2 = source2.asUGenInput;
*objectClassName{
^\FluidBufAudioTransp
}
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
*kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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,source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize, trig,blocking)
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
source1 = source1.asUGenInput;
source2 = source2.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
}
*kr { |source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
^this.new1(\control, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, trig, blocking);
^FluidProxyUgen.kr(this.objectClassName++\Trigger,-1, source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame1, numFrames1, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [destination]
).process(
source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize
*process { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
source1 = source1.asUGenInput;
source2 = source2.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
^this.new(
server, nil, [destination]
).processList(
[source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize,0], freeWhenDone, action
)
}
*processBlocking { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [destination],blocking:1
).process(
source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize
*processBlocking { |server, source1, startFrame1 = 0, numFrames1 = -1, startChan1 = 0, numChans1 = -1, source2, startFrame2 = 0, numFrames2 = -1, startChan2 = 0, numChans2 = -1, destination, interpolation=0.0, bandwidth=255, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source1.isNil.if {"FluidAudioTransport: Invalid source 1 buffer".throw};
source2.isNil.if {"FluidAudioTransport: Invalid source 2 buffer".throw};
source1 = source1.asUGenInput;
source2 = source2.asUGenInput;
destination.isNil.if {"FluidAudioTransport: Invalid destination buffer".throw};
destination = destination.asUGenInput;
^this.new(
server, nil, [destination]
).processList(
[source1, startFrame1, numFrames1, startChan1, numChans1, source2, startFrame2, numFrames2, startChan2, numChans2, destination, interpolation, bandwidth, windowSize, hopSize, fftSize,maxFFTSize,1], freeWhenDone, action
)
}
}

@ -1,33 +1,38 @@
FluidBufCompose : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, trig = 1, blocking|
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source 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);
}
FluidBufCompose : FluidBufProcessor {
*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);
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufCompose: Invalid source buffer".throw};
destination.isNil.if {"FluidBufCompose: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1, 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, action|
^FluidNRTProcess.new(
server, this, action, [destination], blocking:1
).process(
source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
);
*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;
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);//NB always blocking
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, gain = 1, destination, destStartFrame = 0, destStartChan = 0, destGain = 0, action|
^process(
source, startFrame, numFrames, startChan, numChans, gain, destination, destStartFrame, destStartChan, destGain
);
*processBlocking { |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;
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;
destination = destination.asUGenInput;
*kr { |source, destination, axis = 1, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
^super.new1(rate, source, destination, axis, trig, blocking);
source.isNil.if {"FluidBufFlatten: Invalid source buffer".throw};
destination.isNil.if {"FluidBufFlatten: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufFlattenTrigger,-1, source, destination, axis, trig, blocking);
}
*kr { |source, destination, axis = 1, trig = 1, blocking = 1|
^this.new1('control', source, destination, axis, trig, blocking);
}
*process { |server, source, destination, axis = 1, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
*process { |server, source, destination, axis = 1, action|
^FluidNRTProcess.new(
server, this, action, [destination], blocking:1
).process(
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|
^process(
source, destination, axis
);
}
*processBlocking { |server, source, destination, axis = 1, freeWhenDone = true, action|
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};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
harmonic = harmonic.asUGenInput;
percussive = percussive.asUGenInput;
residual = residual.asUGenInput;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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, 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(
'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
);
}
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
harmonic = harmonic ? -1;
percussive = percussive ? -1;
residual = residual ? -1;
source.isNil.if {"FluidBufHPSS: Invalid source buffer".throw};
*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|
^FluidNRTProcess.new(
server, this, action, [harmonic, percussive, residual].select{|x| x!= -1}
).process(
source, startFrame, numFrames, startChan, numChans, harmonic, percussive, residual, harmFilterSize, percFilterSize, maskingMode, harmThreshFreq1, harmThreshAmp1, harmThreshFreq2, harmThreshAmp2, percThreshFreq1, percThreshAmp1, percThreshFreq2, percThreshAmp2, windowSize, hopSize, fftSize
^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,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(
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
);
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {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,6 +1,6 @@
FluidBufLoudness : UGen{
*new1 { |rate,source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0|
FluidBufLoudness : FluidBufProcessor{
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0|
var maxwindowSize = windowSize.nextPowerOfTwo;
source = source.asUGenInput;
@ -8,27 +8,41 @@ FluidBufLoudness : UGen{
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufLoudnessTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, maxwindowSize, trig, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, trig = 1, blocking = 0|
^this.multiNew('control', source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize, trig, blocking );
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
source = source.asUGenInput;
features = features.asUGenInput;
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action|
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize
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, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,0],freeWhenDone,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, action|
^FluidNRTProcess.new(
server, this, action, [features], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, kWeighting, truePeak, windowSize, hopSize
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, kWeighting = 1, truePeak = 1, windowSize = 1024, hopSize = 512, freeWhenDone = true, action|
var maxwindowSize = windowSize.nextPowerOfTwo;
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, kWeighting, truePeak, windowSize, hopSize, maxwindowSize,1],freeWhenDone,action
);
}
}

@ -1,37 +1,45 @@
FluidBufMFCC : UGen{
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
FluidBufMFCC : FluidBufProcessor{
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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)
// same goes to maxNumCoeffs, which is passed numCoeffs in this case
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufMFCCTrigger, -1, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize, maxFFTSize,trig, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone=true, action |
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, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, numCoeffs,windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new(
server, this, action, [features],blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq, windowSize, hopSize, fftSize
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, numCoeffs = 13, numBands = 40, minFreq = 20, maxFreq = 20000, windowSize = 1024, hopSize = -1, fftSize = -1,freeWhenDone=true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufMFCC: Invalid source buffer".throw};
features.isNil.if {"FluidBufMFCC: Invalid features buffer".throw};
^this.new(
server, nil,[features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, numCoeffs, numBands, minFreq, maxFreq,numCoeffs, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action
);
}
}

@ -1,38 +1,50 @@
FluidBufMelBands : UGen {
*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|
FluidBufMelBands : FluidBufProcessor {
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;
features = features.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
source = source.asUGenInput;
features = features.asUGenInput;
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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)
// same for maxNumBands which is passed numBands
source.isNil.if {"FluidBufMelBands: Invalid source buffer".throw};
features.isNil.if {"FluidBufMelBands: Invalid features buffer".throw};
^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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufMelBandsTrigger,-1, source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, numBands, normalize, windowSize, hopSize, fftSize,maxFFTSize, 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|
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
*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|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {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|
^FluidNRTProcess.new(
server, this, action, [features], blocking:1
).process(
source, startFrame, numFrames, startChan, numChans, features, numBands, minFreq, maxFreq, normalize, windowSize, hopSize, fftSize
*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|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {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 {
*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|
source = source.asUGenInput;
resynth = resynth.asUGenInput;
bases = bases.asUGenInput;
activations = activations.asUGenInput;
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
FluidBufNMF : FluidBufProcessor //: UGen {
{
*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.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^super.new1(rate,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);
activations = activations ? -1;
^FluidProxyUgen.kr(\FluidBufNMFTrigger,-1,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};
^FluidNRTProcess.new(
server, this, action, [resynth, bases, activations].select{|x| x!= -1}
).process(
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
);
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize,0],freeWhenDone,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|
*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|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
^FluidNRTProcess.new(
server, this, action, [resynth, bases, activations].select{|x| x!= -1},blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
);
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
resynth = resynth ? -1;
bases = bases ? -1;
activations = activations ? -1;
^this.new(
server,nil,[resynth, bases, activations].select{|x| x!= -1}
).processList([source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, 1],freeWhenDone,action);
}
}

@ -1,34 +1,48 @@
FluidBufNMFCross : UGen{
*new1 { |rate, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
FluidBufNMFCross : FluidBufProcessor {
*kr { |source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
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};
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};
^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|
^this.multiNew(\control, source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize, trig, blocking);
}
*process { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
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(
server, this, action, [output].select{|x| x!= -1}
).process(
source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize
^this.new(
server, nil, [output]
).processList(
[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|
^FluidNRTProcess.new(
server, this, action, [output].select{|x| x!= -1}, blocking: 1
).process(
source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize
*processBlocking { |server, source, target, output , timeSparsity = 10, polyphony = 7, iterations = 50, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
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};
^this.new(
server, nil, [output]
).processList(
[source, target, output, timeSparsity, polyphony, iterations, windowSize, hopSize, fftSize,1],freeWhenDone, action
);
}
}

@ -1,35 +1,47 @@
FluidBufNNDSVD : UGen{
*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};
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)
}
FluidBufNNDSVD : FluidBufProcessor{
*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);
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);
}
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [bases]
).process(
source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
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;
^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|
^FluidNRTProcess.new(
server, this, action, [bases],blocking:1
).process(
source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
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;
^this.new(
server, nil, [bases]
).processList(
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,1],freeWhenDone, action
)
}
}

@ -1,7 +1,8 @@
FluidBufNoveltySlice : UGen {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0 |
FluidBufNoveltySlice : FluidBufProcessor {
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1 , blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
@ -9,29 +10,41 @@ FluidBufNoveltySlice : UGen {
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, trig, blocking);
^FluidProxyUgen.kr(\FluidBufNoveltySliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize, trig, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1 , blocking = 0|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, trig, blocking);
source = source.asUGenInput;
indices = indices.asUGenInput;
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new(
server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize
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, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize,0],freeWhenDone,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, action |
^FluidNRTProcess.new(
server, this, action, [indices], blocking:1
).process(
source, startFrame, numFrames, startChan, numChans, indices, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, feature = 0, kernelSize = 3, threshold = 0.5, filterSize = 1, minSliceLength = 2, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action |
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
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, feature, kernelSize, threshold, filterSize, minSliceLength, windowSize, hopSize, fftSize, maxFFTSize, kernelSize, filterSize,1],freeWhenDone,action
);
}
}

@ -1,37 +1,48 @@
FluidBufOnsetSlice : UGen {
*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|
FluidBufOnsetSlice : FluidBufProcessor {
*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};
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
indices = indices.asUGenInput;
source = source.asUGenInput;
indices = indices.asUGenInput;
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufOnsetSliceTrigger, -1, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
source.isNil.if {"FluidBufOnsetSlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufOnsetSlice: Invalid features buffer".throw};
*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|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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)
source = source.asUGenInput;
indices = indices.asUGenInput;
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
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,0],freeWhenDone,action
);
}
*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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize, trig, blocking);
}
*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|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
*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|
^FluidNRTProcess.new(
server, this, action, [indices]
).process(
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
);
}
source = source.asUGenInput;
indices = indices.asUGenInput;
*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|
^FluidNRTProcess.new(
server, this, action, [indices], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, indices, metric, threshold, minSliceLength, filterSize, frameDelta, windowSize, hopSize, fftSize
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{
*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};
*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|
source = source.asUGenInput;
features = features.asUGenInput;
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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);
source.isNil.if {"FluidBufPitch: Invalid source buffer".throw};
features.isNil.if {"FluidBufPitch: Invalid features buffer".throw};
^FluidProxyUgen.kr(\FluidBufPitchTrigger, -1, 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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize, trig, blocking);
*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|
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|
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
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, 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|
^FluidNRTProcess.new(
server, this, action, [features], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, algorithm, minFreq, maxFreq, unit, windowSize, hopSize, fftSize
*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|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {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|
source = source.asUGenInput;
destination = destination.asUGenInput;
*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;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufScale: Invalid source 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);
source.isNil.if {"FluidBufScale: Invalid source buffer".throw};
destination.isNil.if {"FluidBufScale: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufScaleTrigger, -1, 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|
^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, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, action|
^FluidNRTProcess.new(
server, this, action, [destination], blocking:1
).process(
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|
^process(
source, startFrame, numFrames, startChan, numChans, destination, inputLow, inputHigh, outputLow, outputHigh
);
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, inputLow = 0, inputHigh = 1, outputLow = 0, outputHigh = 1, freeWhenDone = true, action|
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,38 +1,49 @@
FluidBufSines : UGen{
FluidBufSines : FluidBufProcessor {
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, 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;
sines = sines.asUGenInput;
residual = residual.asUGenInput;
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
^FluidProxyUgen.multiNew(\FluidBufSinesTrigger, -1, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [sines, residual].select{|x| x!= -1}
).process(
source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize
^this.new(
server, nil, [sines, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize,maxFFTSize,0],freeWhenDone = true,action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [sines, residual].select{|x| x!= -1}, blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, sines = -1, residual = -1, bandwidth = 76, detectionThreshold = -96, birthLowThreshold = -24, birthHighThreshold = -60, minTrackLen = 15, trackingMethod = 0, trackMagRange = 15, trackFreqRange = 50, trackProb = 0.5, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
sines = sines !? {sines.asUGenInput} ?? {-1};
residual = residual !? {residual.asUGenInput} ?? {-1};
source.isNil.if {"FluidBufSines: Invalid source buffer".throw};
^this.new(
server, nil, [sines, residual].select{|x| x!= -1}
).processList(
[source, startFrame, numFrames, startChan, numChans, sines, residual, bandwidth, detectionThreshold,birthLowThreshold, birthHighThreshold, minTrackLen, trackingMethod, trackMagRange, trackFreqRange, trackProb, windowSize, hopSize, fftSize,maxFFTSize,1],freeWhenDone,action
);
}

@ -1,6 +1,10 @@
FluidBufSpectralShape : UGen {
FluidBufSpectralShape : FluidBufProcessor {
*new1{ |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
*objectClassName{
^\FluidBufSpecShp
}
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
@ -10,33 +14,41 @@ FluidBufSpectralShape : UGen {
source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw};
features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw};
//NB For wrapped versions of NRT classes, we set the params for maxima to
//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, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
^FluidProxyUgen.kr(this.objectClassName++\Trigger, -1, source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, trig, blocking);
}
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, features, 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, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [features]
).process(
source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize
source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw};
features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, action|
^FluidNRTProcess.new(
server, this, action, [features], blocking: 1
).process(
source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, features, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
var maxFFTSize = if (fftSize == -1) {windowSize.nextPowerOfTwo} {fftSize};
source = source.asUGenInput;
features = features.asUGenInput;
source.isNil.if {"FluidBufSpectralShape: Invalid source buffer".throw};
features.isNil.if {"FluidBufSpectralShape: Invalid features buffer".throw};
^this.new(
server, nil, [features]
).processList(
[source, startFrame, numFrames, startChan, numChans, features, windowSize, hopSize, fftSize, maxFFTSize, 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|
source = source.asUGenInput;
stats = stats.asUGenInput;
*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;
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;
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
source.isNil.if {"FluidBufStats: Invalid source buffer".throw};
stats.isNil.if {"FluidBufStats: Invalid stats buffer".throw};
weights = weights ? -1;
^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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, stats, numDerivs, low, middle, high, outliersCutoff, weights, trig, blocking);
}
*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|
source = source.asUGenInput;
stats = stats.asUGenInput;
weights = weights.asUGenInput;
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action|
^FluidNRTProcess.new(
server, this, action, [stats]
).process(
source, startFrame, numFrames, startChan, numChans, stats,numDerivs, low, middle, high, outliersCutoff, weights
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, 0], freeWhenDone, action
);
}
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, stats, numDerivs = 0, low = 0, middle = 50, high = 100, outliersCutoff = -1, weights, action|
^FluidNRTProcess.new(
server, this, action, [stats], blocking: 1
).process(
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, freeWhenDone = true, action|
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;
^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 |
result = result.asUGenInput;
result.isNil.if {this.class.name+": Invalid output buffer".throw};
^super.new1(rate, result, time, trig, blocking);
}
*kr {|result, time, trig = 1, blocking = 0|
result = result.asUGenInput;
result.isNil.if {this.class.name+": Invalid output buffer".throw};
*kr {|result, time, trig = 1, blocking = 0|
^this.new1(\control, result, time, trig, blocking);
^FluidProxyUgen.kr(\FluidBufThreadDemoTrigger, -1, result, time, trig, blocking);
}
*process { |server, result, time = 1000, action|
^FluidNRTProcess.new(
server, this, action, [result]
).process(
result, time
*process { |server, result, time = 1000, freeWhenDone = true, action|
result ?? {this.class.name+": Invalid output buffer".throw};
^this.new(
server, nil, [result]
).processList(
[result.asUGenInput, time, 0], freeWhenDone, action
);
}
*processBlocking { |server, result, time = 1000, action|
^FluidNRTProcess.new(
server, this, action, [result], blocking: 1
).process(
result, time
*processBlocking { |server, result, time = 1000, freeWhenDone = true, action|
result ?? {this.class.name+": Invalid output buffer".throw};
^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|
source = source.asUGenInput;
destination = destination.asUGenInput;
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, trig = 1, blocking = 1|
source = source.asUGenInput;
destination = destination.asUGenInput;
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, destination, threshold, trig, blocking);
source.isNil.if {"FluidBufThresh: Invalid source buffer".throw};
destination.isNil.if {"FluidBufThresh: Invalid destination buffer".throw};
^FluidProxyUgen.kr(\FluidBufThreshTrigger, -1, 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|
^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, freeWhenDone = true, action|
source = source.asUGenInput;
destination = destination.asUGenInput;
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, action|
^FluidNRTProcess.new(
server, this, action, [destination], blocking:1
).process(
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|
^process(
source, startFrame, numFrames, startChan, numChans, destination, threshold
);
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, threshold = 0, freeWhenDone = true, action|
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{
*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|
FluidBufTransientSlice : FluidBufProcessor {
source = source.asUGenInput;
indices = indices.asUGenInput;
*objectClassName{^\FluidBufTrSlice}
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features 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|
^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;
source.isNil.if {"FluidBufNoveltySlice: Invalid source buffer".throw};
indices.isNil.if {"FluidBufNoveltySlice: Invalid features 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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength, trig, blocking);
^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|
^FluidNRTProcess.new(
server, this, action,[indices]
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
*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|
source = source.asUGenInput;
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|
^FluidNRTProcess.new(
server, this, action,[indices], blocking: 1
).process(source, startFrame, numFrames, startChan, numChans, indices, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, minSliceLength
*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|
source = source.asUGenInput;
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;
residual = residual ? -1;
source = source.asUGenInput;
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|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength, trig, blocking);
source = source.asUGenInput;
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|
^FluidNRTProcess.new(
server, this, action,[transients, residual].select{|x| x!= -1}
).process(
source, startFrame, numFrames, startChan, numChans, transients, residual, order, blockSize, padSize, skew, threshFwd, threshBack, windowSize, clumpLength
^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,0],freeWhenDone,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, action|
^FluidNRTProcess.new(
server, this, action,[transients, residual].select{|x| x!= -1}, blocking: 1
).process(
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, freeWhenDone = true, action|
source = source.asUGenInput;
transients = transients ? -1;
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
);
}
}

@ -34,6 +34,7 @@ FluidLoadFolder {
entry.add(\bounds->startEnd[i]);
entry.add(\numchans->f.numChannels);
entry.add(\sr->f.sampleRate);
entry.add(\path->f.path);
index.add(label->entry);
counter = counter + 1;
if(counter == (files.size)) {action !? action.value(index)};

@ -1,91 +1,65 @@
FluidDataSetExistsError : Exception{
}
FluidDataSet : FluidManipulationClient {
FluidDataSet : FluidDataObject
{
*new{|server| ^super.new(server) }
var <id;
classvar serverCaches;
addPointMsg{|label,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\addPoint,id,label.asSymbol,buffer);
}
*initClass {
serverCaches = FluidServerCache.new;
addPoint{|label, buffer, action|
actions[\addPoint] = [nil,action];
this.prSendMsg(this.addPointMsg(label,buffer));
}
*at{ |server, name|
^serverCaches.tryPerform(\at, server, name)
}
getPointMsg{|label,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\getPoint,id,label.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
*new { |server, name|
if(this.at(server,name).notNil){
FluidDataSetExistsError("A FluidDataset called % already exists.".format(name)).throw;
^nil
}
^super.new(server,FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst}
}
getPoint{|label, buffer, action|
actions[\getPoint] = [nil,action];
this.prSendMsg(this.getPointMsg(label,buffer));
}
init {|name|
this.baseinit(FluidManipulationClient.prServerString(name));
id = name;
this.cache;
}
updatePointMsg{|label,buffer|
buffer = this.prEncodeBuffer(buffer);
^this.prMakeMsg(\updatePoint,id,label.asSymbol,buffer,["/b_query",buffer.asUGenInput]);
}
cache {
serverCaches.initCache(server);
serverCaches.put(server,id,this);
}
updatePoint{|label, buffer, action|
actions[\updatePoint] = [nil,action];
this.prSendMsg(this.updatePointMsg(label,buffer));
}
*asUGenInput { |input|
var ascii = input.asString.ascii;
^[ascii.size].addAll(ascii)
}
deletePointMsg{|label| ^this.prMakeMsg(\deletePoint,id,label.asSymbol);}
asString {
^"FluidDataSet(%)".format(id).asString;
}
deletePoint{|label, buffer, action|
actions[\deletePoint] = [nil,action];
this.prSendMsg(this.deletePointMsg(label));
}
asSymbol {
^id.asSymbol
}
clearMsg { ^this.prMakeMsg(\clear,id); }
addPoint{|label, buffer, action|
buffer = this.prEncodeBuffer(buffer);
this.prSendMsg(\addPoint,[label.asSymbol,buffer],action);
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
getPoint{|label, buffer, action|
buffer = this.prEncodeBuffer(buffer);
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);
}
mergeMsg{|sourceDataSet, overwrite = 0|
^this.prMakeMsg(\merge,id,sourceDataSet.asUGenInput,overwrite);
}
merge{|sourceDataSet, overwrite = 0, action|
this.prSendMsg(\merge,
[sourceDataSet.asSymbol, overwrite], action);
actions[\merge] = [nil,action];
this.prSendMsg(this.mergeMsg(sourceDataSet,overwrite));
}
print { |action|
action ?? {action = postit};
this.prSendMsg(\print,[],action,[string(FluidMessageResponse,_,_)]);
}
free {
serverCaches.remove(server,id);
super.free;
}
printMsg { ^this.prMakeMsg(\print,id); }
*freeAll { |server|
serverCaches.do(server,{|x|x.free;});
print { |action=(postResponse)|
actions[\print] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.printMsg);
}
}

@ -1,52 +1,84 @@
FluidDataSetQuery : FluidManipulationClient {
FluidDataSetQuery : FluidDataObject {
*new {|server|
var uid = UniqueID.next;
^super.new(server,uid)!?{|inst|inst.init(uid);inst}
}
init {|uid|
id = uid;
this.baseinit(uid);
}
addColumnMsg { |column|
^this.prMakeMsg(\addColumn,id,column);
}
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|
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|
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|
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|
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|
this.prSendMsg(\clear, [], action);
actions[\clear] = [nil, action];
this.prSendMsg(this.clearMsg);
}
limitMsg{|rows|
^this.prMakeMsg(\limit,id,rows);
}
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|
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|
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|
buf ?? {"No input buffer provided".error};
^super.new1(rate,*(FluidManipulationClient.prServerString(dataset.asSymbol)
++ FluidDataSet.asUGenInput(labelPrefix.asSymbol) ++ labelOffset.asInteger.asUGenInput ++buf.asUGenInput ++ trig ++ blocking));
}
args = [-1] ++ dataset.asUGenInput ++labelPrefix ++ labelOffset.asInteger.asUGenInput ++buf.asUGenInput ++ trig ++ blocking;
*kr { |dataset,labelPrefix = "", labelOffset = 0,buf, trig=1, blocking = 0|
^this.new1(\control,dataset,labelPrefix,labelOffset, buf, trig, blocking)
^FluidProxyUgen.kr(\FluidDataSetWrTrigger,*args);
}
}

@ -1,41 +1,60 @@
FluidKDTree : FluidRTDataClient {
FluidKDTree : FluidRealTimeModel
{
*new {|server,numNeighbours = 1, radius = 0, lookupDataSet = ""|
var env;
var names = [\numNeighbours, \radius]
++ this.prServerString(lookupDataSet.asSymbol).collect{|x,i|
("lookupDataSet"++i).asSymbol;
};
var neighbours,radius,lookup;
var values = [numNeighbours, radius] ++ this.prServerString(lookupDataSet.asSymbol);
var params = [names,values].lace;
*new{ |server, numNeighbours = 1, radius = 0, lookupDataSet|
^super.new(server,[numNeighbours,radius,lookupDataSet ? -1])
.numNeighbours_(numNeighbours)
.radius_(radius)
.lookupDataSet_(lookupDataSet);
}
numNeighbours_{|k|neighbours = k.asInteger; }
numNeighbours{ ^neighbours; }
/* env = Environment();
synthControls[1..].do{|x|
env.put(x,0);
};
env.put(\numNeighbours,1); */
radius_{|r| radius = r.asUGenInput;}
radius{ ^radius; }
^super.new1(server,params);
/* env,
[\numNeighbours]++lookupDataSet); */
}
lookupDataSet_{|ds| lookup = ds ? -1; }
lookupDataSet{|ds| ^ (lookup ? -1) }
prGetParams{^[this.numNeighbours,this.radius,this.lookupDataSet,-1,-1];}
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,this.id,dataSet.id);}
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|
this.prSendMsg(\kNearest,
[buffer.asUGenInput], action,
[strings(FluidMessageResponse,_,_)]
);
actions[\kNearest] = [strings(FluidMessageResponse,_,_),action];
this.prSendMsg(this.kNearestMsg(buffer));
}
kNearestDistMsg {|buffer|
^this.prMakeMsg(\kNearestDist,id,this.prEncodeBuffer(buffer));
}
kNearestDist { |buffer, action|
this.prSendMsg(\kNearestDist, [buffer.asUGenInput], action,
[numbers(FluidMessageResponse,_,nil,_)]
);
actions[\kNearestDist] = [numbers(FluidMessageResponse,_,nil,_),action];
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|
^super.new1(server,[\numClusters,numClusters,\maxIter,maxIter]);
^super.new(server,[numClusters,maxIter])
.numClusters_(numClusters)
.maxIter_(maxIter);
}
fit{|dataSet,action|
this.prSendMsg(\fit,
[dataSet.asSymbol], action,
[numbers(FluidMessageResponse,_,this.numClusters,_)]
);
}
numClusters_{|n| clusters = n.asInteger}
numClusters{ ^clusters }
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|
this.prSendMsg(\fitPredict,
[dataSet.asSymbol,labelSet.asSymbol],
action,[numbers(FluidMessageResponse,_,this.numClusters,_)]
);
actions[\fitPredict] = [
numbers(FluidMessageResponse, _, this.numClusters, _),
action
];
this.prSendMsg(this.fitPredictMsg(dataSet,labelSet));
}
predict{ |dataSet, labelSet,action|
this.prSendMsg(\predict,
[dataSet.asSymbol, labelSet.asSymbol], action,
[numbers(FluidMessageResponse,_,this.numClusters,_)]
);
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
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|
buffer = this.prEncodeBuffer(buffer);
this.prSendMsg(\predictPoint,
[buffer], action,
[number(FluidMessageResponse,_,_)]
);
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer))
}
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|
^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|
this.prSendMsg(\fit,[dataSet.asSymbol, labelSet.asSymbol], action);
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(dataSet, labelSet));
}
predict{|dataSet, labelSet, action|
this.prSendMsg(\predict,
[dataSet.asSymbol, labelSet.asSymbol],
action);
predictMsg{|dataSet, labelSet|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
}
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|
buffer = this.prEncodeBuffer(buffer);
this.prSendMsg(\predictPoint,
[buffer.asUGenInput], action,
[string(FluidMessageResponse,_,_)]
);
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer));
}
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|
^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|
this.prSendMsg(\fit,
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
action
);
actions[\fit] = [nil,action];
this.prSendMsg(this.fitMsg(sourceDataSet, targetDataSet));
}
predictMsg{ |sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,this.id,sourceDataSet.id,targetDataSet.id)
}
predict{ |sourceDataSet, targetDataSet,action|
this.prSendMsg(\predict,
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
action);
actions[\predict] = [nil, action];
this.prSendMsg(this.predictMsg(sourceDataSet, targetDataSet));
}
predictPointMsg { |buffer|
^this.prMakeMsg(\predictPoint,id, this.prEncodeBuffer(buffer));
}
predictPoint { |buffer, action|
buffer = this.prEncodeBuffer(buffer);
this.prSendMsg(\predictPoint, [buffer], action,
[number(FluidMessageResponse,_,_)]);
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(buffer));
}
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;
classvar serverCaches;
addLabelMsg{|identifier,label|
^this.prMakeMsg(\addLabel,id,identifier.asSymbol,label.asSymbol);
}
*initClass {
serverCaches = FluidServerCache.new;
addLabel{|identifier, label, action|
actions[\addLabel] = [nil, action];
this.prSendMsg(this.addLabelMsg(identifier,label));
}
*at{ |server, name|
^serverCaches.tryPerform(\at, server, name)
}
updateLabelMsg{|identifier, label|
^this.prMakeMsg(\updateLabel, id, identifier.asSymbol, label.asSymbol);
}
*new { |server,name|
serverCaches.at(server,name) !? {
FluidLabelSetExistsError("A FluidLabelSet called % already exists.".format(name)).throw;
};
^super.new(server,FluidManipulationClient.prServerString(name))!?{|inst|inst.init(name);inst}
updateLabel{|identifier, label, action|
actions[\updateLabel] = [nil,action];
this.prSendMsg(this.updateLabelMsg(identifier,label));
}
init { |name|
this.baseinit(FluidManipulationClient.prServerString(name));
id = name;
this.cache;
}
getLabelMsg{|identifier|
^this.prMakeMsg(\getLabel, id, identifier.asSymbol);
}
cache {
serverCaches.initCache(server);
serverCaches.put(server,id,this);
getLabel{|identifier, action|
actions[\getLabel] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.getLabelMsg(identifier));
}
asString {
^"FluidLabelSet(%)".format(id).asString;
}
deleteLabelMsg{|identifier, action|
^this.prMakeMsg(\deleteLabel, id, identifier.asSymbol);
}
asSymbol {
^id
deleteLabel{|identifier, action|
actions[\deleteLabel] = [nil, action];
this.prSendMsg(this.deleteLabelMsg(identifier));
}
*asUGenInput { |input|
var ascii = input.asString.ascii;
^[ascii.size].addAll(ascii)
}
clearMsg { ^this.prMakeMsg(\clear,id); }
addLabel{|id, label, action|
this.prSendMsg(\addLabel, [id.asString, label.asString],action);
}
updateLabel{|id, label, action|
this.prSendMsg(\updateLabel, [id.asString, label.asString],action);
}
clear { |action|
actions[\clear] = [nil,action];
this.prSendMsg(this.clearMsg);
}
getLabel{|id, action|
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;});
}
printMsg { ^this.prMakeMsg(\print,id); }
print { |action|
action ?? {action = postit};
this.prSendMsg(\print,[], action, [string(FluidMessageResponse,_,_)]);
print { |action=(postResponse)|
actions[\print] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.printMsg);
}
}

@ -1,4 +1,4 @@
FluidMDS : FluidDataClient {
FluidMDS : FluidModelObject {
classvar < manhattan = 0;
classvar < euclidean = 1;
classvar < sqeuclidean = 2;
@ -7,17 +7,27 @@ FluidMDS : FluidDataClient {
classvar < kl = 5;
classvar < cosine = 5;
var <>numDimensions, <>distanceMetric;
*new {|server,numDimensions = 2, distanceMetric = 1|
^super.new1(server,[
\numDimensions,numDimensions,
\distanceMetric, distanceMetric
])
^super.new(server,[numDimensions, distanceMetric])
.numDimensions_(numDimensions)
.distanceMetric_(distanceMetric);
}
prGetParams{
^[this.numDimensions, this.distanceMetric];
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id, sourceDataSet.id, destDataSet.id);
}
fitTransform{|sourceDataSet, destDataSet, action|
this.prSendMsg(\fitTransform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action);
actions[\fitTransform] = [nil,action];
this.fitTransformMsg(sourceDataSet,destDataSet).postln;
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}
// not implemented

@ -4,6 +4,7 @@ FluidMFCC : MultiOutUGen {
^this.multiNew('control', in.asAudioRateInput(this), numCoeffs, numBands, minFreq, maxFreq, maxNumCoeffs, windowSize, hopSize, fftSize, maxFFTSize);
}
init {arg ...theInputs;
inputs = theInputs;
^this.initOutputs(inputs.at(5),rate);

@ -1,103 +1,151 @@
FluidMLPRegressor : FluidRTDataClient {
FluidMLPRegressor : FluidRealTimeModel {
const <identity = 0;
const <sigmoid = 1;
const <relu = 2;
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|
var hiddenCtrlLabels;
hidden = [hidden.size]++hidden;
hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i};
^super.new1(server,
[hiddenCtrlLabels,hidden].lace ++
[
\activation,activation,
\outputActivation, outputActivation,
\tapIn, tapIn,
\tapOut, tapOut,
\maxIter, maxIter,
\learnRate,learnRate,
\momentum, momentum,
\batchSize,batchSize,
\validation,validation,
])
^super.new(server, [hidden.size] ++ hidden ++ [activation, outputActivation, tapIn, tapOut, maxIter, learnRate, momentum, batchSize, validation])
.hidden_(hidden)
.activation_(activation)
.outputActivation_(outputActivation)
.tapIn_(tapIn)
.tapOut_(tapOut)
.maxIter_(maxIter)
.learnRate_(learnRate)
.momentum_(momentum)
.batchSize_(batchSize)
.validation_(validation);
}
clear{ |action|
this.prSendMsg(\clear,action:action);
prGetParams{
^[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|
this.prSendMsg(\fit,
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
action,numbers(FluidMessageResponse,_,1,_)
);
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetDataSet));
}
predict{ |sourceDataSet, targetDataSet, action|
this.prSendMsg(\predict,
[sourceDataSet.asSymbol, targetDataSet.asSymbol],
action);
predictMsg{|sourceDataSet, targetDataSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetDataSet.id);
}
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|
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
targetBuffer = this.prEncodeBuffer(targetBuffer);
this.prSendMsg(\predictPoint,
[sourceBuffer.asUGenInput, targetBuffer.asUGenInput], action,outputBuffers:[targetBuffer]);
actions[\predictPoint] = [nil,{action.value(targetBuffer)}];
this.predictPointMsg(sourceBuffer, targetBuffer);
this.prSendMsg(this.predictPointMsg(sourceBuffer, 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 <sigmoid = 1;
const <relu = 2;
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|
var hiddenCtrlLabels;
hidden = [hidden.size]++hidden;
hiddenCtrlLabels = hidden.collect{|x,i| \hidden++i};
^super.new1(server,
[hiddenCtrlLabels,hidden].lace ++
[
\activation,activation,
\maxIter, maxIter,
\learnRate,learnRate,
\momentum, momentum,
\batchSize,batchSize,
\validation,validation,
])
^super.new(server,[hidden.size] ++ hidden ++ [activation, maxIter, learnRate, momentum, batchSize, validation])
.hidden_(hidden)
.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|
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|
this.prSendMsg(\fit,
[sourceDataSet.asSymbol, targetLabelSet.asSymbol],
action,numbers(FluidMessageResponse,_,1,_)
);
actions[\fit] = [numbers(FluidMessageResponse,_,1,_),action];
this.prSendMsg(this.fitMsg(sourceDataSet,targetLabelSet));
}
predictMsg{|sourceDataSet, targetLabelSet|
^this.prMakeMsg(\predict,id,sourceDataSet.id, targetLabelSet.id);
}
predict{ |sourceDataSet, targetLabelSet, action|
this.prSendMsg(\predict,
[sourceDataSet.asSymbol, targetLabelSet.asSymbol],
action);
actions[\predict]=[nil,action];
this.prSendMsg(this.predictMsg(sourceDataSet,targetLabelSet));
}
predictPointMsg { |sourceBuffer|
^this.prMakeMsg(\predictPoint,id,this.prEncodeBuffer(sourceBuffer))
}
predictPoint { |sourceBuffer, action|
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
this.prSendMsg(\predictPoint,
[sourceBuffer], action, string(FluidMessageResponse,_,_));
actions[\predictPoint] = [string(FluidMessageResponse,_,_),action];
this.prSendMsg(this.predictPointMsg(sourceBuffer));
}
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,298 +0,0 @@
FluidProxyUgen : UGen {
var <>pluginname;
*kr { |pluginname...args|
^this.new1('control', pluginname,*args)
}
init { |pluginname...args|
this.pluginname = pluginname;
inputs = args;
pluginname
.asSymbol
.asClass
.superclasses
.indexOf(FluidRTDataClient) ??{inputs= inputs ++ [Done.none,0]};
rate = 'control';
}
name{
^pluginname.asString;
}
}
FluidManipulationClient {
classvar clock;
var <server;
var <synth,<>ugen;
var id;
var defName, def;
var onSynthFree, keepAlive;
var aliveThread;
var postit;
*initClass {
clock = TempoClock.new;
clock.permanent = true;
}
*prServerString{ |s|
var ascii = s.ascii;
^[ascii.size].addAll(ascii)
}
*newFromDesc { arg rate, numOutputs, inputs, specialIndex;
^FluidProxyUgen.newFromDesc(rate, numOutputs, inputs, specialIndex)
}
*new{ |server,objectID...args|
server = server ? Server.default;
if(server.serverRunning.not,{
(this.asString + " server not running").error; ^nil
});
^super.newCopyArgs(server ?? {Server.default});//.baseinit(objectID,*args)
}
makeDef { |defName,objectID,args|
var initialVals = [];
args!? { if(args.size > 0) {initialVals = args.unlace(2)[1].flatten}};
^SynthDef(defName,{
var ugen = FluidProxyUgen.kr(this.class.name, *(initialVals ++ objectID));
this.ugen = ugen;
ugen
});
}
updateSynthControls {}
baseinit { |objectID...args|
var makeFirstSynth,synthMsg,synthArgs;
id = UniqueID.next;
postit = {|x| x.postln;};
keepAlive = true;
defName = (this.class.name.asString ++ id).asSymbol;
def = this.makeDef(defName,objectID,args);
synth = Synth.basicNew(def.name, server);
synthMsg = synth.newMsg(RootNode(server),args);
def.doSend(server,synthMsg);
onSynthFree = {
synth = nil;
if(keepAlive){
synth = Synth(defName,target: RootNode(server));
synth.onFree{clock.sched(0,onSynthFree)};
this.updateSynthControls;
}
};
CmdPeriod.add({synth = nil});
synth.onFree{clock.sched(0,onSynthFree)};
}
free{
keepAlive = false;
if(server.serverRunning){server.sendMsg("/cmd","free"++this.class.name,id)};
synth.tryPerform(\free);
^nil
}
cols {|action|
action ?? {action = postit};
this.prSendMsg(\cols,[],action,[numbers(FluidMessageResponse,_,1,_)]);
}
read{|filename, action|
this.prSendMsg(\read,[filename.asString],action);
}
write{|filename, action|
this.prSendMsg(\write,[filename.asString],action);
}
size {|action|
action ?? {action = postit};
this.prSendMsg(\size,[],action,[numbers(FluidMessageResponse,_,1,_)]);
}
prEncodeBuffer { |buf| buf !? {^buf.asUGenInput} ?? {^-1} }
prSendMsg { |msg, args, action,parser,outputBuffers|
if(this.server.serverRunning.not,{(this.asString + " server not running").error; ^nil});
forkIfNeeded{
synth ?? {onSynthFree.value; server.sync};
OSCFunc(
{ |msg|
defer{
var result;
result = FluidMessageResponse.collectArgs(parser,msg.drop(3));
if(outputBuffers.notNil) {
forkIfNeeded {
outputBuffers.collectInPlace{ |b|
server.cachedBufferAt(b) !? {|x| x.updateInfo}
};
server.sync;
if(action.notNil){action.value(*(outputBuffers++result))}{action.value};
};
}
{if(action.notNil){action.value(result)}{action.value};}
}
},'/'++msg, server.addr, nil,[synth.nodeID]).oneShot;
server.listSendMsg(['/u_cmd', synth.nodeID, ugen.synthIndex, msg].addAll(args));
}
}
}
FluidDataClient : FluidManipulationClient {
var synthControls;
var <id;
var parameters;
var parameterDefaults;
*new {|server|
^this.new1(server,#[])
}
*new1{ |server, params|
var uid = UniqueID.next;
^super.new(server, uid, *params) !? { |inst| inst.init(uid, params) }
}
init { |uid, params|
id = uid;
params = params ?? {[]};
if(params.size > 0 and: synthControls.isNil) {synthControls = params.unlace[0]};
parameters = ().putPairs(params);
parameterDefaults = parameters.copy;
this.makePropertyMethods;
this.baseinit(uid,*params);
}
makePropertyMethods{
if (parameters.keys.size > 0) {
parameters.keys.do{|c,i|
this.addUniqueMethod(c,{ parameters.at(c) });
this.addUniqueMethod((c++\_).asSymbol,{|responder,x|
//if we have a default or initial value set, then fall back to
//this if val is nil. Otherwise, fallback even furter to -1 as
// a best guess
x = x ?? { parameterDefaults !? { parameterDefaults[c] } ?? {-1} };
parameters.put(c, x.asUGenInput);
synth !? { if(synth.isRunning){ synth.set(c,x); } };
responder
});
}
};
}
makeDef {|defName,uid,args|
var defControls = synthControls;
var ugenControls = [this.class.name] ++ synthControls ++ uid;
var f = (
"{ |dataClient|"
" SynthDef("++defName.asCompileString++", { |" ++ defControls.join(",") ++ "|"
" var ugen = FluidProxyUgen.kr(" ++ ugenControls.join(",") ++ ");"
" dataClient.ugen = ugen;"
" ugen"
" })"
"}"
);
var res = f.interpret.value(this);
^res
}
updateSynthControls{
synth !? { synth.set(*parameters.asKeyValuePairs); };
}
}
FluidRTDataClient : FluidDataClient
{
*new1{|server, params|
params = params ?? {[]};
^super.new1(server,params)
}
init { |uid, params|
id = uid;
params = params ?? {[]};
if(params.size > 0) {synthControls = params.unlace[0]}{synthControls=[]};
params = params ++ [\inBus,Bus.control(server),\outBus,Bus.control(server),\inBuffer,-1,\outBuffer,-1];
parameters = ().putPairs(params);
parameterDefaults = parameters.copy;
this.makePropertyMethods;
this.baseinit(uid,*params);
}
makeDef {|defName,uid,args|
var defControls = [\inBus, \outBus] ++ synthControls ++ [\inBuffer,\outBuffer];
var ugenControls = [this.class.name,"T2A.ar(In.kr(inBus))"] ++ synthControls ++ [\inBuffer,\outBuffer,uid];
var f = (
"{ |dataClient|"
" SynthDef("++defName.asCompileString++", { |" ++ defControls.join(",") ++ "|"
" var ugen = FluidProxyUgen.kr(" ++ ugenControls.join(",") ++ ");"
" dataClient.ugen = ugen;"
" Out.kr(outBus,ugen);"
" })"
"}"
);
var res = f.interpret.value(this);
^res
}
}
FluidServerCache {
var cache;
*new{ ^super.new.init }
init{
cache = IdentityDictionary.new;
}
do { |server, func|
cache[server]!?{cache[server].do{|x|func.value(x)}}
}
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] !? { cache.removeAt(server) };
}
}

@ -1,4 +1,4 @@
+ FluidManipulationClient {
+ FluidDataObject {
tmpJSONFilename{
^Platform.defaultTempDir++"tmp_fluid_data_"++
Date.localtime.stamp++"_"++UniqueID.next++".json";
@ -6,8 +6,8 @@
dump {|action|
var filename = this.tmpJSONFilename;
action ?? {action = postit};
this.write(filename, {
action ?? {action = postResponse};
this.write(filename, {
action.value(this.parseJSON(File.readAllString(filename)));
File.delete(filename);
});

@ -17,15 +17,12 @@ FluidMessageResponse : Object
}
*string{ |a, offset|
var split = a.find([0],offset);
var res;
if(split.isNil) {"ERROR: can't parse string from server".throw};
^[[a.copyRange(offset,split-1).keep(split).collectAs({|x|x.asInteger.asAscii},String)], split + 1]
^[a]
}
*strings {|a,offset|
//TODO add an n argument as with numbers() to make this less omnivorous
^[a.drop(offset).drop(-1).collectAs({|x|x.asInteger.asAscii},String).split(0.asAscii)]
^[a.drop(offset)];
}
*numbers{ |a, n, offset|
@ -41,4 +38,4 @@ FluidMessageResponse : Object
server = server ? Server.default ;
^[Buffer.cachedBufferAt(server, a[offset]), offset + 1]
}
}
}

@ -1,31 +1,68 @@
FluidNormalize : FluidRTDataClient {
FluidNormalize : FluidRealTimeModel {
var <>min, <>max, <>invert;
*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|
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|
this.prSendMsg(\transform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
);
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet, destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
this.prSendMsg(\fitTransform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
);
actions[\fitTransform] = [nil,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|
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
destBuffer = this.prEncodeBuffer(destBuffer);
this.prSendMsg(\transformPoint,
[sourceBuffer, destBuffer], action,
outputBuffers:[destBuffer]
);
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer, 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|
^super.new1(server,[\numDimensions,numDimensions]);
}
var <>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|
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|
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|
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|
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
destBuffer = this.prEncodeBuffer(destBuffer);
this.prSendMsg(\transformPoint,[sourceBuffer, destBuffer], action, outputBuffers:[destBuffer]);
actions[\transformPoint] = [nil,{action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,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 {
*new {|server, invert = 0|
^super.new1(server,[\invert, invert]);
FluidStandardize : FluidRealTimeModel {
var <>invert;
*new {|server, invert = 0|
^super.new(server,[invert]).invert_(invert);
}
fit{|dataSet, action|
this.prSendMsg(\fit, [dataSet.asSymbol], action);
prGetParams{
^[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|
this.prSendMsg(\transform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
);
actions[\transform] = [nil,action];
this.prSendMsg(this.transformMsg(sourceDataSet,destDataSet));
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform,id,sourceDataSet.id,destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
this.prSendMsg(\fitTransform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action
);
actions[\fitTransform] = [nil,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|
sourceBuffer = this.prEncodeBuffer(sourceBuffer);
destBuffer = this.prEncodeBuffer(destBuffer);
this.prSendMsg(\transformPoint,
[sourceBuffer, destBuffer], action, outputBuffers:[destBuffer]
);
actions[\transformPoint] = [nil, {action.value(destBuffer)}];
this.prSendMsg(this.transformPointMsg(sourceBuffer,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,29 +1,35 @@
FluidUMAP : FluidRTDataClient {
FluidUMAP : FluidModelObject {
*new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1, batchSize = 50|
^super.new1(server,[
\numDimensions,numDimensions,
\numNeighbours, numNeighbours,
\minDist, minDist,
\iterations, iterations,
\learnRate, learnRate
])
}
var <>numDimensions, <>numNeighbours, <>minDist, <>iterations, <>learnRate, <>batchSize;
fit{|sourceDataSet, action|
this.prSendMsg(\fit,
[sourceDataSet.asSymbol], action);
}
transform{|sourceDataSet, destDataSet, action|
this.prSendMsg(\transform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action);
*new {|server,numDimensions = 2, numNeighbours = 15, minDist = 0.1, iterations = 200, learnRate = 0.1, batchSize = 50|
^super.new(server,[numDimensions, numNeighbours, minDist, iterations, learnRate, batchSize])
.numDimensions_(numDimensions)
.numNeighbours_(numNeighbours)
.minDist_(minDist)
.iterations_(iterations)
.learnRate_(learnRate)
.batchSize_(batchSize);
}
prGetParams{
^[
this.numDimensions,
this.numNeighbours,
this.minDist,
this.iterations,
this.learnRate,
this.batchSize
]
}
fitTransformMsg{|sourceDataSet, destDataSet|
^this.prMakeMsg(\fitTransform, id, sourceDataSet.id, destDataSet.id)
}
fitTransform{|sourceDataSet, destDataSet, action|
this.prSendMsg(\fitTransform,
[sourceDataSet.asSymbol, destDataSet.asSymbol], action);
actions[\fitTransform] = [nil, action];
this.prSendMsg(this.fitTransformMsg(sourceDataSet,destDataSet));
}
transformPoint{|sourceBuffer, destBuffer, action|

@ -19,7 +19,11 @@ FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1,
// (optional) compare auraly the stereo with the MS
b.play;
{PlayBuf.ar(1,[c,d])}.play;
{PlayBuf.ar(1, [c,d])}.play;
//check nullsum with real-time versions
{PlayBuf.ar(1, c) - PlayBuf.ar(2, b).madd(-3.0.dbamp).sum}.play;
{PlayBuf.ar(1, d) - PlayBuf.ar(2, b).madd(-3.0.dbamp * [1,-1]).sum}.play;
// The geeky bit: copy the side (buffer d) on itself with specific amplitudes and delays, in effect applying a FIR filter through expensive convolution

@ -40,6 +40,6 @@ c.postln
b.play
{PlayBuf.ar(1,b.bufnum,startPos:c[740])}.play
{PlayBuf.ar(1,b.bufnum,startPos:c[15])}.play
Buffer.freeAll

@ -37,4 +37,4 @@ b.plot
c.postln
b.play
{PlayBuf.ar(1,b.bufnum,startPos:c[740])}.play
{PlayBuf.ar(1,b.bufnum,startPos:c[15])}.play

@ -1,6 +1,6 @@
// define a few processes
(
~ds = FluidDataSet(s,\test); // still need a name on the server to make sure we do not forget it exists. it is now permanent aka will resist cmd+.
~ds = FluidDataSet(s);//no name needs to be provided
//define as many buffers as we have parallel voices/threads in the extractor processing (default is 4)
~mfccbuf = 4.collect{Buffer.new};
~statsbuf = 4.collect{Buffer.new};
@ -8,11 +8,11 @@
// here we instantiate a loader which creates a single large buffer with a dictionary of what was included in it
// ~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/sons/smallnum/");
~loader = FluidLoadFolder("/Volumes/machins/projets/newsfeed/sons/segments/");
~loader = FluidLoadFolder(File.realpath(FluidLoadFolder.class.filenameSymbol).dirname +/+ "../AudioFiles");
// here we instantiate a further slicing step if needs be, which iterate through all the items of the FluidLoadFolder and slice the slices with the declared function. Here it is a very picky onset slicer
// here we instantiate a further slicing step if needs be, which iterate through all the items of the FluidLoadFolder and slice the slices with the declared function.
~slicer = FluidSliceCorpus({ |src,start,num,dest|
FluidBufOnsetSlice.kr(src,start,num,metric: 9, minSliceLength: 17, indices:dest, threshold:2,blocking: 1)
FluidBufOnsetSlice.kr(src, start, num, metric: 9, minSliceLength: 17, indices:dest, threshold:0.7, blocking: 1)
});
// here we instantiate a process of description and dataset writing, which will run each slice of the previous slice and write the entry. Note the chain of Done.kr triggers.
@ -20,10 +20,10 @@
var mfcc, stats, writer, flatten,mfccBuf, statsBuf, flatBuf, label, voice;
label = data.key;
voice = data.value[\voice];
mfcc = FluidBufMFCC.kr(src,startFrame:start,numFrames:num,numChans:1,features:~mfccbuf[voice],trig:1,blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice],stats:~statsbuf[voice],trig:Done.kr(mfcc),blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice],~flatbuf[voice],trig:Done.kr(stats),blocking: 1);
writer = FluidDataSetWr.kr(~ds,label, -1, ~flatbuf[voice], Done.kr(flatten),blocking: 1)
mfcc = FluidBufMFCC.kr(src, startFrame:start, numFrames:num, numChans:1, features:~mfccbuf[voice], trig:1, blocking: 1);
stats = FluidBufStats.kr(~mfccbuf[voice], stats:~statsbuf[voice], trig:Done.kr(mfcc), blocking: 1);
flatten = FluidBufFlatten.kr(~statsbuf[voice], ~flatbuf[voice], trig:Done.kr(stats), blocking: 1);
writer = FluidDataSetWr.kr(~ds, label, -1, ~flatbuf[voice], trig: Done.kr(flatten), blocking: 1)
});
)
@ -134,7 +134,7 @@ t = Main.elapsedTime;
//retrieve a sound to match
~targetsound = Buffer(s);
~targetname = ~slicer.index.keys.asArray.last.asSymbol;
~targetname = ~slicer.index.keys.asArray.scramble[0].asSymbol;
#a,b = ~slicer.index[~targetname][\bounds];
FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targetsound,action: {~targetsound.play;})
@ -145,6 +145,7 @@ FluidBufCompose.process(s,~loader.buffer,a,(b-a),numChans: 1, destination: ~targ
mfcc = FluidBufMFCC.kr(~targetsound,features:~mfccbuf[0],trig:1);
stats = FluidBufStats.kr(~mfccbuf[0],stats:~statsbuf[0],trig:Done.kr(mfcc));
flatten = FluidBufFlatten.kr(~statsbuf[0],~flatbuf[0],trig:Done.kr(stats));
FreeSelfWhenDone.kr(flatten);
}.play;
)

@ -1,8 +1,8 @@
// define a few processes
(
~ds = FluidDataSet(s,\ds10);
~dsW = FluidDataSet(s,\ds10W);
~dsL = FluidDataSet(s,\ds10L);
~ds = FluidDataSet(s);
~dsW = FluidDataSet(s);
~dsL = FluidDataSet(s);
//define as many buffers as we have parallel voices/threads in the extractor processing (default is 4)
~loudbuf = 4.collect{Buffer.new};
~weightbuf = 4.collect{Buffer.new};
@ -94,10 +94,12 @@ t = Main.elapsedTime;
// extracting whatever stats we want. In this case, mean/std/lowest/highest, and the same on the first derivative - excluding MFCC0 as it is mostly volume, keeping MFCC1-12
~curated = FluidDataSet(s,\ds10c);
~curatedW = FluidDataSet(s,\ds10cW);
(
~curated = FluidDataSet(s);
~curatedW = FluidDataSet(s);
~curator = FluidDataSetQuery.new(s);
)
(
~curator.addRange(1,12,{
~curator.addRange(14,12,{

@ -68,7 +68,6 @@ c.free
//for completion, here is just with rejection of outliers - not as good, but a decent second best!
FluidBufStats.process(s,~pitches, stats:~stats,outliersCutoff: 1.5)
~stats.getn(0,14,{|x|~pitchIQRStats = x;x.reshape(7,2).do{|y| "%\t\t\t%\n".postf(y[0].round(0.1),y[1].round(0.01))}})
//now that is impressive!
c = {SinOsc.ar(~pitchIQRStats[0],mul: 0.05)}.play
b.play
c.free

@ -2,10 +2,10 @@
// define a few datasets
(
~pitchDS = FluidDataSet(s,\pitch11);
~loudDS = FluidDataSet(s,\loud11);
~mfccDS = FluidDataSet(s,\mfcc11);
~durDS = FluidDataSet(s,\dur11);
~pitchDS = FluidDataSet(s);
~loudDS = FluidDataSet(s);
~mfccDS = FluidDataSet(s);
~durDS = FluidDataSet(s);
//define as many buffers as we have parallel voices/threads in the extractor processing (default is 4)
~pitchbuf = 4.collect{Buffer.new};
@ -102,7 +102,7 @@ t = Main.elapsedTime;
///////////////////////////////////////////////////////
//reduce the MFCC timbral space stats (many potential ways to explore here... - 2 are provided to compare, with and without the derivatives before running a dimension reduction)
~tempDS = FluidDataSet(s,\temp11);
~tempDS = FluidDataSet(s);
~query = FluidDataSetQuery(s);
~query.addRange(0,24);//add only means and stddev of the 12 coeffs...
@ -115,12 +115,12 @@ t = Main.elapsedTime;
// standardizing before the PCA, as argued here:
// https://scikit-learn.org/stable/auto_examples/preprocessing/plot_scaling_importance.html
~stan = FluidStandardize(s);
~stanDS = FluidDataSet(s,\stan11);
~stanDS = FluidDataSet(s);
~stan.fitTransform(~tempDS,~stanDS)
//shrinking A: using 2 stats on the values, and 2 stats on the redivative (12 x 2 x 2 = 48 dim)
~pca = FluidPCA(s,4);//shrink to 4 dimensions
~timbreDSd = FluidDataSet(s,\timbreDSd11);
~timbreDSd = FluidDataSet(s);
~pca.fitTransform(~stanDS,~timbreDSd,{|x|x.postln;})//accuracy
//shrinking B: using only the 2 stats on the values
@ -133,7 +133,7 @@ t = Main.elapsedTime;
//keep its own PCA so we can keep the various states for later transforms
~pca2 = FluidPCA(s,4);//shrink to 4 dimensions
~timbreDS = FluidDataSet(s,\timbreDS11);
~timbreDS = FluidDataSet(s);
~pca2.fitTransform(~tempDS,~timbreDS,{|x|x.postln;})//accuracy
// comparing NN for fun
@ -181,7 +181,7 @@ Routine{
///////////////////////////////////////////////////////
// compositing queries - defining a target and analysing it
~globalDS = FluidDataSet(s,\global11);
~globalDS = FluidDataSet(s);
// define a source
~targetsound = Buffer.read(s,File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav",42250,44100);
@ -267,9 +267,9 @@ Routine{
// make sure to define and describe the source above (lines 178 to 201)
// let's make normalised versions of the 3 datasets, keeping the normalisers separate to query later
~loudDSn = FluidDataSet(s,\loud11n);
~pitchDSn = FluidDataSet(s,\pitch11n);
~timbreDSn = FluidDataSet(s,\timbre11n);
~loudDSn = FluidDataSet(s);
~pitchDSn = FluidDataSet(s);
~timbreDSn = FluidDataSet(s);
~normL = FluidNormalize(s)
~normP = FluidNormalize(s)
@ -290,13 +290,15 @@ Routine{
// let's assemble the query
// first let's normalise our target descriptors
~targetPitch = Buffer(s)
~targetLoud = Buffer(s)
~targetMFCC = Buffer(s)
~targetMFCCs = Buffer(s)
~targetMFCCsp = Buffer(s)
~targetTimbre = Buffer(s)
~targetAll= Buffer(s)
(
~targetPitch = Buffer(s);
~targetLoud = Buffer(s);
~targetMFCC = Buffer(s);
~targetMFCCs = Buffer(s);
~targetMFCCsp = Buffer(s);
~targetTimbre = Buffer(s);
~targetAll= Buffer(s);
)
~normL.transformPoint(~flatLoudbuf[0], ~targetLoud) //normalise the loudness (all dims)
~normP.transformPoint(~flatPitchbuf[0], ~targetPitch) //normalise the pitch (all dims)

@ -1,122 +1,228 @@
// load a source
b = Buffer.read(s,"/Volumes/machins/projets/newsfeed/sons/textes/Audio/synth/fromtexttospeech-AmE-George.wav")
b.play
// load a source folder
~loader = FluidLoadFolder(File.realpath(FluidBufPitch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/");
~loader.play;
//slightly oversegment with novelty
//segments should still make sense but might cut a few elements in 2 or 3
~originalslices = Buffer(s);
FluidBufNoveltySlice.process(s, b, indices: ~originalslices, feature: 1, kernelSize: 29, threshold: 0.05, filterSize: 5, hopSize: 128, action: {~originalslices.numFrames.postln;})
~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)});
~slicer.play(s, ~loader.buffer,~loader.index);
//test the segmentation by looping them
(
{
BufRd.ar(1, b,
Phasor.ar(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);
~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]};
d = {arg start=0, end = 44100;
BufRd.ar(1, ~loader.buffer, Phasor.ar(0,1,start,end,start),0,1);
}.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
~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
//analyse each segment with 20 MFCCs in a dataset and spectralshapes in another one
(
~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);
~stats = Buffer(s);
~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;
t = Main.elapsedTime;
~extractor.play(s,~loader.buffer, ~slicer.index, action:{(Main.elapsedTime - t).postln;"Analysis done".postln});
)
~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
//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;
~curated = FluidDataSet(s);
~query = FluidDataSetQuery(s);
~kmeans = FluidKMeans(s,2,100);
~windowDS = FluidDataSet(s,\windowDS);
~windowLS = FluidLabelSet(s,\windowLS);
~stan = FluidStandardize(s);
~kmeans = FluidKMeans(s,2,1000);
~windowDS = FluidDataSet(s);
~windowLS = FluidLabelSet(s);
)
(
Routine{
~indices = [0];
~head = 0;
~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);
//curate stats (MFCCs)
~query.clear
~query.addRange((0*20)+1,10);
~query.transform(~slicesMFCC,~curated);
//kmeans 2 and retrieve ordered array of class assignations
~kmeans.fitPredict(~windowDS, ~windowLS, {|x| nbass = x;});
s.sync;
"% - fitted1: ".postf(~head); nbass.postln;
//OR
//curate stats (moments)
~query.clear
~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;};
s.sync;
"% - assigned ".postf(~head);
//optionally standardize in place
~stan.fitTransform(~curated, ~curated);
~assignments.postln;
~curated.print
step.do{|i|
if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])});
};
//retrieve the dataset as dictionary
~curated.dump{|x|~sliceDict = x;};
~head = ~head + step;
"-----------------".postln;
});
~originalslicesarray = (~originalindices.flatten ++ ~loader.buffer.numFrames).asSet.asArray.sort
~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
if ( (~originalslicesarray.size - ~head) > 1, {
//run a process on (a.size - ~head) items from ~head
(~originalslicesarray.size - ~head - 1).do{|i|
if (~assignments[i+1] != ~assignments[i], {~indices= ~indices ++ (~originalslicesarray[~head+i+1])});
// (~head+i).postln;
};
assignments.postln;
(winSize-1).do{|i|
if (assignments[i+1] != assignments[i], {
~newindices= ~newindices ++ (~originalslicesarray[head+i+1]).asInteger;
~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;
{var i = 4;BufRd.ar(1,b,Line.ar(~indices[i],~indices[i+1],(~indices[i+1] - ~indices[i])/b.sampleRate, doneAction: 2))}.play;
//the job
//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
(
//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;
};
}
)

@ -1,12 +1,12 @@
s.reboot
~ds = FluidDataSet.new(s,\simple1data)
~ds = FluidDataSet.new(s)
~point = Buffer.alloc(s,1,1)
(
Routine{
10.do{|i|
~point.set(0,i);
~ds.addPoint(i.asString,~point,{("addPoint"+i).postln});
s.sync;
~ds.addPoint(i.asString,~point,{("addPoint"+i).postln}); //because buffer.set do an immediate update in the RT thread we can take for granted it'll be updated when we call addPoint
s.sync; //but we need to sync to make sure everything is done on the DataSet before the next iteration
}
}.play
)
@ -29,7 +29,7 @@ Routine{
/*** KMEANS ***/
~kmeans = FluidKMeans.new(s,maxIter:100)
~kmeans = FluidKMeans.new(s,maxIter:100);
~kmeans.numClusters = 2; //play with this
~kmeans.fit(~ds,action:{|x| "Done fitting with these number of items per cluster ".post;x.postln;})
@ -49,7 +49,7 @@ Routine{
(
~labels.size{|x|
Routine{x.asInteger.do{|i| //size does not return a value, but we can retrieve it via a function
Routine{x.asInteger.do{|i|
~labels.getLabel(i,action: {|l|
("Label for entry " + i ++ ":" + l).postln;
});
@ -60,4 +60,14 @@ Routine{
)
// or simply print it
~labels.print
~labels.print
// or dump and format
(
~labels.dump{|x|
var keys = x["data"].keys.asArray.sort;
keys.do{|key|
"Label for entry % is %\n".postf(key, x["data"][key][0]);
}
}
)

@ -1,5 +1,5 @@
s.reboot
~ds = FluidDataSet.new(s,\simple1data)
~ds = FluidDataSet.new(s)
~point = Buffer.alloc(s,1,1)
(
Routine{
@ -41,8 +41,8 @@ Routine{
Routine{
15.do{|i|
~point.set(0,i);
s.sync;
~kmeans.predictPoint(~point,{|x| ("Predicted Cluster for a value of " + i ++ ":" + x).postln});
s.sync;
}
}.play
)

@ -1,6 +1,6 @@
(
~simpleInput = FluidDataSet(s,\simpleInput);
~simpleOutput = FluidLabelSet(s,\simpleOutput);
~simpleInput = FluidDataSet(s);
~simpleOutput = FluidLabelSet(s);
b = Buffer.alloc(s,2);
~knn = FluidKNNClassifier(s);
~knn.numNeighbours = 3
@ -53,7 +53,8 @@ Routine{
~simpleInput.addPoint((i+50).asString,b,{("Added Input" + (i+50)).postln});
~simpleOutput.addLabel((i+50).asString,"Red",{("Added Output" + (i+50)).postln});
s.sync;
}
};
\done.postln;
}.play;
)

@ -9,9 +9,10 @@ n = 200
// creates the dataset with these associated indices and values
(
~simpleInput = FluidDataSet(s,\simpleInput);
~simpleOutput = FluidDataSet(s,\simpleOutput);
b = Buffer.alloc(s,1,1);
~simpleInput = FluidDataSet(s);
~simpleOutput = FluidDataSet(s);
b = Buffer.alloc(s,1);
c = Buffer.alloc(s,1);
~mappingviz = Buffer.alloc(s,512);
)
@ -19,12 +20,11 @@ b = Buffer.alloc(s,1,1);
Routine{
n.do{|i|
b.set(0,~idx[i]);
c.set(0,~data[i]);
~simpleInput.addPoint(i.asString,b,{("Added Input" + i).postln});
~simpleOutput.addPoint(i.asString,c,{("Added Output" + i).postln});
~mappingviz.set((~idx[i]/61.4).asInteger,~data[i]);
s.sync;
b.set(0,~data[i]);
~simpleOutput.addPoint(i.asString,b,{("Added Output" + i).postln});
s.sync;
~mappingviz.set((~idx[i]/61).asInteger,~data[i])
}
}.play
)

@ -1,7 +1,7 @@
(
// set some variables
~nb_of_dim = 10;
~dataset = FluidDataSet(s,\test,~nb_of_dim);
~dataset = FluidDataSet(s);
)
(
@ -14,6 +14,7 @@ Routine{
s.sync;
});
buf.free;
\done.postln;
}.play
)
@ -38,7 +39,7 @@ Routine{
~normalize.fit(~dataset,{"done".postln;});
// making an empty 'normed_dataset' which is required for the normalize function
~normed_dataset = FluidDataSet(s,\normed,~nb_of_dim);
~normed_dataset = FluidDataSet(s);
// normalize the full dataset
~normalize.transform(~dataset,~normed_dataset,{"done".postln;});
@ -55,7 +56,7 @@ Routine{
~standardize.fit(~dataset,{"done".postln;});
// standardize the full dataset
~standardized_dataset = FluidDataSet(s,\standardized,~nb_of_dim);
~standardized_dataset = FluidDataSet(s);
~standardize.transform(~dataset,~standardized_dataset,{"done".postln;});
// look at a point to see that it has points in it
@ -89,7 +90,7 @@ Routine{
~normtree.fit(~normed_dataset)
~normtree.kNearest(~normbuf, {|x| ("Labels:" + x).postln});
~normtree.kNearestDist(~normbuf, {|x| ("Distances:" + x).postln});
// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will probably have changed. The distance is now different too
// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg might have changed. The distance is now different too
// standardize that same point (~query_buf) to be at the right scale
~stdbuf = Buffer.alloc(s,~nb_of_dim);
@ -101,7 +102,7 @@ Routine{
~stdtree.fit(~standardized_dataset)
~stdtree.kNearest(~stdbuf, {|x| ("Labels:" + x).postln});
~stdtree.kNearestDist(~stdbuf, {|x| ("Distances:" + x).postln});
// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg will probably have changed yet again. The distance is also different too
// its nearest neighbourg is still itself as it should be, but the 2nd neighbourg might have changed yet again. The distance is also different too
// where it starts to be interesting is when we query points that are not in our original dataset
@ -112,7 +113,7 @@ Routine{
~normalize.transformPoint(~query_buf,~normbuf);
~standardize.transformPoint(~query_buf,~stdbuf);
//query the single nearest neighbourg via 3 different data scaling. Depending on the random source at the begining, you will get small to large differences between the 3 answers!
//query the single nearest neighbourg via 3 different data scaling. Depending on the random source at the begining, you should get (small or large) differences between the 3 answers!
[~tree,~normtree,~stdtree].do{|t| t.numNeighbours =1 };
~tree.kNearest(~query_buf, {|x| ("Original:" + x).post;~tree.kNearestDist(~query_buf, {|x| (" with a distance of " + x).postln});});
~normtree.kNearest(~normbuf, {|x| ("Normalized:" + x).post;~normtree.kNearestDist(~normbuf, {|x| (" with a distance of " + x).postln});});

@ -41,4 +41,12 @@
~classifier = FluidKNNClassifier.new(s, numNeighbours:2);
~classifier.fit(~ds,~ls, {\done.postln;})
~classifier.predictPoint(~target, action: {|x|x.postln;})
// run many times for random pleasure
(
fork{
var value = 7.rand;
~ds.getPoint(("entry-"++value).asSymbol,~target);
s.sync;
~classifier.predictPoint(~target, action: {|x|"entry % is an % entry.\n".postf(value,x);})
}
)

@ -8,13 +8,13 @@ Routine{
d = Dictionary.new;
d.add(\cols -> 2);
d.add(\data -> Dictionary.newFrom(["f-f", [0,0], "f-t", [0,1], "t-f", [1,0], "t-t", [1,1]]));
~mlpHelpSource = FluidDataSet.new(s,\mlpHelpSource);
~mlpHelpSource = FluidDataSet.new(s);
s.sync;
~mlpHelpSource.load(d);
s.sync;
d.add(\cols -> 1);
d.add(\data -> Dictionary.newFrom(["f-f", [0], "f-t", [1], "t-f", [1], "t-t", [0]]));
~mlpHelpTarget = FluidDataSet.new(s,\mlpHelpTarget);
~mlpHelpTarget = FluidDataSet.new(s);
s.sync;
~mlpHelpTarget.load(d);
s.sync;
@ -27,7 +27,7 @@ Routine{
// make an MLPregressor
~mlp = FluidMLPRegressor(s, [3], FluidMLPRegressor.sigmoid, FluidMLPRegressor.sigmoid,maxIter:1000,learnRate: 0.1,momentum: 0.1,batchSize: 1,validation: 0);//1000 epoch at a time
//train on it and observe the error
//train it by executing the following line multiple time, and observe the error
~mlp.fit(~mlpHelpSource,~mlpHelpTarget,{|x|x.postln;});
//to make a plot of the error let's do a classic 'shades of truth' (a grid of 11 x 11 with each values of truth between 0 and 1
@ -37,7 +37,7 @@ Routine{
d.add(\cols -> 2);
d.add(\data -> Dictionary.newFrom(121.collect{|x|[x.asString, [x.div(10)/10,x.mod(10)/10]]}.flatten));
~mlpHelpShades = FluidDataSet.new(s,\mlpHelpShades);
~mlpHelpShades = FluidDataSet.new(s);
s.sync;
~mlpHelpShades.load(d);
s.sync;
@ -48,7 +48,7 @@ Routine{
~mlpHelpShades.print
// let's make a destination for our regressions
~mlpHelpRegressed = FluidDataSet.new(s,\mlpHelpRegressed);
~mlpHelpRegressed = FluidDataSet.new(s);
// then predict the full DataSet in our trained network
~mlp.predict(~mlpHelpShades,~mlpHelpRegressed);
@ -77,6 +77,7 @@ w.refresh;
w.front;
)
~mlp.free
~mlpHelpShades.free
~mlpHelpSource.free
~mlpHelpTarget.free

@ -7,8 +7,8 @@ var output = Buffer.alloc(s,10);
var mlp = FluidMLPRegressor(s,[6],activation: 1,outputActivation: 1,maxIter: 1000,learnRate: 0.1,momentum: 0,batchSize: 1,validation: 0);
var entry = 0;
~inData = FluidDataSet(s,\indata);
~outData = FluidDataSet(s,\outdata);
~inData = FluidDataSet(s);
~outData = FluidDataSet(s);
w = Window("ChaosSynth", Rect(10, 10, 790, 320)).front;
a = MultiSliderView(w,Rect(10, 10, 400, 300)).elasticMode_(1).isFilled_(1);
@ -42,8 +42,10 @@ f.action = {arg x,y; //if trained, predict the point f.x f.y
c = Button(w, Rect(730,240,50, 20)).states_([["train", Color.red, Color.white], ["trained", Color.white, Color.grey]]).action_{
mlp.fit(~inData,~outData,{|x|
trained = 1;
c.value = 1;
e.value = x.round(0.001).asString;
{
c.value = 1;
e.value = x.round(0.001).asString;
}.defer;
});//train the network
};
d = Button(w, Rect(730,10,50, 20)).states_([["entry", Color.white, Color.grey], ["entry", Color.red, Color.white]]).action_{

@ -1,9 +1,9 @@
s.reboot;
//Preliminaries: we want some audio, a couple of FluidDataSets, some Buffers
(
~raw = FluidDataSet(s,\Mel40);
~norm = FluidDataSet(s,\Mel40n);
~retrieved = FluidDataSet(s,\ae2);
~raw = FluidDataSet(s);
~norm = FluidDataSet(s);
~retrieved = FluidDataSet(s);
~audio = Buffer.read(s,File.realpath(FluidBufMelBands.class.filenameSymbol).dirname +/+ "../AudioFiles/Tremblay-ASWINE-ScratchySynth-M.wav");
~melfeatures = Buffer.new(s);
~stats = Buffer.alloc(s, 7, 40);
@ -56,7 +56,7 @@ FluidBufMelBands.process(s,~audio, features: ~melfeatures,action: {\done.postln;
~retrieved.print;
//let's normalise it for display
~normData = FluidDataSet(s,\ae2N);
~normData = FluidDataSet(s);
~reducedarray = Array.new(100);
~normalView = FluidNormalize(s,0.1,0.9);
(

@ -2,9 +2,9 @@
//Here we make a 3-points pair of dataset.
~dsIN = FluidDataSet(s,\dsIN);
~dsIN = FluidDataSet(s);
~dsIN.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\point1, [10], \point2, [20], \point3, [30]])]));
~dsOUT = FluidDataSet(s,\dsOUT);
~dsOUT = FluidDataSet(s);
~dsOUT.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom([\point1, [0.8], \point2, [0.2], \point3, [0.5]])]));
//check the values
@ -13,13 +13,13 @@
(0.dup(10) ++ 0.8 ++ 0.dup(9) ++ 0.2 ++ 0.dup(9) ++ 0.5 ++ 0.dup(10)).plot(\source,discrete: true, minval:0, maxval: 1).plotMode=\bars;
//Let's make a complete dataset to predict each points in our examples:
~dsALLin = FluidDataSet(s,\dsALLin);
~dsALLin = FluidDataSet(s);
~dsALLin.load(Dictionary.newFrom([\cols, 1, \data, Dictionary.newFrom(Array.fill(41,{|x| [x.asSymbol, [x]];}).flatten(1);)]));
~dsALLin.print
//We'll regress these values via KNN and plot
~regK = FluidKNNRegressor(s,numNeighbours: 2,weight: 1);
~dsALLknn = FluidDataSet(s,\dsALLknn);
~dsALLknn = FluidDataSet(s);
~regK.fit(~dsIN,~dsOUT);
~regK.predict(~dsALLin,~dsALLknn);
@ -36,7 +36,7 @@
//Let's do the same process with MLP
~regM = FluidMLPRegressor(s,hidden: [4],activation: 1,outputActivation: 1,maxIter: 10000,learnRate: 0.1,momentum: 0,batchSize: 1,validation: 0);
~dsALLmlp = FluidDataSet(s,\dsALLmlp);
~dsALLmlp = FluidDataSet(s);
~regM.fit(~dsIN,~dsOUT,{|x|x.postln;});
~regM.predict(~dsALLin,~dsALLmlp);
@ -86,7 +86,7 @@
~dsALLknn.dump{|x| 41.do{|i|
~knnALLval.add((x["data"][i.asString]));
};//draw everything
[~source, ~knnALLval.flatten(1), ~mlpALLval.flatten(1)].flop.flatten(1).plot(\source,numChannels: 3, discrete: false, minval:0, maxval: 1).plotMode=\bars;
{[~source, ~knnALLval.flatten(1), ~mlpALLval.flatten(1)].flop.flatten(1).plot(\source,numChannels: 3, discrete: false, minval:0, maxval: 1).plotMode=\bars;}.defer;
};
});
});

@ -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
)

@ -7,6 +7,7 @@ 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
@ -14,14 +15,17 @@ Routine{
maxIters = 100; //max number of iterations acceptable
//makes a first iteration
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh).wait;
prevVal = c.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});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh).wait;
curVal = c.numFrames;
//makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached
iters = 2;
@ -42,7 +46,8 @@ Routine{
//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});
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh).wait;
curVal = c.numFrames;
}
);
//depending on the outcome, gives the right info back

@ -43,7 +43,7 @@ ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##LinkwitzRiley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
RETURNS::
An audio stream with square envelopes around the slices. The latency between the input and the output is dependant on the relation between the two envelope followers.
@ -54,7 +54,8 @@ code::
// detrending explained
// Our source here is a sinewave that does not go to silence and has sharp-ish amplitude bumps as onsets we try to track
(
{var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
{
var env, source = SinOsc.ar(320,0,LFSaw.ar(20, 0, -0.4, 0.6));
env = FluidAmpSlice.ar(source,fastRampUp: 5,fastRampDown: 50,slowRampUp: 220,slowRampDown: 220, onThreshold: 10, offThreshold: 10,floor: -60);
[source, env]
}.plot(0.08);

@ -16,80 +16,81 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
This is the method that calls for the slicing to be calculated on a given source buffer.
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
The server on which the buffers to be processed are allocated.
The server on which the buffers to be processed are allocated.
ARGUMENT:: source
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
The index of the buffer to use as the source material to be sliced through novelty identification. The different channels of multichannel buffers will be summed.
ARGUMENT:: startFrame
Where in the srcBuf should the slicing process start, in sample.
Where in the srcBuf should the slicing process start, in sample.
ARGUMENT:: numFrames
How many frames should be processed.
How many frames should be processed.
ARGUMENT:: startChan
For multichannel sources, which channel should be processed.
For multichannel sources, which channel should be processed.
ARGUMENT:: numChans
For multichannel sources, how many channel should be summed.
For multichannel sources, how many channel should be summed.
ARGUMENT:: indices
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
The index of the buffer where the indices (in sample) of the estimated starting points of slices will be written. The first and last points are always the boundary points of the analysis.
ARGUMENT:: rampUp
The number of samples the envelope follower will take to reach the next value when raising.
The number of samples the envelope follower will take to reach the next value when raising.
ARGUMENT:: rampDown
The number of samples the envelope follower will take to reach the next value when falling.
The number of samples the envelope follower will take to reach the next value when falling.
ARGUMENT:: onThreshold
The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state.
The threshold in dB of the envelope follower to trigger an onset, aka to go ON when in OFF state.
ARGUMENT:: offThreshold
The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state.
The threshold in dB of the envelope follower to trigger an offset, , aka to go ON when in OFF state.
ARGUMENT:: minSliceLength
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
The length in samples that the Slice will stay ON. Changes of states during that period will be ignored.
ARGUMENT:: minSilenceLength
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
The length in samples that the Slice will stay OFF. Changes of states during that period will be ignored.
ARGUMENT:: minLengthAbove
The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
The length in samples that the envelope have to be above the threshold to consider it a valid transition to ON. The Slice will start at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: minLengthBelow
The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
The length in samples that the envelope have to be below the threshold to consider it a valid transition to OFF. The Slice will end at the first sample when the condition is met. Therefore, this affects the latency.
ARGUMENT:: lookBack
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
The length of the buffer kept before an onset to allow the algorithm, once a new Slice is detected, to go back in time (up to that many samples) to find the minimum amplitude as the Slice onset point. This affects the latency of the algorithm.
ARGUMENT:: lookAhead
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
The length of the buffer kept after an offset to allow the algorithm, once the Slice is considered finished, to wait further in time (up to that many samples) to find a minimum amplitude as the Slice offset point. This affects the latency of the algorithm.
ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
The frequency of the fourth-order link::https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter##LinkwitzRiley high-pass filter::. This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
code::
// define a test signal and a destination buffer
(
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs}));
c = Buffer.new(s);
b = Buffer.sendCollection(s, Array.fill(44100,{|i| sin(i*pi/ (44100/640)) * (sin(i*pi/ 22050)).abs}));
c = Buffer.new(s);
)
b.play
b.plot
//basic tests: absThresh sanity
FluidBufAmpGate.process(s, b, indices:c, rampUp:5, rampDown:25, onThreshold:-12, offThreshold: -12)
c.query
@ -140,30 +141,33 @@ STRONG::A musical example.::
CODE::
//load a buffer
(
b = Buffer.read(s,File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
c = Buffer.new(s);
b = Buffer.read(s, File.realpath(FluidBufAmpGate.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
c = Buffer.new(s);
)
// slice the samples
FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40)
c.query
c.getn(0,c.numFrames*2,{|item|item.postln;})
//reformatting to read the onsets and offsets as pairs
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});})
(
Routine{
FluidBufAmpGate.process(s, b, indices:c, rampUp:110, rampDown:2205, onThreshold:-27, offThreshold: -31, minSilenceLength:1100, lookBack:441, highPassFreq:40).wait;
c.query;
c.getn(0,c.numFrames*2,{|item|item.postln;});
//reformatting to read the onsets and offsets as pairs
c.getn(0,c.numFrames*2,{|items|items.reshape(c.numFrames,2).do({|x| x.postln});});
}.play
)
//loops over a splice with the MouseX, taking the respective onset and offset of a given slice
(
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(2, c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0],
BufRd.kr(2, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1],
BufRd.kr(2,c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0]
), 0, 1);
}.play;
{
BufRd.ar(1, b,
Phasor.ar(0,1,
BufRd.kr(2, c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0],
BufRd.kr(2, c,
MouseX.kr(1, BufFrames.kr(c)), 0, 1)[1],
BufRd.kr(2,c,
MouseX.kr(0, BufFrames.kr(c)), 0, 1)[0]
), 0, 1);
}.play;
)
::
@ -178,15 +182,16 @@ b.play
// create a new buffer as destinations
c = Buffer.new(s);
OSCFunc.trace(false)
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30);
(Main.elapsedTime - t).postln;
}.play
// with basic params
Routine{
var t = Main.elapsedTime;
var proc= FluidBufAmpGate.process(s, b, indices: c, rampUp:1, rampDown:10, onThreshold: -30);
proc.wait;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed. The two channels of the output, respectively onset and offset indices, are interleaved as this is the SuperCollider buffer data formatting

@ -16,7 +16,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
@ -67,11 +67,13 @@ ARGUMENT:: minSliceLength
ARGUMENT:: highPassFreq
The frequency of the fourth-order LinkwitzRiley high-pass filter (https://en.wikipedia.org/wiki/Linkwitz%E2%80%93Riley_filter). This is done first on the signal to minimise low frequency intermodulation with very fast ramp lengths. A frequency of 0 bypasses the filter.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -143,14 +145,16 @@ b.play
c = Buffer.new(s);
//run the process on them
(
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5);
(Main.elapsedTime - t).postln;
}.play
)
(
// with basic params
Routine{
var t = Main.elapsedTime;
var proc = FluidBufAmpSlice.process(s,b, indices: c, fastRampUp: 10,fastRampDown: 2205,slowRampUp: 4410,slowRampDown: 4410, onThreshold: 10,offThreshold: 5);
proc.wait;
c.query;
(Main.elapsedTime - t).postln;
}.play
)
// list the indicies of detected attacks - the two input channels have been summed.
c.getn(0,c.numFrames,{|item|(item * 2).postln;})

@ -11,7 +11,7 @@ Henderson and Solomonm (2019) AUDIO TRANSPORT: A GENERALIZED PORTAMENTO VIA OPTI
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
Process two audio link::Classes/Buffer::
ARGUMENT:: server
@ -65,6 +65,9 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument.
@ -89,6 +92,7 @@ FluidBufAudioTransport.process(s,b,source2:c,destination:d,interpolation:0.5,act
// listen to the source and the result
b.play
c.play
d.updateInfo
d.play
// more interesting sources: two cardboard bowing gestures

@ -12,7 +12,7 @@ The algorithm takes a srcBuf, and writes the information at the provided dstBuf.
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This method triggers the compositing.
ARGUMENT:: server
@ -48,11 +48,13 @@ ARGUMENT:: destStartChan
ARGUMENT:: destGain
The gain applied to the samples in the region of the destination buffer over which the source is to be copied. The default value (0) will overwrite that section of the destination buffer, and a value of 1.0 would sum the source to the material that was present.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and destination instance variables have been updated on the client side. The function will be passed destination as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
DISCUSSION::
It is important to understand the rules used for determining the final desintinaiton buffer dimensions to get the most out of this object. If needs be, the destination buffer will be resized to the maxima of the requsted source numFrames and numChannels. Frames will be written up to the limit of actually available samples (meaning you can create zero padding); channels will be written modulo the available channels, taking into account the channel offsets, meaning you can have channels repeat or loop into the source buffer's channels. See the examples below.
@ -68,29 +70,42 @@ d = Buffer.new(s);
)
// with basic params (basic summing of each full buffer in all dimensions)
FluidBufCompose.process(s, source: b, destination: d);
FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0);
d.query;
d.play;
(
Routine{
FluidBufCompose.process(s, source: b, destination: d).wait;
FluidBufCompose.process(s, source: c, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play;
)
//constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d);
FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0);
d.query;
d.play;
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 9000, gain: 0.5, destination: d).wait;
FluidBufCompose.process(s, source: c, startFrame:30000, numFrames:44100, numChans:1, gain:0.9, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
//constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d);
FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0);
d.query;
d.play;
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, startFrame: 441000, numChans: 2, gain: 0.6, destination: d).wait;
FluidBufCompose.process(s, source: c, numFrames: 78000, startChan: 1, numChans: 2, gain: 0.5, destStartFrame: 22050, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
//constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d);
FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0);
d.query;
d.play;
(
Routine{
d.free; d = Buffer.new(s);
FluidBufCompose.process(s, source: b, numFrames: 44100, numChans: 1, destStartChan: 1, destination: d).wait;
FluidBufCompose.process(s, source: c, numFrames:44100, numChans:1, destination: d, destGain: 1.0).wait;
d.query;
d.play;
}.play
)
::

@ -47,6 +47,9 @@ The link::Classes/Buffer:: to write the flattened data to
ARGUMENT:: axis
Whether to group points channel-wise or frame-wise
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
Runs when processing is complete
@ -65,6 +68,8 @@ Whether to group points channel-wise or frame-wise
ARGUMENT:: trig
Trigger signal to defer / retrigger processing
returns:: an instance of the processor
EXAMPLES::
code::
@ -83,10 +88,10 @@ FluidBufPitch.process(s,b,numFrames:512 * 10,numChans:1,features:~pitchdata,acti
// Flatten and print the flat buffer. We expect to see larger numbers (20-2000) interleaved with smaller (0-1)
(
FluidBufFlatten.process(s,~pitchdata,~flatdata,action:{
FluidBufFlatten.process(s,~pitchdata,~flatdata,axis:1,action:{
~flatdata.loadToFloatArray(action:{ |a|
a.postln;
})
})
)
::
::

@ -24,7 +24,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the HPSS to be calculated on a given source buffer.
ARGUMENT:: server
@ -102,11 +102,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long; at least the size of the window; and a power of 2. Making it larger than the window size provides interpolation in frequency. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [harmonic, percussive, residual] as an argument.
RETURNS::
Nothing, as the various destination buffers are declared in the function call.
returns:: an instance of the processor
Discussion::
HPSS works by using median filters on the spectral magnitudes of a sound. It hinges on a simple modelling assumption that tonal components will tend to yield concentrations of energy across time, spread out in frequency, and percussive components will manifest as concentrations of energy across frequency, spread out in time. By using median filters across time and frequency respectively, we get initial esitmates of the tonal-ness / transient-ness of a point in time and frequency. These are then combined into 'masks' that are applied to the orginal spectral data in order to produce a separation.
@ -123,16 +125,17 @@ code::
d = Buffer.new(s);
e = Buffer.new(s);
)
OSCFunc.trace(true, true)
// run with basic parameters
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: c, percussive: d);
FluidBufHPSS.process(s, b, harmonic: c, percussive: d).wait;
(Main.elapsedTime - t).postln;
}.play
)
c.query
d.query
//play the harmonic
c.play;
//play the percussive
@ -143,11 +146,12 @@ d.play;
//more daring parameters, in mode 2
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512);
(Main.elapsedTime - t).postln;
}.play
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: c, percussive: d, residual:e, harmFilterSize:31, maskingMode:2, harmThreshFreq1: 0.005, harmThreshAmp1: 7.5, harmThreshFreq2: 0.168, harmThreshAmp2: 7.5, percThreshFreq1: 0.004, percThreshAmp1: 26.5, percThreshFreq2: 0.152, percThreshAmp2: 26.5,windowSize:4096,hopSize:512)
.wait;
(Main.elapsedTime - t).postln;
}.play
)
//play the harmonic
@ -171,9 +175,12 @@ c = Buffer.read(s,File.realpath(FluidBufHPSS.class.filenameSymbol).dirname.withT
)
// composite one on left one on right as test signals
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b)
b.play
(
Routine{
FluidBufCompose.process(s, c, numFrames:b.numFrames, startFrame:555000,destStartChan:1, destination:b).wait;
b.play
}.play
)
// create 2 new buffers as destinations
d = Buffer.new(s); e = Buffer.new(s);
@ -181,7 +188,7 @@ d = Buffer.new(s); e = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufHPSS.process(s, b, harmonic: d, percussive:e);
FluidBufHPSS.process(s, b, harmonic: d, percussive:e).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the loudness descriptor to be calculated on a given source buffer.
ARGUMENT:: server
@ -51,11 +51,13 @@ ARGUMENT:: windowSize
ARGUMENT:: hopSize
How much the buffered window moves forward, in samples. By default 512 to be similar with all other FluCoMa objects, the EBU specifies other values as per the examples below.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -73,7 +75,7 @@ b.play; b.plot;
(
Routine{
t = Main.elapsedTime;
FluidBufLoudness.process(s, source:b, features: c);
FluidBufLoudness.process(s, source:b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -109,7 +111,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410);
FluidBufLoudness.process(s, b, features: c, windowSize: 17640, hopSize:4410).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
@ -59,11 +59,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -78,7 +80,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufMFCC.process(s, b, features: c);
FluidBufMFCC.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -108,7 +110,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufMFCC.process(s, b, numCoeffs:5, features: c);
FluidBufMFCC.process(s, b, numCoeffs:5, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
@ -60,11 +60,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -79,7 +81,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufMelBands.process(s, b, features: c, numBands:10);
FluidBufMelBands.process(s, b, features: c, numBands:10).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -109,7 +111,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufMelBands.process(s, b, features: c, numBands:10);
FluidBufMelBands.process(s, b, features: c, numBands:10).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -35,7 +35,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the factorisation to be calculated on a given source buffer.
ARGUMENT:: server
@ -102,11 +102,13 @@ ARGUMENT:: windowType
ARGUMENT:: randomSeed
The NMF process needs to seed its starting point. If specified, the same values will be used. The default of -1 will randomly assign them. (not implemented yet)
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [resynth, bases, activations] as an argument.
RETURNS::
Nothing, as the various destination buffers are declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -142,7 +144,7 @@ d.play //////(beware !!!! loud!!!)
(
// separate them in 2 components
Routine {
FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2);
FluidBufNMF.process(s, d, resynth:e, bases: f, activations:g, components:2).wait;
e.query;
f.query;
g.query;
@ -179,16 +181,15 @@ y = Buffer.new(s);
// matrix factorisation, requesting everything - wait for the computation time to appear.
(
Routine{
t = Main.elapsedTime;
FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size);
(Main.elapsedTime - t).postln;
var t = Main.elapsedTime;
FluidBufNMF.process(s,b, 0,-1,0,-1,c,x,0,y,0,5,100,~frame_size,~hop_size,~fft_size)
.wait;
(Main.elapsedTime - t).postln;
}.play
)
//look at the resynthesised components, the bases and the activations
c.plot; x.plot; y.plot;
//null test of the sum of sources
{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play
// play the components spread in the stereo field
{Splay.ar(PlayBuf.ar(5,c,doneAction:2))}.play
@ -196,6 +197,9 @@ c.plot; x.plot; y.plot;
//play a single source
{PlayBuf.ar(5,c,doneAction:2)[~which_component].dup}.play
//null test of the sum of sources
{(PlayBuf.ar(5,c,doneAction:2).sum)+(-1*PlayBuf.ar(1,b,doneAction:2))}.play
//play noise using one of the bases as filter.
(
{
@ -251,7 +255,7 @@ b.play
// train using the first 2 seconds of the sound file
(
Routine {
FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10);
FluidBufNMF.process(s,b,0,44100*5,0,1, ~originalNMF, ~bases, components:10).wait;
~originalNMF.query;
}.play;
)
@ -296,7 +300,7 @@ z.do({|chan| FluidBufCompose.process(s, ~bases, startChan:chan, numChans: 1, des
//process the whole file, splitting it with the 2 trained bases
(
Routine{
FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2);
FluidBufNMF.process(s, b, resynth: ~sortedNMF, bases: ~trainedBases, basesMode: 2, components:2).wait;
~originalNMF.query;
}.play;
)
@ -354,7 +358,7 @@ e.query
(
// use the seeding basis, without updating
Routine {
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3);
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 2, activations:g, components:3).wait;
e.query;
f.query;
g.query;
@ -373,7 +377,7 @@ g.plot;
(
// use the seeding bases, with updating this time
Routine {
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3);
FluidBufNMF.process(s, d, resynth:f, bases: e, basesMode: 1, activations:g, components:3).wait;
e.query;
f.query;
g.query;

@ -52,9 +52,13 @@ The analysus hop size in samples (default winSize / 2)
ARGUMENT:: fftSize
The analsyis FFT size in samples (default = winSize)
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A function to run when processing is complete, taking the output buffer as its argument
returns:: an instance of the processor
INSTANCEMETHODS::
@ -69,4 +73,4 @@ t = Buffer.read(s,~path+/+"Tremblay-SA-UprightPianoPedalWide.wav")
o = Buffer.new
FluidBufNMFCross.process(s,t,b,o,action:{"Ding".postln})
o.play
::
::

@ -10,7 +10,7 @@ See http://nimfa.biolab.si/nimfa.methods.seeding.nndsvd.html
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the decomposition to be calculated on a given source buffer.
ARGUMENT:: server
@ -53,9 +53,15 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [destination] as an argument.
returns:: an instance of the processor
INSTANCEMETHODS::
private:: synth, server
@ -71,16 +77,24 @@ b = Buffer.read(s,File.realpath(FluidBufNNDSVD.class.filenameSymbol).dirname.wit
)
//how many bases do I need to decompose the buffer with 90% accuracy
FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1, action: {\done.postln;})
(
Routine{
FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.9, method: 1).wait;
"% bases".format(~bases.numChannels).postln;
}.play;
)
//check how many bases we are returned:
~bases.numChannels
//try the same process with less accuracy
FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5, action: {\done.postln;})
~bases.numChannels
(
Routine{
FluidBufNNDSVD.process(s, b, ~bases, ~activations, coverage: 0.5).wait;
"% bases".format(~bases.numChannels).postln;
}.play
)
//use the bases to run NMF on
FluidBufNMF.process(s, b, resynth: ~resynth, bases: ~bases, activations: ~activations,actMode: 2, components: ~bases.numChannels, action: {\done.postln;})
{PlayBuf.ar(~resynth.numChannels, ~resynth)[1]}.play
{PlayBuf.ar(~resynth.numChannels, ~resynth)[2]}.play
::

@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
@ -67,11 +67,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument.
RETURNS::
Nothing, as the various destination buffers are declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -87,7 +89,7 @@ c = Buffer.new(s);
// with basic params, with a minimum slight length to avoid over
Routine{
t = Main.elapsedTime;
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8);
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.4,filterSize: 4, minSliceLength: 8).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -120,11 +122,13 @@ c = Buffer.new(s);
)
// process with a given filterSize
FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1)
(
Routine{
FluidBufNoveltySlice.process(s,b, indices: c, kernelSize:31, threshold:0.1, filterSize:1).wait;
//check the number of slices: it is the number of frames in the transBuf minus the boundary index.
c.query;
c.query;
}.play;
)
//play slice number 3
(
{
@ -164,7 +168,7 @@ c = Buffer.new(s);
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3);
FluidBufNoveltySlice.process(s,b, indices: c, threshold:0.3).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -14,7 +14,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
@ -74,11 +74,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed indices as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -93,7 +95,7 @@ c = Buffer.new(s);
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5);
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.5).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -134,7 +136,7 @@ c = Buffer.new(s);
// with basic params
Routine{
t = Main.elapsedTime;
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001);
FluidBufOnsetSlice.process(s,b, indices: c, threshold:0.00001).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the pitch descriptor to be calculated on a given source buffer.
ARGUMENT:: server
@ -44,7 +44,7 @@ ARGUMENT:: algorithm
TABLE::
## 0 || Cepstrum: Returns a pitch estimate as the location of the second highest peak in the Cepstrum of the signal (after DC).
## 1 || Harmonic Product Spectrum: Implements the Harmonic Product Spectrum algorithm for pitch detection . See e.g. FOOTNOTE:: A. Lerch, "An Introduction to Audio Content Analysis: Applications in Signal Processing and Music Informatics." John Wiley & Sons, 2012.https://onlinelibrary.wiley.com/doi/book/10.1002/9781118393550 ::
## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications. QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html
## 2 || YinFFT: Implements the frequency domain version of the YIN algorithm, as described in FOOTNOTE::P. M. Brossier, "Automatic Annotation of Musical Audio for Interactive Applications." QMUL, London, UK, 2007. :: See also https://essentia.upf.edu/documentation/reference/streaming_PitchYinFFT.html
::
ARGUMENT:: minFreq
@ -65,11 +65,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -87,7 +89,7 @@ b.play; b.plot;
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c);
FluidBufPitch.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -122,7 +124,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1);
FluidBufPitch.process(s, b, features: c, minFreq:200, maxFreq:2000, unit:1).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -143,7 +145,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})});
FluidBufPitch.process(s, b, features: c,action:{c.loadToFloatArray(action: {|x| d = x.reshape((x.size()/2).asInteger, 2)})}).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -13,7 +13,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the scaling to be calculated on a given source buffer.
ARGUMENT:: server
@ -49,40 +49,61 @@ The output value when the input is inputLow
ARGUMENT:: outputHigh
The output value when the input is inputHigh
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The metric will be passed indices as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
code::
// make a buffer of know qualities
b = Buffer.sendCollection(s,1.0.series(1.1,2.0))
// and a destination buffer
c = Buffer(s)
// play with the scaling
FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10)
// retrieve the buffer and enjoy the results.
c.getn(0,10,{|x|x.postln;})
(
Routine{
// make a buffer of known qualities
b = Buffer.sendCollection(s,1.0.series(1.1,2.0));
// and a destination buffer
c = Buffer(s);
// play with the scaling
FluidBufScale.process(s, b, destination: c, inputLow: 0, inputHigh: 1, outputLow: 20, outputHigh:10).wait;
// retrieve the buffer and enjoy the results.
c.getn(0,10,{|x|x.postln;})
}.play
)
// also works in multichannel - explore the following buffer
b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2)
b.plot.plotMode_(\points)
//process
FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1)
//enjoy - same shape, different range
c.plot.plotMode_(\points)
(
Routine{
b = Buffer.sendCollection(s,-10.0.series(-9,10.0).scramble,2);
c = Buffer(s);
s.sync;
defer{b.plot(bounds:Rect(400,400,400,400)).plotMode_(\points).bounds};
FluidBufScale.process(s, b, destination: c, inputLow: -20, inputHigh: 20, outputLow: 0, outputHigh:1).wait;
//enjoy - same shape, different range
defer{c.plot(bounds:Rect(800,400,400,400)).plotMode_(\points)};
}.play;
)
//also works with a subset of the input, resizing the output
b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3)
b.plot(separately: true).plotMode_(\points)
//process
FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1)
//enjoy
c.plot(separately: true).plotMode_(\points)
c.query
c.getn(0,4,{|x|x.postln;})
(
Routine{
b = Buffer.sendCollection(s,0.0.series(0.1,3.0).reshape(3,10).flop.flat,3);
c = Buffer(s);
s.sync;
defer{b.plot(separately: true,bounds:Rect(400,400,400,400)).plotMode_(\points)};
//process
FluidBufScale.process(s, b, startFrame: 3,numFrames: 4,startChan: 1,numChans: 1, destination: c, inputLow: 0, inputHigh: 3, outputLow: 0, outputHigh:1).wait;
//enjoy
c.query;
c.getn(0,4,{|x|x.postln;});
defer{c.plot(separately: true,bounds:Rect(800,400,400,400)).plotMode_(\points)};
}.play
)
::

@ -19,7 +19,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the sinusoidal estimation to be calculated on a given source buffer and to be resynthesised.
ARGUMENT:: server
@ -82,11 +82,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the highest of windowSize and (bandwidth - 1) * 2.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [sines, residual] as an argument.
RETURNS::
Nothing, as the various destination buffers are declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -102,7 +104,7 @@ d = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufSines.process(s, b, sines: c, residual:d);
FluidBufSines.process(s, b, sines: c, residual:d).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -135,7 +137,7 @@ d = Buffer.new(s); e = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384);
FluidBufSines.process(s, b, sines: d, residual:e, windowSize: 2048, hopSize: 256, fftSize: 16384).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -31,7 +31,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the spectral shape descriptors to be calculated on a given source buffer.
ARGUMENT:: server
@ -64,11 +64,13 @@ ARGUMENT:: hopSize
ARGUMENT:: fftSize
The inner FFT/IFFT size. It should be at least 4 samples long, at least the size of the window, and a power of 2. Making it larger allows an oversampling of the spectral precision. The -1 default value will use the next power of 2 equal or above the windowSize.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and all Buffer's instance variables have been updated on the client side. The function will be passed [features] as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -83,7 +85,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufSpectralShape.process(s, b, features: c);
FluidBufSpectralShape.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -113,7 +115,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufSpectralShape.process(s, b, features: c);
FluidBufSpectralShape.process(s, b, features: c).wait;
(Main.elapsedTime - t).postln;
}.play
)

@ -15,7 +15,7 @@ By default, this UGen spawns a new thread to avoid blocking the server command q
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the slicing to be calculated on a given source buffer.
ARGUMENT:: server
@ -61,11 +61,13 @@ ARGUMENT:: weights
## weights must be positive (anything lower than 0 will be rejected)
::
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A Function to be evaluated once the offline process has finished and indices instance variables have been updated on the client side. The function will be passed stats as an argument.
RETURNS::
Nothing, as the destination buffer is declared in the function call.
returns:: an instance of the processor
EXAMPLES::
@ -73,7 +75,7 @@ STRONG::A didactic example::
CODE::
// make a buffer of known lenght
// make a buffer of known length
b = Buffer.alloc(s,101);
// add known values - here, a ramp up
@ -86,7 +88,7 @@ c = Buffer.new(s);
(
Routine{
t = Main.elapsedTime;
FluidBufStats.process(s, b, stats:c, numDerivs:1);
FluidBufStats.process(s, b, stats:c, numDerivs:1).wait;
(Main.elapsedTime - t).postln;
}.play
)
@ -144,11 +146,13 @@ g= Array.new;
Routine({
e.doAdjacentPairs({
arg start,end;
FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1, action: {d.loadToFloatArray(action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInteger,((end-start)/512).max(2).asInteger, array[12]);
})});
FluidBufStats.processBlocking(s,c,(start/512).asInteger,((end-start)/512).max(2).asInteger,0,1,d,1,
action: {d.loadToFloatArray(action: {
arg array;
g = g.add(array[12]);
"% % %\n".postf((start/512).asInteger,((end-start)/512 ).max(2).asInteger, array[12]);})
}
).wait;
});
"Done".postln;
}).play;
@ -182,10 +186,11 @@ c = Buffer.new(s);
// run the stats and send back the values
FluidBufStats.process(s, b, stats:c, numDerivs:1, action:{c.getn(0,c.numFrames * c.numChannels,{|item|d = item; d.postln})});
OSCFunc.allEnabled
//looking at the result is not easy to grasp, since it is interleaved: first number is mean of L, second is mean of R, third is stddev of L, fourth is stddev or R
//this will make it tidier - the first value of each line is Left, the second is Right
d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t\t", "kurtosis\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln;
d.reshape(14,2).do({|x,i|["mean\t\t","stddev\t\t","skew\t\t", "kurtosis\t\t", "min\t\t\t", "median\t\t", "max\t\t\t","d-mean\t","d-stddev\t","d-skew\t\t", "d-kurtosis", "d-min\t\t", "d-median\t", "d-max\t\t"].at(i).post;x.round(0.01).postln});"".postln;
::
STRONG::Outliers and Weights::

@ -12,7 +12,7 @@ The process will, after waiting for STRONG::time:: millisecond, return its delay
CLASSMETHODS::
METHOD:: process
METHOD:: process, processBlocking
This is the method that calls for the job to be done. In this case, simply waiting STRONG::time:: millisecond before writing a value in the destination buffer.
ARGUMENT:: server
@ -24,11 +24,13 @@ ARGUMENT:: result
ARGUMENT:: time
The duration in milliseconds of the delay that the background thread will wait for before yielding the value to the destination buffer.
ARGUMENT:: freeWhenDone
Free the server instance when processing complete. Default true
ARGUMENT:: action
A function that will be executed upon completion. It is passed the destination buffer as argument.
RETURNS::
The instance of FluidNRTProcess which can be used to cancel the job.
returns::The instance of FluidNRTProcess which can be used to cancel the job.
METHOD:: kr
@ -43,8 +45,7 @@ ARGUMENT:: time
ARGUMENT:: doneAction
An integer representing an action to be executed when the process is finished. This can be used to free the enclosing synth, etc. See link::Classes/Done:: for more detail.
RETURNS::
It report the approximate job progress, from 0 to 1.
returns::It report the approximate job progress, from 0 to 1.
EXAMPLES::
@ -56,11 +57,11 @@ CODE::
b=Buffer.alloc(s,1);
// a simple call, where we query the destination buffer upon completion with the action message.
FluidBufThreadDemo.process(s, b, 1000, {|x|x.get(0,{|y|y.postln});});
FluidBufThreadDemo.process(s, b, 1000, action:{|x|x.get(0,{|y|y.postln});});
// as the 'process' returns its parent UGen, we can cancel the process easily
c = FluidBufThreadDemo.process(s, b, 100000, {|x|x.get(0,{|y|y.postln});});
c.cancel //it stops silently for now but check the synth count going down by 1.
// as the 'process' returns its instance, we can cancel the process easily
c = FluidBufThreadDemo.process(s, b, 100000, action: {|x|x.get(0,{|y|y.postln});});
c.cancel;
// if a simple call to the UGen is used, the progress can be monitored. The usual cmd. will cancel the job by freeing the synth.
{c = FluidBufThreadDemo.kr(b,10000, Done.freeSelf); Poll.kr(Impulse.kr(2),c);}.scope;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save