/* Part of the Fluid Corpus Manipulation Project (http://www.flucoma.org/) Copyright 2017-2019 University of Huddersfield. Licensed under the BSD-3 License. See license.md file in the project root for full license information. This project has received funding from the European Research Council (ERC) under the European Union’s Horizon 2020 research and innovation programme (grant agreement No 725899). */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace fluid { namespace client { /* 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 SCBufferAdaptor; std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b); class SCBufferAdaptor :public client::BufferAdaptor { public: SCBufferAdaptor(const SCBufferAdaptor&) = delete; SCBufferAdaptor& operator=(const SCBufferAdaptor&) = delete; SCBufferAdaptor(SCBufferAdaptor&&) = default; SCBufferAdaptor(SndBuf* buf, World* world, bool local) : mBuffer{buf}, mWorld{world}, mLocal{local} {} SCBufferAdaptor(index bufnum, World* world) : mBuffer{World_GetNRTBuf(world, static_cast(bufnum))}, mBufnum(bufnum), mWorld(world) { if (mBuffer && !static_cast(mBuffer->samplerate)) mBuffer->samplerate = world->mFullRate.mSampleRate; } // ~SCBufferAdaptor() { cleanUp(); } void assignToRT(World* rtWorld) { if(mLocal) return; SndBuf* rtBuf = World_GetBuf(rtWorld, static_cast(mBufnum)); *rtBuf = *mBuffer; rtWorld->mSndBufUpdates[mBufnum].writes++; } void cleanUp() { boost::alignment::aligned_free(mOldData); mOldData = nullptr; } // No locks in (vanilla) SC, so no-ops for these bool acquire() const override { return true; } void release() const override {} // Validity is reports whether underlying SndBuf is null / 0-size bool valid() const override { return mBuffer && mBuffer->data && mBuffer->channels && mBuffer->frames; } // Existence is based on whether this buffer is within the range the server // knows about bool exists() const override { return (mLocal ? true : mBufnum >= 0 && mBufnum < asSigned(mWorld->mNumSndBufs)); } FluidTensorView allFrames() override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v.transpose(); } FluidTensorView allFrames() const override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v.transpose(); } FluidTensorView samps(index channel) override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v.col(channel); } FluidTensorView samps(index offset, index nframes, index chanoffset) override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0); } FluidTensorView samps(index channel) const override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v.col(channel); } FluidTensorView samps(index offset, index nframes, index chanoffset) const override { FluidTensorView v{mBuffer->data, 0, mBuffer->frames, mBuffer->channels}; return v(fluid::Slice(offset, nframes), fluid::Slice(chanoffset, 1)).col(0); } index numFrames() const override { return valid() ? this->mBuffer->frames : 0; } index numChans() const override { return valid() ? this->mBuffer->channels : 0; } double sampleRate() const override { return mBuffer ? mBuffer->samplerate : 0; } std::string asString() const override { return std::to_string(bufnum()); } const Result resize(index frames, index channels, double sampleRate) override { if(mLocal) // don't try and resize { if(frames > mBuffer->frames || channels > mBuffer->channels) { return {Result::Status::kError, "Local buffer must be presized adequetly, need ", frames, " frames, ", channels, " channels." }; } else return {}; } mOldData = mBuffer->data; int allocResult = mWorld->ft->fBufAlloc(mBuffer, static_cast(channels), static_cast(frames), sampleRate); if(mBuffer->data == mOldData) mOldData = nullptr; Result r; if (allocResult != kSCErr_None) { r.set(Result::Status::kError); r.addMessage("Resize on buffer ", bufnum(), " failed. (Requested ", frames, " frames, ", channels, "channels)"); } return r; } index bufnum() const { return mBufnum; } void realTime(bool rt) { mRealTime = rt; } protected: SndBuf* mBuffer; bool mRealTime{false}; float* mOldData{nullptr}; index mBufnum; World* mWorld; bool mLocal{false}; }; std::ostream& operator<<(std::ostream& os, SCBufferAdaptor& b) { return os << b.bufnum(); } } // namespace client } // namespace fluid