Merge pull request #865 from jpcima/vst-defered

Make VST updates defered
This commit is contained in:
JP Cimalando 2021-04-28 17:46:19 +02:00 committed by GitHub
commit eba974cfe1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 128 additions and 210 deletions

View file

@ -21,8 +21,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
Steinberg::UpdateHandler::instance(); Steinberg::UpdateHandler::instance();
// create update objects // create update objects
oscUpdate_ = Steinberg::owned(new OSCUpdate); queuedUpdates_ = Steinberg::owned(new QueuedUpdates);
noteUpdate_ = Steinberg::owned(new NoteUpdate);
sfzUpdate_ = Steinberg::owned(new SfzUpdate); sfzUpdate_ = Steinberg::owned(new SfzUpdate);
sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate); sfzDescriptionUpdate_ = Steinberg::owned(new SfzDescriptionUpdate);
scalaUpdate_ = Steinberg::owned(new ScalaUpdate); scalaUpdate_ = Steinberg::owned(new ScalaUpdate);
@ -289,10 +288,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
if (result != kResultTrue) if (result != kResultTrue)
return result; return result;
// this is a synchronous send, because the update object gets reused IPtr<OSCUpdate> update = Steinberg::owned(
oscUpdate_->setMessage(data, size, false); new OSCUpdate(reinterpret_cast<const uint8*>(data), size));
oscUpdate_->changed(); queuedUpdates_->enqueue(update);
oscUpdate_->clear(); queuedUpdates_->deferUpdate();
} }
else if (!strcmp(id, "NoteEvents")) { else if (!strcmp(id, "NoteEvents")) {
const void* data = nullptr; const void* data = nullptr;
@ -303,10 +302,10 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
const std::pair<uint32_t, float>*>(data); const std::pair<uint32_t, float>*>(data);
uint32 numEvents = size / sizeof(events[0]); uint32 numEvents = size / sizeof(events[0]);
// this is a synchronous send, because the update object gets reused IPtr<NoteUpdate> update = Steinberg::owned(
noteUpdate_->setEvents(events, numEvents, false); new NoteUpdate(events, numEvents));
noteUpdate_->changed(); queuedUpdates_->enqueue(update);
noteUpdate_->clear(); queuedUpdates_->deferUpdate();
} }
else if (!strcmp(id, "Automate")) { else if (!strcmp(id, "Automate")) {
const void* data = nullptr; const void* data = nullptr;
@ -343,20 +342,17 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
if (name != Vst::ViewType::kEditor) if (name != Vst::ViewType::kEditor)
return nullptr; return nullptr;
std::vector<FObject*> continuousUpdates; std::vector<FObject*> updates;
continuousUpdates.push_back(sfzUpdate_); updates.push_back(queuedUpdates_);
continuousUpdates.push_back(sfzDescriptionUpdate_); updates.push_back(sfzUpdate_);
continuousUpdates.push_back(scalaUpdate_); updates.push_back(sfzDescriptionUpdate_);
continuousUpdates.push_back(playStateUpdate_); updates.push_back(scalaUpdate_);
updates.push_back(playStateUpdate_);
for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i)
continuousUpdates.push_back(parameters.getParameterByIndex(i)); updates.push_back(parameters.getParameterByIndex(i));
std::vector<FObject*> triggerUpdates;
triggerUpdates.push_back(oscUpdate_);
triggerUpdates.push_back(noteUpdate_);
IPtr<SfizzVstEditor> editor = Steinberg::owned( IPtr<SfizzVstEditor> editor = Steinberg::owned(
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates))); new SfizzVstEditor(this, absl::MakeSpan(updates)));
editor->remember(); editor->remember();
return editor; return editor;

View file

@ -44,8 +44,7 @@ public:
REFCOUNT_METHODS(Vst::EditController) REFCOUNT_METHODS(Vst::EditController)
protected: protected:
Steinberg::IPtr<OSCUpdate> oscUpdate_; Steinberg::IPtr<QueuedUpdates> queuedUpdates_;
Steinberg::IPtr<NoteUpdate> noteUpdate_;
Steinberg::IPtr<SfzUpdate> sfzUpdate_; Steinberg::IPtr<SfzUpdate> sfzUpdate_;
Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_; Steinberg::IPtr<SfzDescriptionUpdate> sfzDescriptionUpdate_;
Steinberg::IPtr<ScalaUpdate> scalaUpdate_; Steinberg::IPtr<ScalaUpdate> scalaUpdate_;

