#pragma once #include "data/FluidTensor.hpp" #include "clients/common/FluidParams.hpp" #include "SC_PlugIn.h" #include #include //static InterfaceTable *ft; namespace fluid { namespace sc{ /** A descendent of SndBuf that will populate itself from the NRT mirror buffers given a world and a bufnum **/ struct NRTBuf: public SndBuf { NRTBuf(SndBuf& b):SndBuf(b){} NRTBuf(World* world,long bufnum): NRTBuf(*World_GetNRTBuf(world,bufnum)) { this->samplerate = world->mFullRate.mSampleRate; } }; /** A combination of SndBuf and parameter::BufferAdaptor (which, in turn, exposes FluidTensorView), 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. The SndBuf can then be 'transferred' back to the RT buffers once we're done with it, and SC notified of the update. (In the context of SequencedCommands, in which this is meant to be used, this would happen at Stage3() on the real-time thread) nSamps = rows nChans = columns **/ class SCBufferView: public NRTBuf, public parameter::BufferAdaptor { public: SCBufferView() = delete; SCBufferView(SCBufferView&) = delete; SCBufferView operator=(SCBufferView&) = delete; SCBufferView(long bufnum,World* world): NRTBuf(world,bufnum), BufferAdaptor({0,{static_cast(frames),static_cast(channels)}},NRTBuf::data), mBufnum(bufnum), mWorld(world) {} ~SCBufferView() = default; void assignToRT(World* rtWorld) { SndBuf* rtBuf = World_GetBuf(rtWorld,mBufnum); *rtBuf = static_cast(*this); rtWorld->mSndBufUpdates[mBufnum].writes++; } //No locks in (vanilla) SC, so no-ops for these void acquire() override {} void release() override {} //Validity is based on whether this buffer is within the range the server knows about bool valid() const override { return (mBufnum >=0 && mBufnum < mWorld->mNumSndBufs); } void resize(size_t frames, size_t channels, size_t rank) override { SndBuf* thisThing = static_cast(this); float* oldData = thisThing->data; mWorld->ft->fBufAlloc(this, channels * rank, frames, this->samplerate); FluidTensorView v= FluidTensorView(NRTBuf::data,0,static_cast(frames),static_cast(channels * rank)); static_cast&>(*this) = std::move(v); if(oldData) boost::alignment::aligned_free(oldData); } protected: bool equal(BufferAdaptor* rhs) const override { SCBufferView* x = dynamic_cast(rhs); if(x) { return mBufnum == x->mBufnum; } return false; } long mBufnum; World* mWorld; }; class NRTCommandBase{ using param_type = fluid::parameter::Instance; public: NRTCommandBase() = delete; NRTCommandBase(NRTCommandBase&) = delete; NRTCommandBase& operator=(NRTCommandBase&) = delete; NRTCommandBase(std::vector params, void* inUserData): // mWorld(inWorld),mReplyAddr(replyAddr), mCompletionMsgData(completionMsgData), mCompletionMsgSize(completionMsgSize), mParams(params) {} ~NRTCommandBase() = default; template using AsyncFn = bool (T::*)(World* w); template using AsyncCleanup = void (T::*)(); template F> static bool call(World* w,void* x){return (static_cast(x)->*F)(w);} template static void call(World*, void* x){delete static_cast(x);} template Stage2, AsyncFn Stage3, AsyncFn Stage4> void cmd(World* world, std::string name, void* replyAddr, char* completionMsgData, size_t completionMsgSize) { (world->ft->fDoAsynchronousCommand)(world, replyAddr,name.c_str(),this, call, call, call,call, completionMsgSize,completionMsgData); } protected: // World * mWorld; // InterfaceTable *ft; long bufNUm; void* mReplyAddr; const char* cmdName; void *cmdData; char* mCompletionMsgData; size_t mCompletionMsgSize; std::vector mParams; }; //This wraps a class instance in a function call to pass to SC template void command(World *inWorld, void* inUserData, struct sc_msg_iter *args, void *replyAddr) { //Iterate over parameter descriptions associated with this client object, fill with data from language side std::vector params = NRT_Plug::client_type::newParameterSet(); for (auto&& p: params) { switch(p.getDescriptor().getType()) { case parameter::Type::Buffer: { long bufNum = static_cast(args->geti()); if(bufNum >= 0){ SCBufferView* buf = new SCBufferView(bufNum,inWorld); p.setBuffer(buf); } break; } case parameter::Type::Long: { p.setLong(static_cast(args->geti())); break; } case parameter::Type::Float: { p.setFloat(args->getf()); break; } default: { p.setLong(static_cast(args->geti())); } } } //Deal with the completion message at the end, if any size_t completionMsgSize = args->getbsize(); char* completionMsgData = 0; if(completionMsgSize) { //allocate string completionMsgData = (char*)inWorld->ft->fRTAlloc(inWorld,completionMsgSize); args->getb(completionMsgData,completionMsgSize); } //Make a new pointer for our plugin, and set it going NRT_Plug* cmd = new NRT_Plug(params, inUserData); cmd->runCommand(inWorld, replyAddr, completionMsgData, completionMsgSize); } } //namespace sc }//namespace fluid template void registerCommand(InterfaceTable* ft, const char* name) { PlugInCmdFunc cmd = fluid::sc::command; (*ft->fDefinePlugInCmd)(name,cmd,nullptr); }