Make VST updates defered

This commit is contained in:
Jean Pierre Cimalando 2021-04-28 17:19:42 +02:00
parent 4110c0e78c
commit ac8ba20ed5
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();
// 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<OSCUpdate> update = Steinberg::owned(
new OSCUpdate(reinterpret_cast<const uint8*>(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<uint32_t, float>*>(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<NoteUpdate> 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<FObject*> continuousUpdates;
continuousUpdates.push_back(sfzUpdate_);
continuousUpdates.push_back(sfzDescriptionUpdate_);
continuousUpdates.push_back(scalaUpdate_);
continuousUpdates.push_back(playStateUpdate_);
std::vector<FObject*> 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<FObject*> triggerUpdates;
triggerUpdates.push_back(oscUpdate_);
triggerUpdates.push_back(noteUpdate_);
updates.push_back(parameters.getParameterByIndex(i));
IPtr<SfizzVstEditor> editor = Steinberg::owned(
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates)));
new SfizzVstEditor(this, absl::MakeSpan(updates)));
editor->remember();
return editor;

View file

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

View file

@ -30,14 +30,10 @@ enum {
kNoteEventQueueSize = 8192,
};
SfizzVstEditor::SfizzVstEditor(
SfizzVstController* controller,
absl::Span<FObject*> continuousUpdates,
absl::Span<FObject*> triggerUpdates)
SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> 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<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)) {
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<std::mutex> lock(oscQueueMutex_);
oscQueue_.reset();
}
{
std::lock_guard<std::mutex> 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<OSCUpdate>(changedUnknown)) {
// 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));
}
if (processUpdate(changedUnknown, message))
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)) {
// 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<std::mutex> 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<SfzUpdate>(changedUnknown)) {
const std::string path = update->getPath();
uiReceiveValue(EditId::SfzFile, path);
return;
return true;
}
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]);
}
}
return;
return true;
}
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
const std::string path = update->getPath();
uiReceiveValue(EditId::ScalaFile, path);
return;
return true;
}
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
const SfizzPlayState playState = update->getState();
uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices);
return;
return true;
}
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_);
parametersToUpdate_.insert(id);
}
return;
return true;
}
Vst::VSTGUIEditor::update(changedUnknown, message);
}
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();
return false;
}
void SfizzVstEditor::processParameterUpdates()

View file

@ -25,10 +25,7 @@ class SfizzVstEditor : public Vst::VSTGUIEditor,
public:
using Self = SfizzVstEditor;
SfizzVstEditor(
SfizzVstController* controller,
absl::Span<FObject*> continuousUpdates,
absl::Span<FObject*> triggerUpdates);
SfizzVstEditor(SfizzVstController* controller, absl::Span<FObject*> 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<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
std::vector<IPtr<FObject>> continuousUpdates_;
std::vector<IPtr<FObject>> triggerUpdates_;
std::vector<IPtr<FObject>> updates_;
// thread safety
std::unique_ptr<Vst::ThreadChecker> threadChecker_;

View file

@ -8,61 +8,51 @@
#include <algorithm>
#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_)
delete[] reinterpret_cast<const uint8_t*>(data_);
data_ = nullptr;
size_ = 0;
allocated_ = false;
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<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;
Item* copy = new Item[count];
std::copy_n(items, count, copy);
events_.reset(copy);
count_ = count;
allocated_ = copy;
}

View file

@ -8,62 +8,64 @@
#include "SfizzVstState.h"
#include <base/source/fobject.h>
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <mutex>
#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
* 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<uint8[]> 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<uint32_t, float>* events, uint32_t count, bool copy);
using Item = std::pair<uint32_t, float>;
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_; }
OBJ_METHODS(NoteUpdate, FObject)
private:
const std::pair<uint32_t, float>* events_ = nullptr;
std::unique_ptr<Item[]> events_;
uint32_t count_ = 0;
bool allocated_ = false;
private:
NoteUpdate(const NoteUpdate&) = delete;
NoteUpdate& operator=(const NoteUpdate&) = delete;
};
/**