View file

@ -30,14 +30,10 @@ enum {
kNoteEventQueueSize = 8192, kNoteEventQueueSize = 8192,
}; };
SfizzVstEditor::SfizzVstEditor( SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates)
SfizzVstController* controller,
absl::Span<FObject*> continuousUpdates,
absl::Span<FObject*> triggerUpdates)
: VSTGUIEditor(controller, &sfizzUiViewRect), : VSTGUIEditor(controller, &sfizzUiViewRect),
oscTemp_(new uint8_t[kOscTempSize]), oscTemp_(new uint8_t[kOscTempSize]),
continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()), updates_(updates.begin(), updates.end())
triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end())
{ {
} }
@ -69,19 +65,6 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
editor_.reset(editor); editor_.reset(editor);
} }
{
std::lock_guard<std::mutex> lock(oscQueueMutex_);
OscByteVec* queue = new OscByteVec;
oscQueue_.reset(queue);
queue->reserve(kOscQueueSize);
}
{
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
NoteEventsVec* queue = new NoteEventsVec;
noteEventQueue_.reset(queue);
queue->reserve(kNoteEventQueueSize);
}
if (!frame->open(parent, platformType, config)) { if (!frame->open(parent, platformType, config)) {
fprintf(stderr, "[sfizz] error opening frame\n"); fprintf(stderr, "[sfizz] error opening frame\n");
return false; return false;
@ -89,9 +72,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
editor->open(*frame); editor->open(*frame);
for (FObject* update : continuousUpdates_) for (FObject* update : updates_)
update->addDependent(this);
for (FObject* update : triggerUpdates_)
update->addDependent(this); update->addDependent(this);
threadChecker_ = Vst::ThreadChecker::create(); threadChecker_ = Vst::ThreadChecker::create();
@ -100,7 +81,7 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
Steinberg::IdleUpdateHandler::start(); Steinberg::IdleUpdateHandler::start();
for (FObject* update : continuousUpdates_) for (FObject* update : updates_)
update->deferUpdate(); update->deferUpdate();
// let the editor know about plugin format // let the editor know about plugin format
@ -138,9 +119,7 @@ void PLUGIN_API SfizzVstEditor::close()
if (frame) { if (frame) {
Steinberg::IdleUpdateHandler::stop(); Steinberg::IdleUpdateHandler::stop();
for (FObject* update : continuousUpdates_) for (FObject* update : updates_)
update->removeDependent(this);
for (FObject* update : triggerUpdates_)
update->removeDependent(this); update->removeDependent(this);
if (editor_) if (editor_)
@ -152,15 +131,6 @@ void PLUGIN_API SfizzVstEditor::close()
this->frame = nullptr; this->frame = nullptr;
} }
{
std::lock_guard<std::mutex> lock(oscQueueMutex_);
oscQueue_.reset();
}
{
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
noteEventQueue_.reset();
}
updateEditorIsOpenParameter(); updateEditorIsOpenParameter();
} }
@ -195,8 +165,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
#endif #endif
if (message == CVSTGUITimer::kMsgTimer) { if (message == CVSTGUITimer::kMsgTimer) {
processOscQueue();
processNoteEventQueue();
processParameterUpdates(); processParameterUpdates();
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time 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) void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
{ {
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) { if (processUpdate(changedUnknown, message))
// this update is synchronous: may happen from non-UI thread
uint32 size = update->size();
if (size > 0) {
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(update->data());
std::lock_guard<std::mutex> lock(oscQueueMutex_);
if (OscByteVec* queue = oscQueue_.get())
std::copy(bytes, bytes + size, std::back_inserter(*queue));
}
return; return;
Vst::VSTGUIEditor::update(changedUnknown, message);
}
bool SfizzVstEditor::processUpdate(FUnknown* changedUnknown, int32 message)
{
if (QueuedUpdates* update = FCast<QueuedUpdates>(changedUnknown)) {
for (FObject* queuedUpdate : update->getUpdates(this))
processUpdate(queuedUpdate, message);
return true;
}
if (OSCUpdate* update = FCast<OSCUpdate>(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<NoteUpdate>(changedUnknown)) { if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
// this update is synchronous: may happen from non-UI thread const NoteUpdate::Item* events = update->events();
uint32 count = update->count(); uint32 count = update->count();
if (count > 0) { for (uint32 i = 0; i < count; ++i)
const auto* events = update->events(); uiReceiveValue(editIdForKey(events[i].first), events[i].second);
std::lock_guard<std::mutex> lock(noteEventQueueMutex_); return true;
if (NoteEventsVec* queue = noteEventQueue_.get())
std::copy(events, events + count, std::back_inserter(*queue));
}
return;
} }
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) { if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
const std::string path = update->getPath(); const std::string path = update->getPath();
uiReceiveValue(EditId::SfzFile, path); uiReceiveValue(EditId::SfzFile, path);
return; return true;
} }
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) { if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
@ -268,19 +253,19 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]); uiReceiveValue(editIdForCCLabel(int(cc)), desc.ccLabel[cc]);
} }
} }
return; return true;
} }
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) { if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
const std::string path = update->getPath(); const std::string path = update->getPath();
uiReceiveValue(EditId::ScalaFile, path); uiReceiveValue(EditId::ScalaFile, path);
return; return true;
} }
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) { if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
const SfizzPlayState playState = update->getState(); const SfizzPlayState playState = update->getState();
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
return; return true;
} }
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) { if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
@ -297,50 +282,10 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_); std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
parametersToUpdate_.insert(id); parametersToUpdate_.insert(id);
} }
return; return true;
} }
Vst::VSTGUIEditor::update(changedUnknown, message); return false;
}
void SfizzVstEditor::processOscQueue()
{
std::lock_guard<std::mutex> 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<std::mutex> lock(noteEventQueueMutex_);
NoteEventsVec* queue = noteEventQueue_.get();
if (!queue)
return;
for (std::pair<uint32, float> event : *queue)
uiReceiveValue(editIdForKey(event.first), event.second);
queue->clear();
} }
void SfizzVstEditor::processParameterUpdates() void SfizzVstEditor::processParameterUpdates()

