diff --git a/plugins/vst/SfizzVstController.cpp b/plugins/vst/SfizzVstController.cpp index 9f7b9246..4cd7abfc 100644 --- a/plugins/vst/SfizzVstController.cpp +++ b/plugins/vst/SfizzVstController.cpp @@ -11,6 +11,7 @@ #include "base/source/fstreamer.h" #include "base/source/updatehandler.h" #include +#include tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) { @@ -226,7 +227,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) { - // Note: may be called from any thread (Reaper) + // Note: is expected to be called from the controller thread only tresult result = EditController::notify(message); if (result != kResultFalse) @@ -239,103 +240,62 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) } const char* id = message->getMessageID(); - Vst::IAttributeList* attr = message->getAttributes(); /// - auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult { - const void* data = nullptr; - uint32 size = 0; - tresult result = attr->getBinary(id, data, size); - if (result == kResultTrue) - string = absl::string_view(reinterpret_cast(data), size); - return result; - }; - - /// - if (!strcmp(id, "LoadedSfz")) { - absl::string_view sfzFile; - absl::string_view sfzDescriptionBlob; - - result = stringFromBinaryAttribute("File", sfzFile); - if (result != kResultTrue) - return result; - - result = stringFromBinaryAttribute("Description", sfzDescriptionBlob); - if (result != kResultTrue) - return result; - - sfzUpdate_->setPath(std::string(sfzFile)); + if (!strcmp(id, SfzUpdate::getFClassID())) { + if (!sfzUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } sfzUpdate_->deferUpdate(); - sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob)); + } + else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) { + if (!sfzDescriptionUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } sfzDescriptionUpdate_->deferUpdate(); } - else if (!strcmp(id, "LoadedScala")) { - absl::string_view scalaFile; - - result = stringFromBinaryAttribute("File", scalaFile); - if (result != kResultTrue) - return result; - - scalaUpdate_->setPath(std::string(scalaFile)); + else if (!strcmp(id, ScalaUpdate::getFClassID())) { + if (!scalaUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } scalaUpdate_->deferUpdate(); } - else if (!strcmp(id, "NotifiedPlayState")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("PlayState", data, size); - - if (result != kResultTrue) - return result; - - playStateUpdate_->setState(*static_cast(data)); + else if (!strcmp(id, PlayStateUpdate::getFClassID())) { + if (!playStateUpdate_->convertFromMessage(*message)) { + assert(false); + return kResultFalse; + } playStateUpdate_->deferUpdate(); } - else if (!strcmp(id, "ReceivedMessage")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Message", data, size); - - if (result != kResultTrue) - return result; - - IPtr update = Steinberg::owned( - new OSCUpdate(reinterpret_cast(data), size)); - queuedUpdates_->enqueue(update); - queuedUpdates_->deferUpdate(); - } - else if (!strcmp(id, "NoteEvents")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Events", data, size); - - const auto* events = reinterpret_cast< - const std::pair*>(data); - uint32 numEvents = size / sizeof(events[0]); - - IPtr update = Steinberg::owned( - new NoteUpdate(events, numEvents)); - queuedUpdates_->enqueue(update); - queuedUpdates_->deferUpdate(); - } - else if (!strcmp(id, "Automate")) { - const void* data = nullptr; - uint32 size = 0; - result = attr->getBinary("Data", data, size); - - if (result != kResultTrue) - return result; - - const uint8* pos = reinterpret_cast(data); - const uint8* end = pos + size; - - while (static_cast(end - pos) >= sizeof(uint32) + sizeof(float)) { - Vst::ParamID pid = *reinterpret_cast(pos); - pos += sizeof(uint32); - float value = *reinterpret_cast(pos); - pos += sizeof(float); - #pragma message("setParam() on non-UI thread is dangerous on Reaper, make it deferred instead") //NOLINT - setParam(pid, value); + else if (!strcmp(id, OSCUpdate::getFClassID())) { + IPtr update = OSCUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; } + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); + } + else if (!strcmp(id, NoteUpdate::getFClassID())) { + IPtr update = NoteUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } + queuedUpdates_->enqueue(update); + queuedUpdates_->deferUpdate(); + } + else if (!strcmp(id, AutomationUpdate::getFClassID())) { + IPtr update = AutomationUpdate::createFromMessage(*message); + if (!update) { + assert(false); + return kResultFalse; + } + for (AutomationUpdate::Item item : update->getItems()) + setParam(item.first, item.second); } return result; diff --git a/plugins/vst/SfizzVstProcessor.cpp b/plugins/vst/SfizzVstProcessor.cpp index a2d8573e..0614803b 100644 --- a/plugins/vst/SfizzVstProcessor.cpp +++ b/plugins/vst/SfizzVstProcessor.cpp @@ -78,6 +78,20 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) // initialize the update handler Steinberg::UpdateHandler::instance(); + _queuedMessages = Steinberg::owned(new QueuedUpdates); + _playStateUpdate = Steinberg::owned(new PlayStateUpdate); + _sfzUpdate = Steinberg::owned(new SfzUpdate); + _sfzDescriptionUpdate = Steinberg::owned(new SfzDescriptionUpdate); + _scalaUpdate = Steinberg::owned(new ScalaUpdate); + _automationUpdate = Steinberg::owned(new AutomationUpdate); + + _queuedMessages->addDependent(this); + _playStateUpdate->addDependent(this); + _sfzUpdate->addDependent(this); + _sfzDescriptionUpdate->addDependent(this); + _scalaUpdate->addDependent(this); + _automationUpdate->addDependent(this); + addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo); addEventInput(STR16("Event Input"), 1); @@ -115,6 +129,18 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context) return result; } +tresult PLUGIN_API SfizzVstProcessor::terminate() +{ + _queuedMessages->removeDependent(this); + _playStateUpdate->removeDependent(this); + _sfzUpdate->removeDependent(this); + _sfzDescriptionUpdate->removeDependent(this); + _scalaUpdate->removeDependent(this); + _automationUpdate->removeDependent(this); + + return AudioEffect::terminate(); +} + tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) { bool isStereo = numIns == 0 && numOuts == 1 && outputs[0] == Vst::SpeakerArr::kStereo; @@ -132,10 +158,7 @@ tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other) return result; // when controller connects, send these messages that we couldn't earlier - if (_loadedSfzMessage) - sendMessage(_loadedSfzMessage); - if (_automateMessage) - sendMessage(_automateMessage); + _queuedMessages->deferUpdate(); return kResultTrue; } @@ -316,7 +339,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data) synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); // - std::pair noteEvents[128]; + NoteUpdate::Item noteEvents[128]; size_t numNoteEvents = 0; for (uint32 key = 0; key < 128; ++key) { float value = _noteEventsCurrentCycle[key]; @@ -528,7 +551,7 @@ void SfizzVstProcessor::processMessagesFromUi() tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) { - // Note(jpc) this notification is not necessarily handled by the RT thread + // Note(jpc) this notification is not handled by the RT thread tresult result = AudioEffect::notify(message); if (result != kResultFalse) @@ -563,10 +586,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message) _synth->loadScalaFile(_state.scalaFile); lock.unlock(); - Steinberg::OPtr reply { allocateMessage() }; - reply->setMessageID("LoadedScala"); - reply->getAttributes()->setBinary("File", _state.scalaFile.data(), _state.scalaFile.size()); - sendMessage(reply); + _scalaUpdate->setPath(_state.scalaFile); + _scalaUpdate->deferUpdate(); } else if (!std::strcmp(id, "MidiMessage")) { const void* data = nullptr; @@ -601,7 +622,48 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message) return true; } - // TODO + if (OSCUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (PlayStateUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (NoteUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (SfzUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (SfzDescriptionUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (ScalaUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + + if (AutomationUpdate* update = FCast(changedUnknown)) { + if (IPtr message = update->convertToMessage(this)) + sendMessage(message); + return true; + } + return false; } @@ -637,12 +699,6 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i const std::string descBlob = getDescriptionBlob(synth.handle()); - Steinberg::OPtr loadMessage { allocateMessage() }; - loadMessage->setMessageID("LoadedSfz"); - Vst::IAttributeList* loadAttrs = loadMessage->getAttributes(); - loadAttrs->setBinary("File", filePath.data(), filePath.size()); - loadAttrs->setBinary("Description", descBlob.data(), descBlob.size()); - { std::vector> newControllers(sfz::config::numCCs); const std::vector> oldControllers = std::move(_state.controllers); @@ -665,26 +721,21 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i } // create a message which requests the controller to automate initial parameters - Steinberg::OPtr automateMessage { allocateMessage() }; - automateMessage->setMessageID("Automate"); - Vst::IAttributeList* automateAttrs = automateMessage->getAttributes(); - std::string automateBlob; - automateBlob.reserve(sfz::config::numCCs * (sizeof(uint32) + sizeof(float))); + std::vector automationItems; + automationItems.reserve(sfz::config::numCCs); for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) { - uint32 pid = kPidCC0 + cc; + Vst::ParamID pid = kPidCC0 + cc; float value = _state.controllers[cc].value_or(0.0f); - automateBlob.append(reinterpret_cast(&pid), sizeof(uint32)); - automateBlob.append(reinterpret_cast(&value), sizeof(float)); + automationItems.emplace_back(pid, value); } - automateAttrs->setBinary("Data", automateBlob.data(), uint32(automateBlob.size())); - - // sending can fail if controller is not connected yet, so keep it around - _loadedSfzMessage = loadMessage; - _automateMessage = automateMessage; // send message - sendMessage(loadMessage); - sendMessage(automateMessage); + _sfzUpdate->setPath(filePath); + _sfzUpdate->deferUpdate(); + _sfzDescriptionUpdate->setDescription(descBlob); + _sfzDescriptionUpdate->deferUpdate(); + _automationUpdate->setItems(std::move(automationItems)); + _automationUpdate->deferUpdate(); } void SfizzVstProcessor::doBackgroundWork() @@ -729,16 +780,16 @@ void SfizzVstProcessor::doBackgroundWork() _synth->setPreloadSize(value); } else if (id == kMsgIdReceiveOSC) { - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("ReceivedMessage"); - notification->getAttributes()->setBinary("Message", msg->payload(), msg->size); - sendMessage(notification); + IPtr update = Steinberg::owned( + new OSCUpdate(msg->payload(), msg->size)); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); } else if (id == kMsgIdNoteEvents) { - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("NoteEvents"); - notification->getAttributes()->setBinary("Events", msg->payload(), msg->size); - sendMessage(notification); + IPtr update = Steinberg::owned( + new NoteUpdate(msg->payload(), msg->size / sizeof(NoteUpdate::Item))); + _queuedMessages->enqueue(update); + _queuedMessages->deferUpdate(); } Clock::time_point currentTime = Clock::now(); @@ -755,10 +806,8 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter) { SfizzPlayState ps; ps.activeVoices = _synth->getNumActiveVoices(); - Steinberg::OPtr notification { allocateMessage() }; - notification->setMessageID("NotifiedPlayState"); - notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps)); - sendMessage(notification); + _playStateUpdate->setState(ps); + _playStateUpdate->deferUpdate(); } if (idleCounter % 25 == 0) { diff --git a/plugins/vst/SfizzVstProcessor.h b/plugins/vst/SfizzVstProcessor.h index 89ff1850..a41016e8 100644 --- a/plugins/vst/SfizzVstProcessor.h +++ b/plugins/vst/SfizzVstProcessor.h @@ -6,6 +6,7 @@ #pragma once #include "SfizzVstState.h" +#include "SfizzVstUpdates.h" #include "OrderedEventProcessor.h" #include "plugin/RMSFollower.h" #include "sfizz/RTSemaphore.h" @@ -27,6 +28,7 @@ public: ~SfizzVstProcessor(); tresult PLUGIN_API initialize(FUnknown* context) override; + tresult PLUGIN_API terminate() override; tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override; tresult PLUGIN_API connect(IConnectionPoint* other) override; @@ -56,8 +58,6 @@ public: private: // synth state. acquire processMutex before accessing std::unique_ptr _synth; - Steinberg::IPtr _loadedSfzMessage; - Steinberg::IPtr _automateMessage; bool _isActive = false; SfizzVstState _state; float _currentStretchedTuning = 0; @@ -70,6 +70,12 @@ private: bool _editorIsOpen = false; // updates + IPtr _queuedMessages; + IPtr _playStateUpdate; + IPtr _sfzUpdate; + IPtr _sfzDescriptionUpdate; + IPtr _scalaUpdate; + IPtr _automationUpdate; bool processUpdate(FUnknown* changedUnknown, int32 message); // client diff --git a/plugins/vst/SfizzVstUpdates.cpp b/plugins/vst/SfizzVstUpdates.cpp index eac7a685..5dfcfd3a 100644 --- a/plugins/vst/SfizzVstUpdates.cpp +++ b/plugins/vst/SfizzVstUpdates.cpp @@ -57,10 +57,118 @@ bool OSCUpdate::loadFromAttributes(Vst::IAttributeList* attrs) } /// -NoteUpdate::NoteUpdate(const Item* items, uint32 count) +bool NoteUpdate::saveToAttributes(Vst::IAttributeList* attrs) const { - Item* copy = new Item[count]; - std::copy_n(items, count, copy); - events_.reset(copy); - count_ = count; + return attrs->setBinary("Events", events_.data(), events_.size() * sizeof(Item)) == kResultTrue; +} + +bool NoteUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Events", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + events_.assign(events, events + numEvents); + return true; +} + +/// +bool FilePathUpdate::saveFilePathAttributes_(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Path", path_.data(), path_.size()) == kResultTrue; +} + +bool FilePathUpdate::loadFilePathAttributes_(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Path", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + path_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool SfzUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool SfzUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool ScalaUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + return saveFilePathAttributes_(attrs); +} + +bool ScalaUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + return loadFilePathAttributes_(attrs); +} + +/// +bool SfzDescriptionUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Blob", description_.data(), description_.size()) == kResultTrue; +} + +bool SfzDescriptionUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Blob", binData, binSize) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + description_.assign(reinterpret_cast(binData), binSize); + return true; +} + +/// +bool PlayStateUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setInt("ActiveVoices", state_.activeVoices) == kResultTrue; +} + +bool PlayStateUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + int64 activeVoices; + if (attrs->getInt("ActiveVoices", activeVoices) != kResultTrue) + return false; + std::lock_guard lock(mutex_); + state_.activeVoices = static_cast(activeVoices); + return true; +} + +/// +bool AutomationUpdate::saveToAttributes(Vst::IAttributeList* attrs) const +{ + std::lock_guard lock(mutex_); + return attrs->setBinary("Items", items_.data(), items_.size() * sizeof(Item)) == kResultTrue; +} + +bool AutomationUpdate::loadFromAttributes(Vst::IAttributeList* attrs) +{ + const void* binData = nullptr; + uint32 binSize = 0; + if (attrs->getBinary("Items", binData, binSize) != kResultTrue) + return false; + + const Item* events = reinterpret_cast(binData); + uint32 numEvents = binSize / sizeof(Item); + + std::lock_guard lock(mutex_); + items_.assign(events, events + numEvents); + return true; } diff --git a/plugins/vst/SfizzVstUpdates.h b/plugins/vst/SfizzVstUpdates.h index 23f8a529..76357d5f 100644 --- a/plugins/vst/SfizzVstUpdates.h +++ b/plugins/vst/SfizzVstUpdates.h @@ -25,7 +25,8 @@ public: virtual ~IConvertibleToMessage() {} IPtr convertToMessage(Vst::ComponentBase* sender) const; - static IPtr convertFromMessage(Vst::IMessage& message); + bool convertFromMessage(Vst::IMessage& message); + static IPtr createFromMessage(Vst::IMessage& message); protected: virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0; @@ -75,25 +76,32 @@ private: /** * @brief Update which notifies one or more note on/off events */ -class NoteUpdate : public Steinberg::FObject { +class NoteUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: - using Item = std::pair; + using Item = std::pair; - NoteUpdate(const Item* items, uint32 count); - const Item* events() const noexcept { return events_.get(); } - const uint32_t count() const noexcept { return count_; } + NoteUpdate() = default; + NoteUpdate(const Item* items, uint32 count) : events_(items, items + count) {} + const Item* events() const noexcept { return events_.data(); } + const uint32 count() const noexcept { return static_cast(events_.size()); } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; OBJ_METHODS(NoteUpdate, FObject) private: - std::unique_ptr events_; - uint32_t count_ = 0; + std::vector events_; }; /** - * @brief Update which notifies a change of SFZ file. + * @brief Abstract update which notifies change of a certain file path. */ -class SfzUpdate : public Steinberg::FObject { +class FilePathUpdate : public Steinberg::FObject { +protected: + FilePathUpdate() = default; + public: void setPath(std::string newPath) { @@ -107,17 +115,46 @@ public: return path_; } - OBJ_METHODS(SfzUpdate, FObject) + OBJ_METHODS(FilePathUpdate, FObject) + +protected: + bool saveFilePathAttributes_(Vst::IAttributeList* attrs) const; + bool loadFilePathAttributes_(Vst::IAttributeList* attrs); private: std::string path_; mutable std::mutex mutex_; }; +/** + * @brief Update which notifies a change of SFZ file. + */ +class SfzUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(SfzUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + +/** + * @brief Update which notifies a change of scala file. + */ +class ScalaUpdate : public FilePathUpdate, + public IConvertibleToMessage { +public: + OBJ_METHODS(ScalaUpdate, FilePathUpdate) + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; +}; + /** * @brief Update which notifies a change of SFZ description. */ -class SfzDescriptionUpdate : public Steinberg::FObject { +class SfzDescriptionUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: void setDescription(std::string newDescription) { @@ -131,6 +168,9 @@ public: return description_; } + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + OBJ_METHODS(SfzDescriptionUpdate, FObject) private: @@ -138,34 +178,11 @@ private: mutable std::mutex mutex_; }; -/** - * @brief Update which notifies a change of scala file. - */ -class ScalaUpdate : public Steinberg::FObject { -public: - void setPath(std::string newPath) - { - std::lock_guard lock(mutex_); - path_ = std::move(newPath); - } - - std::string getPath() const - { - std::lock_guard lock(mutex_); - return path_; - } - - OBJ_METHODS(ScalaUpdate, FObject) - -private: - std::string path_; - mutable std::mutex mutex_; -}; - /** * @brief Update which indicates the playing SFZ status. */ -class PlayStateUpdate : public Steinberg::FObject { +class PlayStateUpdate : public Steinberg::FObject, + public IConvertibleToMessage { public: void setState(SfizzPlayState newState) { @@ -179,6 +196,9 @@ public: return state_; } + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + virtual bool loadFromAttributes(Vst::IAttributeList* attrs) override; + OBJ_METHODS(PlayStateUpdate, FObject) private: @@ -186,4 +206,36 @@ private: mutable std::mutex mutex_; }; +/** + * @brief Update which automates a pack of parameters + */ +class AutomationUpdate : public Steinberg::FObject, + public IConvertibleToMessage { +public: + using Item = std::pair; + + AutomationUpdate() = default; + + void setItems(std::vector newItems) + { + std::lock_guard lock(mutex_); + items_ = std::move(newItems); + } + + std::vector getItems() const + { + std::lock_guard lock(mutex_); + return items_; + } + + bool saveToAttributes(Vst::IAttributeList* attrs) const override; + bool loadFromAttributes(Vst::IAttributeList* attrs) override; + + OBJ_METHODS(AutomationUpdate, FObject) + +private: + std::vector items_; + mutable std::mutex mutex_; +}; + #include "SfizzVstUpdates.hpp" diff --git a/plugins/vst/SfizzVstUpdates.hpp b/plugins/vst/SfizzVstUpdates.hpp index 48f9d32a..5b1ce1f9 100644 --- a/plugins/vst/SfizzVstUpdates.hpp +++ b/plugins/vst/SfizzVstUpdates.hpp @@ -10,7 +10,7 @@ template IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBase* sender) const { - IPtr message = owned(sender->allocateMessage()); + IPtr message = Steinberg::owned(sender->allocateMessage()); if (!message) return nullptr; message->setMessageID(static_cast(this)->isA()); @@ -20,13 +20,22 @@ IPtr IConvertibleToMessage::convertToMessage(Vst::ComponentBas } template -IPtr IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +IPtr IConvertibleToMessage::createFromMessage(Vst::IMessage& message) { IPtr object; if (!strcmp(T::getFClassID(), message.getMessageID())) { - object = owned(new T); + object = Steinberg::owned(new T); if (!object->loadFromAttributes(message.getAttributes())) object = nullptr; } return object; } + +template +bool IConvertibleToMessage::convertFromMessage(Vst::IMessage& message) +{ + bool success = false; + if (!strcmp(T::getFClassID(), message.getMessageID())) + success = loadFromAttributes(message.getAttributes()); + return success; +}