diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index dc250162..eb658137 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -21,8 +21,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) Steinberg::UpdateHandler::instance(); // create update objects - oscUpdate_ = Steinberg::owned(new OSCUpdate); - noteUpdate_ = Steinberg::owned(new NoteUpdate); + queuedUpdates_ = Steinberg::owned(new QueuedUpdates); sfzUpdate_ = Steinberg::owned(new SfzUpdate); sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate); scalaUpdate_ = Steinberg::owned(new ScalaUpdate); @@ -289,10 +288,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - // this is a synchronous send, because the update object gets reused - oscUpdate_->setMessage(data, size, false); - oscUpdate_->changed(); - oscUpdate_->clear(); + IPtr update = Steinberg::owned( + new OSCUpdate(reinterpret_cast(data), size)); + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); } else if (!strcmp(id, "NoteEvents")) { const void* data = nullptr; @@ -303,10 +302,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) const std::pair*>(data); uint32 numEvents = size / sizeof(events[0]); - // this is a synchronous send, because the update object gets reused - noteUpdate_->setEvents(events, numEvents, false); - noteUpdate_->changed(); - noteUpdate_->clear(); + IPtr update = Steinberg::owned( + new NoteUpdate(events, numEvents)); + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); } else if (!strcmp(id, "Automate")) { const void* data = nullptr; @@ -343,20 +342,17 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) if (name != Vst::ViewType::kEditor) return nullptr; - std::vector continuousUpdates; - continuousUpdates.push_back(sfzUpdate_); - continuousUpdates.push_back(sfzDescriptionUpdate_); - continuousUpdates.push_back(scalaUpdate_); - continuousUpdates.push_back(playStateUpdate_); + std::vector updates; + updates.push_back(queuedUpdates_); + updates.push_back(sfzUpdate_); + updates.push_back(sfzDescriptionUpdate_); + updates.push_back(scalaUpdate_); + updates.push_back(playStateUpdate_); for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) - continuousUpdates.push_back(parameters.getParameterByIndex(i)); - - std::vector triggerUpdates; - triggerUpdates.push_back(oscUpdate_); - triggerUpdates.push_back(noteUpdate_); + updates.push_back(parameters.getParameterByIndex(i)); IPtr editor = Steinberg::owned( - new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates))); + new SfizzVstEditor(this, absl::MakeSpan(updates))); editor->remember(); return editor; diff --git a/plugins/vst/SfizzVstController.h b/plugins/vst/SfizzVstController.h index b8d11cd8..db52179f 100644 --- a/plugins/vst/SfizzVstController.h +++ b/plugins/vst/SfizzVstController.h @@ -44,8 +44,7 @@ public: REFCOUNT_METHODS(Vst::EditController) protected: - Steinberg::IPtr oscUpdate_; - Steinberg::IPtr noteUpdate_; + Steinberg::IPtr queuedUpdates_; Steinberg::IPtr sfzUpdate_; Steinberg::IPtr sfzDescriptionUpdate_; Steinberg::IPtr scalaUpdate_; diff --git a/plugins/vst/SfizzVstEditor.cpp b/plugins/vst/SfizzVstEditor.cpp index 4c9f2565..9f9e614c 100644 --- a/plugins/vst/SfizzVstEditor.cpp +++ b/plugins/vst/SfizzVstEditor.cpp @@ -30,14 +30,10 @@ enum { kNoteEventQueueSize = 8192, }; -SfizzVstEditor::SfizzVstEditor( - SfizzVstController* controller, - absl::Span continuousUpdates, - absl::Span triggerUpdates) +SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span updates) : VSTGUIEditor(controller, &sfizzUiViewRect), oscTemp_(new uint8_t[kOscTempSize]), - continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()), - triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end()) + updates_(updates.begin(), updates.end()) { } @@ -69,19 +65,6 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor_.reset(editor); } - { - std::lock_guard lock(oscQueueMutex_); - OscByteVec* queue = new OscByteVec; - oscQueue_.reset(queue); - queue->reserve(kOscQueueSize); - } - { - std::lock_guard lock(noteEventQueueMutex_); - NoteEventsVec* queue = new NoteEventsVec; - noteEventQueue_.reset(queue); - queue->reserve(kNoteEventQueueSize); - } - if (!frame->open(parent, platformType, config)) { fprintf(stderr, "[sfizz] error opening frame\n"); return false; @@ -89,9 +72,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor->open(*frame); - for (FObject* update : continuousUpdates_) - update->addDependent(this); - for (FObject* update : triggerUpdates_) + for (FObject* update : updates_) update->addDependent(this); threadChecker_ = Vst::ThreadChecker::create(); @@ -100,7 +81,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p Steinberg::IdleUpdateHandler::start(); - for (FObject* update : continuousUpdates_) + for (FObject* update : updates_) update->deferUpdate(); // let the editor know about plugin format @@ -138,9 +119,7 @@ void PLUGIN_API SfizzVstEditor::close() if (frame) { Steinberg::IdleUpdateHandler::stop(); - for (FObject* update : continuousUpdates_) - update->removeDependent(this); - for (FObject* update : triggerUpdates_) + for (FObject* update : updates_) update->removeDependent(this); if (editor_) @@ -152,15 +131,6 @@ void PLUGIN_API SfizzVstEditor::close() this->frame = nullptr; } - { - std::lock_guard lock(oscQueueMutex_); - oscQueue_.reset(); - } - { - std::lock_guard lock(noteEventQueueMutex_); - noteEventQueue_.reset(); - } - updateEditorIsOpenParameter(); } @@ -195,8 +165,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) #endif if (message == CVSTGUITimer::kMsgTimer) { - processOscQueue(); - processNoteEventQueue(); processParameterUpdates(); updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time } @@ -206,34 +174,51 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) { - if (OSCUpdate* update = FCast(changedUnknown)) { - // this update is synchronous: may happen from non-UI thread - uint32 size = update->size(); - if (size > 0) { - const uint8_t* bytes = reinterpret_cast(update->data()); - std::lock_guard lock(oscQueueMutex_); - if (OscByteVec* queue = oscQueue_.get()) - std::copy(bytes, bytes + size, std::back_inserter(*queue)); - } + if (processUpdate(changedUnknown, message)) return; + + Vst::VSTGUIEditor::update(changedUnknown, message); +} + +bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message) +{ + if (QueuedUpdates* update = FCast(changedUnknown)) { + for (FObject* queuedUpdate : update->getUpdates(this)) + processUpdate(queuedUpdate, message); + return true; + } + + if (OSCUpdate* update = FCast(changedUnknown)) { + const uint8* oscData = update->data(); + uint32 oscSize = update->size(); + + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + + uint32_t msgSize; + while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + uiReceiveMessage(path, sig, args); + oscData += msgSize; + oscSize -= msgSize; + } + + return true; } if (NoteUpdate* update = FCast(changedUnknown)) { - // this update is synchronous: may happen from non-UI thread + const NoteUpdate::Item* events = update->events(); uint32 count = update->count(); - if (count > 0) { - const auto* events = update->events(); - std::lock_guard lock(noteEventQueueMutex_); - if (NoteEventsVec* queue = noteEventQueue_.get()) - std::copy(events, events + count, std::back_inserter(*queue)); - } - return; + for (uint32 i = 0; i < count; ++i) + uiReceiveValue(editIdForKey(events[i].first), events[i].second); + return true; } if (SfzUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); uiReceiveValue(EditId::SfzFile, path); - return; + return true; } if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { @@ -268,19 +253,19 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]); } } - return; + return true; } if (ScalaUpdate* update = FCast(changedUnknown)) { const std::string path = update->getPath(); uiReceiveValue(EditId::ScalaFile, path); - return; + return true; } if (PlayStateUpdate* update = FCast(changedUnknown)) { const SfizzPlayState playState = update->getState(); uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); - return; + return true; } if (Vst::RangeParameter* param = Steinberg::FCast(changedUnknown)) { @@ -297,50 +282,10 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) std::lock_guard lock(parametersToUpdateMutex_); parametersToUpdate_.insert(id); } - return; + return true; } - Vst::VSTGUIEditor::update(changedUnknown, message); -} - -void SfizzVstEditor::processOscQueue() -{ - std::lock_guard lock(oscQueueMutex_); - - OscByteVec* queue = oscQueue_.get(); - if (!queue) - return; - - const uint8_t* oscData = queue->data(); - size_t oscSize = queue->size(); - - const char* path; - const char* sig; - const sfizz_arg_t* args; - uint8_t buffer[1024]; - - uint32_t msgSize; - while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { - uiReceiveMessage(path, sig, args); - oscData += msgSize; - oscSize -= msgSize; - } - - queue->clear(); -} - -void SfizzVstEditor::processNoteEventQueue() -{ - std::lock_guard lock(noteEventQueueMutex_); - - NoteEventsVec* queue = noteEventQueue_.get(); - if (!queue) - return; - - for (std::pair event : *queue) - uiReceiveValue(editIdForKey(event.first), event.second); - - queue->clear(); + return false; } void SfizzVstEditor::processParameterUpdates() diff --git a/plugins/vst/SfizzVstEditor.h b/plugins/vst/SfizzVstEditor.h index 7d3b48c8..9ced7659 100644 --- a/plugins/vst/SfizzVstEditor.h +++ b/plugins/vst/SfizzVstEditor.h @@ -25,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor, public: using Self = SfizzVstEditor; - SfizzVstEditor( - SfizzVstController* controller, - absl::Span continuousUpdates, - absl::Span triggerUpdates); + SfizzVstEditor(SfizzVstController* controller, absl::Span updates); ~SfizzVstEditor(); bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override; @@ -48,8 +45,7 @@ public: // private: - void processOscQueue(); - void processNoteEventQueue(); + bool processUpdate(FUnknown* changedUnknown, int32 message); void processParameterUpdates(); void updateParameter(Vst::Parameter* parameterToUpdate); @@ -76,18 +72,8 @@ private: // messaging std::unique_ptr oscTemp_; - // editor state - // note: might be updated from a non-UI thread - typedef std::vector OscByteVec; - std::unique_ptr oscQueue_; - std::mutex oscQueueMutex_; - typedef std::vector> NoteEventsVec; - std::unique_ptr noteEventQueue_; - std::mutex noteEventQueueMutex_; - // subscribed updates - std::vector> continuousUpdates_; - std::vector> triggerUpdates_; + std::vector> updates_; // thread safety std::unique_ptr threadChecker_; diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp index 20e7c1e4..6ffcbea5 100644 --- a/plugins/vst/SfizzVstUpdates.cpp +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -8,61 +8,51 @@ #include #include -OSCUpdate::~OSCUpdate() +void QueuedUpdates::enqueue(IPtr update) { - clear(); + std::lock_guard lock(mutex_); + for (std::pair& item : updates_) + item.second.push_back(update); } -void OSCUpdate::clear() +auto QueuedUpdates::getUpdates(IDependent* dep) -> List { - if (allocated_) - delete[] reinterpret_cast(data_); - data_ = nullptr; - size_ = 0; - allocated_ = false; + std::lock_guard lock(mutex_); + List list; + auto it = updates_.find(dep); + if (it != updates_.end()) + std::swap(list, it->second); + return list; } -void OSCUpdate::setMessage(const void* data, uint32_t size, bool copy) +void QueuedUpdates::addDependent(IDependent* dep) { - clear(); + std::lock_guard lock(mutex_); + FObject::addDependent(dep); + updates_.emplace(dep, List()); +} - if (copy) { - uint8_t *buffer = new uint8_t[size]; - std::memcpy(buffer, data, size); - data = buffer; - } - - data_ = data; - size_ = size; - allocated_ = copy; +void QueuedUpdates::removeDependent(IDependent* dep) +{ + std::lock_guard lock(mutex_); + FObject::removeDependent(dep); + updates_.erase(dep); } /// -NoteUpdate::~NoteUpdate() +OSCUpdate::OSCUpdate(const uint8* data, uint32 size) { - clear(); + uint8* copy = new uint8[size]; + std::copy_n(data, size, copy); + data_.reset(copy); + size_ = size; } -void NoteUpdate::clear() +/// +NoteUpdate::NoteUpdate(const Item* items, uint32 count) { - if (allocated_) - delete[] events_; - events_ = nullptr; - count_ = 0; - allocated_ = false; -} - -void NoteUpdate::setEvents(const std::pair* events, uint32_t count, bool copy) -{ - clear(); - - if (copy) { - auto *buffer = new std::pair[count]; - std::copy_n(events, count, buffer); - events = buffer; - } - - events_ = events; + Item* copy = new Item[count]; + std::copy_n(items, count, copy); + events_.reset(copy); count_ = count; - allocated_ = copy; } diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index add4107e..d39edcfd 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -8,62 +8,64 @@ #include "SfizzVstState.h" #include #include +#include #include +#include #include #include +/** + * @brief Update which notifies a FIFO queue of one-time updates + */ +class QueuedUpdates : public Steinberg::FObject { +public: + using List = std::vector>; + + void enqueue(IPtr update); + List getUpdates(IDependent* dep); + + void addDependent(IDependent* dep) override; + void removeDependent(IDependent* dep) override; + + OBJ_METHODS(QueuedUpdates, FObject) + +private: + std::mutex mutex_; + std::map updates_; +}; + /** * @brief Update which notifies a single OSC message - * Is is supposed to be used synchronously. - * (ie. FObject::changed or UpdateHandler::triggerUpdates) */ class OSCUpdate : public Steinberg::FObject { public: - OSCUpdate() = default; - ~OSCUpdate(); - void clear(); - void setMessage(const void* data, uint32_t size, bool copy); - - const void* data() const noexcept { return data_; } - const uint32_t size() const noexcept { return size_; } + OSCUpdate(const uint8* data, uint32 size); + const uint8* data() const noexcept { return data_.get(); } + uint32_t size() const noexcept { return size_; } OBJ_METHODS(OSCUpdate, FObject) private: - const void* data_ = nullptr; - uint32_t size_ = 0; - bool allocated_ = false; - -private: - OSCUpdate(const OSCUpdate&) = delete; - OSCUpdate& operator=(const OSCUpdate&) = delete; + std::unique_ptr data_; + uint32 size_ = 0; }; /** * @brief Update which notifies one or more note on/off events - * Is is supposed to be used synchronously. - * (ie. FObject::changed or UpdateHandler::triggerUpdates) */ class NoteUpdate : public Steinberg::FObject { public: - NoteUpdate() = default; - ~NoteUpdate(); - void clear(); - void setEvents(const std::pair* events, uint32_t count, bool copy); + using Item = std::pair; - const std::pair* events() const noexcept { return events_; } + NoteUpdate(const Item* items, uint32 count); + const Item* events() const noexcept { return events_.get(); } const uint32_t count() const noexcept { return count_; } OBJ_METHODS(NoteUpdate, FObject) private: - const std::pair* events_ = nullptr; + std::unique_ptr events_; uint32_t count_ = 0; - bool allocated_ = false; - -private: - NoteUpdate(const NoteUpdate&) = delete; - NoteUpdate& operator=(const NoteUpdate&) = delete; }; /**