View file

@ -25,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor,
public: public:
using Self = SfizzVstEditor; using Self = SfizzVstEditor;
SfizzVstEditor( SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> updates);
SfizzVstController* controller,
absl::Span<FObject*> continuousUpdates,
absl::Span<FObject*> triggerUpdates);
~SfizzVstEditor(); ~SfizzVstEditor();
bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override; bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType) override;
@ -48,8 +45,7 @@ public:
// //
private: private:
void processOscQueue(); bool processUpdate(FUnknown* changedUnknown, int32 message);
void processNoteEventQueue();
void processParameterUpdates(); void processParameterUpdates();
void updateParameter(Vst::Parameter* parameterToUpdate); void updateParameter(Vst::Parameter* parameterToUpdate);
@ -76,18 +72,8 @@ private:
// messaging // messaging
std::unique_ptr<uint8[]> oscTemp_; std::unique_ptr<uint8[]> oscTemp_;
// editor state
// note: might be updated from a non-UI thread
typedef std::vector<uint8_t> OscByteVec;
std::unique_ptr<OscByteVec> oscQueue_;
std::mutex oscQueueMutex_;
typedef std::vector<std::pair<uint32, float>> NoteEventsVec;
std::unique_ptr<NoteEventsVec> noteEventQueue_;
std::mutex noteEventQueueMutex_;
// subscribed updates // subscribed updates
std::vector<IPtr<FObject>> continuousUpdates_; std::vector<IPtr<FObject>> updates_;
std::vector<IPtr<FObject>> triggerUpdates_;
// thread safety // thread safety
std::unique_ptr<Vst::ThreadChecker> threadChecker_; std::unique_ptr<Vst::ThreadChecker> threadChecker_;

