From 07668c7bd62c2da1bc7dc4591bcdff7ef3f68eda Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sat, 28 Mar 2020 17:00:07 +0100 Subject: [PATCH] VST hard realtime fix and improvement --- vst/SfizzVstEditor.cpp | 2 +- vst/SfizzVstProcessor.cpp | 177 +++++++++++++++++++------------------- vst/SfizzVstProcessor.h | 31 ++++++- vst/SfizzVstState.h | 2 + 4 files changed, 121 insertions(+), 91 deletions(-) diff --git a/vst/SfizzVstEditor.cpp b/vst/SfizzVstEditor.cpp index 4205c4f6..8448b218 100644 --- a/vst/SfizzVstEditor.cpp +++ b/vst/SfizzVstEditor.cpp @@ -199,7 +199,7 @@ void SfizzVstEditor::loadSfzFile(const std::string& filePath) msg->setMessageID("LoadSfz"); Vst::IAttributeList* attr = msg->getAttributes(); - attr->setString("File", Steinberg::String(filePath.c_str()).text()); + attr->setBinary("File", filePath.data(), filePath.size()); ctl->sendMessage(msg); msg->release(); diff --git a/vst/SfizzVstProcessor.cpp b/vst/SfizzVstProcessor.cpp index daf141d1..46fab205 100644 --- a/vst/SfizzVstProcessor.cpp +++ b/vst/SfizzVstProcessor.cpp @@ -21,7 +21,7 @@ constexpr int fastRound(T x) } SfizzVstProcessor::SfizzVstProcessor() - : _fifoToWorker(1024) + : _fifoToWorker(64 * 1024) { setControllerClass(SfizzVstController::cid); } @@ -119,14 +119,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state) _fileChangePeriod = static_cast(processSetup.sampleRate); - if (!_msgCheckShouldReload) { - auto msg = IPtr::adopt(allocateMessage()); - if (!msg) - return kResultFalse; - msg->setMessageID("CheckShouldReload"); - _msgCheckShouldReload = msg; - } - _workRunning = true; _worker = std::thread([this]() { doBackgroundWork(); }); } else { @@ -183,11 +175,8 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) _fileChangeCounter += numFrames; if (_fileChangeCounter > _fileChangePeriod) { _fileChangeCounter %= _fileChangePeriod; - Vst::IMessage* msg = _msgCheckShouldReload.get(); - if (_fifoToWorker.push(msg)) { - msg->addRef(); + if (writeWorkerMessage("CheckShouldReload", nullptr, 0)) _semaToWorker.post(); - } } return kResultTrue; @@ -214,47 +203,26 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) break; case kPidNumVoices: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - Vst::IMessage* msg = allocateMessage(); - if (!msg) - break; - msg->setMessageID("SetNumVoices"); - Vst::IAttributeList* attr = msg->getAttributes(); - attr->setInt("NumVoices", static_cast(kParamNumVoicesRange.denormalize(value))); - if (!_fifoToWorker.push(msg)) { - msg->release(); - break; - } - _semaToWorker.post(); + int32 data = static_cast(kParamNumVoicesRange.denormalize(value)); + _state.numVoices = data; + if (writeWorkerMessage("SetNumVoices", &data, sizeof(data))) + _semaToWorker.post(); } break; case kPidOversampling: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - Vst::IMessage* msg = allocateMessage(); - if (!msg) - break; - msg->setMessageID("SetOversampling"); - Vst::IAttributeList* attr = msg->getAttributes(); - attr->setInt("Oversampling", static_cast(kParamOversamplingRange.denormalize(value))); - if (!_fifoToWorker.push(msg)) { - msg->release(); - break; - } - _semaToWorker.post(); + int32 data = static_cast(kParamOversamplingRange.denormalize(value)); + _state.oversamplingLog2 = data; + if (writeWorkerMessage("SetOversampling", &data, sizeof(data))) + _semaToWorker.post(); } break; case kPidPreloadSize: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - Vst::IMessage* msg = allocateMessage(); - if (!msg) - break; - msg->setMessageID("SetPreloadSize"); - Vst::IAttributeList* attr = msg->getAttributes(); - attr->setInt("PreloadSize", static_cast(kParamPreloadSizeRange.denormalize(value))); - if (!_fifoToWorker.push(msg)) { - msg->release(); - break; - } - _semaToWorker.post(); + int32 data = static_cast(kParamPreloadSizeRange.denormalize(value)); + _state.preloadSize = data; + if (writeWorkerMessage("SetPreloadSize", &data, sizeof(data))) + _semaToWorker.post(); } break; } @@ -335,17 +303,29 @@ int SfizzVstProcessor::convertVelocityFromFloat(float x) tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) { + // Note(jpc) this notification is not necessarily handled by the RT thread + tresult result = AudioEffect::notify(message); if (result != kResultFalse) return result; - if (!_fifoToWorker.push(message)) - return kOutOfMemory; + const char* id = message->getMessageID(); + Vst::IAttributeList* attr = message->getAttributes(); - message->addRef(); - _semaToWorker.post(); + if (!std::strcmp(id, "LoadSfz")) { + const void* data = nullptr; + uint32 size = 0; + result = attr->getBinary("File", data, size); - return kResultTrue; + if (result != kResultTrue) + return result; + + std::lock_guard lock(_processMutex); + _state.sfzFile.assign(static_cast(data), size); + _synth->loadSfzFile(_state.sfzFile); + } + + return result; } FUnknown* SfizzVstProcessor::createInstance(void*) @@ -355,61 +335,38 @@ FUnknown* SfizzVstProcessor::createInstance(void*) void SfizzVstProcessor::doBackgroundWork() { - constexpr uint32 maxPathLen = 32768; - for (;;) { _semaToWorker.wait(); if (!_workRunning) break; - Vst::IMessage* msg; - if (!_fifoToWorker.pop(msg)) { + RTMessagePtr msg = readWorkerMessage(); + if (!msg) { fprintf(stderr, "[Sfizz] message synchronization error in worker\n"); std::abort(); } - const char* id = msg->getMessageID(); - Vst::IAttributeList* attr = msg->getAttributes(); + const char* id = msg->type; - if (!std::strcmp(id, "LoadSfz")) { - std::vector path(maxPathLen + 1); - if (attr->getString("File", path.data(), maxPathLen) == kResultTrue) { - std::lock_guard lock(_processMutex); - _state.sfzFile = Steinberg::String(path.data()).text8(); - _synth->loadSfzFile(_state.sfzFile); - } - } - else if (!std::strcmp(id, "SetNumVoices")) { - int64 value; - if (attr->getInt("NumVoices", value) == kResultTrue) { - _state.numVoices = value; - _synth->setNumVoices(value); - } + if (!std::strcmp(id, "SetNumVoices")) { + int32 value = *msg->payload(); + _synth->setNumVoices(value); } else if (!std::strcmp(id, "SetOversampling")) { - int64 value; - if (attr->getInt("Oversampling", value) == kResultTrue) { - _state.oversamplingLog2 = value; - _synth->setOversamplingFactor(1 << value); - } + int32 value = *msg->payload(); + _synth->setOversamplingFactor(1 << value); } else if (!std::strcmp(id, "SetPreloadSize")) { - int64 value; - if (attr->getInt("PreloadSize", value) == kResultTrue) { - _state.preloadSize = value; - _synth->setPreloadSize(value); - } + int32 value = *msg->payload(); + _synth->setPreloadSize(value); } else if (!std::strcmp(id, "CheckShouldReload")) { if (_synth->shouldReloadFile()) { fprintf(stderr, "[Sfizz] file has changed, reloading\n"); - std::lock_guard lock(_processMutex); _synth->loadSfzFile(_state.sfzFile); } } - - msg->release(); } } @@ -423,15 +380,61 @@ void SfizzVstProcessor::stopBackgroundWork() _worker.join(); while (_semaToWorker.try_wait()) { - Vst::IMessage* msg; - if (!_fifoToWorker.pop(msg)) { + if (!discardWorkerMessage()) { fprintf(stderr, "[Sfizz] message synchronization error in processor\n"); std::abort(); } - msg->release(); } } +bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size) +{ + RTMessage header; + header.type = type; + header.size = size; + + if (_fifoToWorker.size_free() < sizeof(header) + size) + return false; + + _fifoToWorker.put(header); + _fifoToWorker.put(static_cast(data), size); + return true; +} + +SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage() +{ + RTMessage header; + + if (!_fifoToWorker.peek(header)) + return nullptr; + if (_fifoToWorker.size_used() < sizeof(header) + header.size) + return nullptr; + + RTMessagePtr msg { reinterpret_cast(std::malloc(sizeof(header) + header.size)) }; + if (!msg) + throw std::bad_alloc(); + + msg->type = header.type; + msg->size = header.size; + _fifoToWorker.discard(sizeof(header)); + _fifoToWorker.get(const_cast(msg->payload()), header.size); + + return msg; +} + +bool SfizzVstProcessor::discardWorkerMessage() +{ + RTMessage header; + + if (!_fifoToWorker.peek(header)) + return false; + if (_fifoToWorker.size_used() < sizeof(header) + header.size) + return false; + + _fifoToWorker.discard(sizeof(header) + header.size); + return true; +} + /* Note(jpc) Generated at random with uuidgen. Can't find docs on it... maybe it's to register somewhere? diff --git a/vst/SfizzVstProcessor.h b/vst/SfizzVstProcessor.h index a8ed43e1..1ac71827 100644 --- a/vst/SfizzVstProcessor.h +++ b/vst/SfizzVstProcessor.h @@ -7,12 +7,13 @@ #pragma once #include "SfizzVstState.h" #include "RTSemaphore.h" +#include "ring_buffer/ring_buffer.h" #include "public.sdk/source/vst/vstaudioeffect.h" -#include "public.sdk/source/vst/utility/ringbuffer.h" #include #include #include #include +#include using namespace Steinberg; @@ -51,16 +52,40 @@ private: // worker and thread sync std::thread _worker; volatile bool _workRunning = false; - Steinberg::OneReaderOneWriter::RingBuffer _fifoToWorker; + Ring_Buffer _fifoToWorker; RTSemaphore _semaToWorker; std::mutex _processMutex; // file modification periodic checker uint32 _fileChangeCounter = 0; uint32 _fileChangePeriod = 0; - IPtr _msgCheckShouldReload; + + // messaging + struct RTMessage { + const char* type; + uintptr_t size; + // 32-bit aligned data after header + template const T* payload() const; + }; + struct RTMessageDelete { + void operator()(RTMessage* x) const noexcept { std::free(x); } + }; + typedef std::unique_ptr RTMessagePtr; // worker void doBackgroundWork(); void stopBackgroundWork(); + // writer + bool writeWorkerMessage(const char* type, const void* data, uintptr_t size); + // reader + RTMessagePtr readWorkerMessage(); + bool discardWorkerMessage(); }; + +//------------------------------------------------------------------------------ + +template const T* SfizzVstProcessor::RTMessage::payload() const +{ + return reinterpret_cast( + reinterpret_cast(this) + sizeof(*this)); +} diff --git a/vst/SfizzVstState.h b/vst/SfizzVstState.h index 5f3a0fb0..eed212e8 100644 --- a/vst/SfizzVstState.h +++ b/vst/SfizzVstState.h @@ -31,6 +31,8 @@ enum { class SfizzVstState { public: + SfizzVstState() { sfzFile.reserve(8192); } + std::string sfzFile; float volume = 0; int32 numVoices = 64;