View file

@ -8,61 +8,51 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
OSCUpdate::~OSCUpdate() void QueuedUpdates::enqueue(IPtr<FObject> update)
{ {
clear(); std::lock_guard<std::mutex> lock(mutex_);
for (std::pair<IDependent* const, List>& item : updates_)
item.second.push_back(update);
} }
void OSCUpdate::clear() auto QueuedUpdates::getUpdates(IDependent* dep) -> List
{ {
if (allocated_) std::lock_guard<std::mutex> lock(mutex_);
delete[] reinterpret_cast<const uint8_t*>(data_); List list;
data_ = nullptr; auto it = updates_.find(dep);
size_ = 0; if (it != updates_.end())
allocated_ = false; 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<std::mutex> lock(mutex_);
FObject::addDependent(dep);
updates_.emplace(dep, List());
}
if (copy) { void QueuedUpdates::removeDependent(IDependent* dep)
uint8_t *buffer = new uint8_t[size]; {
std::memcpy(buffer, data, size); std::lock_guard<std::mutex> lock(mutex_);
data = buffer; FObject::removeDependent(dep);
} updates_.erase(dep);
data_ = data;
size_ = size;
allocated_ = copy;
} }
/// ///
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_) Item* copy = new Item[count];
delete[] events_; std::copy_n(items, count, copy);
events_ = nullptr; events_.reset(copy);
count_ = 0;
allocated_ = false;
}
void NoteUpdate::setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy)
{
clear();
if (copy) {
auto *buffer = new std::pair<uint32_t, float>[count];
std::copy_n(events, count, buffer);
events = buffer;
}
events_ = events;
count_ = count; count_ = count;
allocated_ = copy;
} }

View file

@ -8,62 +8,64 @@
#include "SfizzVstState.h" #include "SfizzVstState.h"
#include <base/source/fobject.h> #include <base/source/fobject.h>
#include <vector> #include <vector>
#include <map>
#include <string> #include <string>
#include <memory>
#include <mutex> #include <mutex>
#include <cstdint> #include <cstdint>
/**
* @brief Update which notifies a FIFO queue of one-time updates
*/
class QueuedUpdates : public Steinberg::FObject {
public:
using List = std::vector<IPtr<FObject>>;
void enqueue(IPtr<FObject> 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<IDependent*, List> updates_;
};
/** /**
* @brief Update which notifies a single OSC message * @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 { class OSCUpdate : public Steinberg::FObject {
public: public:
OSCUpdate() = default; OSCUpdate(const uint8* data, uint32 size);
~OSCUpdate(); const uint8* data() const noexcept { return data_.get(); }
void clear(); uint32_t size() const noexcept { return size_; }
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_; }
OBJ_METHODS(OSCUpdate, FObject) OBJ_METHODS(OSCUpdate, FObject)
private: private:
const void* data_ = nullptr; std::unique_ptr<uint8[]> data_;
uint32_t size_ = 0; uint32 size_ = 0;
bool allocated_ = false;
private:
OSCUpdate(const OSCUpdate&) = delete;
OSCUpdate& operator=(const OSCUpdate&) = delete;
}; };
/** /**
* @brief Update which notifies one or more note on/off events * @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 { class NoteUpdate : public Steinberg::FObject {
public: public:
NoteUpdate() = default; using Item = std::pair<uint32_t, float>;
~NoteUpdate();
void clear();
void setEvents(const std::pair<uint32_t, float>* events, uint32_t count, bool copy);
const std::pair<uint32_t, float>* 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_; } const uint32_t count() const noexcept { return count_; }
OBJ_METHODS(NoteUpdate, FObject) OBJ_METHODS(NoteUpdate, FObject)
private: private:
const std::pair<uint32_t, float>* events_ = nullptr; std::unique_ptr<Item[]> events_;
uint32_t count_ = 0; uint32_t count_ = 0;
bool allocated_ = false;
private:
NoteUpdate(const NoteUpdate&) = delete;
NoteUpdate& operator=(const NoteUpdate&) = delete;
}; };
/** /**