Defered message sending from VST processor
This commit is contained in:
parent
e93e760f4f
commit
eb22706fcd
6 changed files with 363 additions and 179 deletions
|
|
@ -11,6 +11,7 @@
|
|||
#include "base/source/fstreamer.h"
|
||||
#include "base/source/updatehandler.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
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<const char*>(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<const SfizzPlayState*>(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<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;
|
||||
uint32 size = 0;
|
||||
result = attr->getBinary("Events", data, size);
|
||||
|
||||
const auto* events = reinterpret_cast<
|
||||
const std::pair<uint32_t, float>*>(data);
|
||||
uint32 numEvents = size / sizeof(events[0]);
|
||||
|
||||
IPtr<NoteUpdate> 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<const uint8*>(data);
|
||||
const uint8* end = pos + size;
|
||||
|
||||
while (static_cast<size_t>(end - pos) >= sizeof(uint32) + sizeof(float)) {
|
||||
Vst::ParamID pid = *reinterpret_cast<const uint32*>(pos);
|
||||
pos += sizeof(uint32);
|
||||
float value = *reinterpret_cast<const float*>(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<OSCUpdate> update = OSCUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, NoteUpdate::getFClassID())) {
|
||||
IPtr<NoteUpdate> update = NoteUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
queuedUpdates_->enqueue(update);
|
||||
queuedUpdates_->deferUpdate();
|
||||
}
|
||||
else if (!strcmp(id, AutomationUpdate::getFClassID())) {
|
||||
IPtr<AutomationUpdate> update = AutomationUpdate::createFromMessage(*message);
|
||||
if (!update) {
|
||||
assert(false);
|
||||
return kResultFalse;
|
||||
}
|
||||
for (AutomationUpdate::Item item : update->getItems())
|
||||
setParam(item.first, item.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -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<uint32, float> 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<Vst::IMessage> 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<OSCUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PlayStateUpdate* update = FCast<PlayStateUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NoteUpdate* update = FCast<NoteUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzUpdate* update = FCast<SfzUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SfzDescriptionUpdate* update = FCast<SfzDescriptionUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ScalaUpdate* update = FCast<ScalaUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> message = update->convertToMessage(this))
|
||||
sendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutomationUpdate* update = FCast<AutomationUpdate>(changedUnknown)) {
|
||||
if (IPtr<Vst::IMessage> 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<Vst::IMessage> 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<absl::optional<float>> newControllers(sfz::config::numCCs);
|
||||
const std::vector<absl::optional<float>> 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<Vst::IMessage> automateMessage { allocateMessage() };
|
||||
automateMessage->setMessageID("Automate");
|
||||
Vst::IAttributeList* automateAttrs = automateMessage->getAttributes();
|
||||
std::string automateBlob;
|
||||
automateBlob.reserve(sfz::config::numCCs * (sizeof(uint32) + sizeof(float)));
|
||||
std::vector<AutomationUpdate::Item> 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<const char*>(&pid), sizeof(uint32));
|
||||
automateBlob.append(reinterpret_cast<const char*>(&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<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("ReceivedMessage");
|
||||
notification->getAttributes()->setBinary("Message", msg->payload<uint8_t>(), msg->size);
|
||||
sendMessage(notification);
|
||||
IPtr<OSCUpdate> update = Steinberg::owned(
|
||||
new OSCUpdate(msg->payload<uint8>(), msg->size));
|
||||
_queuedMessages->enqueue(update);
|
||||
_queuedMessages->deferUpdate();
|
||||
}
|
||||
else if (id == kMsgIdNoteEvents) {
|
||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NoteEvents");
|
||||
notification->getAttributes()->setBinary("Events", msg->payload<uint8_t>(), msg->size);
|
||||
sendMessage(notification);
|
||||
IPtr<NoteUpdate> update = Steinberg::owned(
|
||||
new NoteUpdate(msg->payload<NoteUpdate::Item>(), 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<Vst::IMessage> notification { allocateMessage() };
|
||||
notification->setMessageID("NotifiedPlayState");
|
||||
notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps));
|
||||
sendMessage(notification);
|
||||
_playStateUpdate->setState(ps);
|
||||
_playStateUpdate->deferUpdate();
|
||||
}
|
||||
|
||||
if (idleCounter % 25 == 0) {
|
||||
|
|
|
|||
|
|
@ -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<sfz::Sfizz> _synth;
|
||||
Steinberg::IPtr<Vst::IMessage> _loadedSfzMessage;
|
||||
Steinberg::IPtr<Vst::IMessage> _automateMessage;
|
||||
bool _isActive = false;
|
||||
SfizzVstState _state;
|
||||
float _currentStretchedTuning = 0;
|
||||
|
|
@ -70,6 +70,12 @@ private:
|
|||
bool _editorIsOpen = false;
|
||||
|
||||
// updates
|
||||
IPtr<QueuedUpdates> _queuedMessages;
|
||||
IPtr<PlayStateUpdate> _playStateUpdate;
|
||||
IPtr<SfzUpdate> _sfzUpdate;
|
||||
IPtr<SfzDescriptionUpdate> _sfzDescriptionUpdate;
|
||||
IPtr<ScalaUpdate> _scalaUpdate;
|
||||
IPtr<AutomationUpdate> _automationUpdate;
|
||||
bool processUpdate(FUnknown* changedUnknown, int32 message);
|
||||
|
||||
// client
|
||||
|
|
|
|||
|
|
@ -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<const Item*>(binData);
|
||||
uint32 numEvents = binSize / sizeof(Item);
|
||||
|
||||
events_.assign(events, events + numEvents);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool FilePathUpdate::saveFilePathAttributes_(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
|
||||
path_.assign(reinterpret_cast<const char *>(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<std::mutex> 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<std::mutex> lock(mutex_);
|
||||
description_.assign(reinterpret_cast<const char *>(binData), binSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool PlayStateUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
|
||||
state_.activeVoices = static_cast<uint32>(activeVoices);
|
||||
return true;
|
||||
}
|
||||
|
||||
///
|
||||
bool AutomationUpdate::saveToAttributes(Vst::IAttributeList* attrs) const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<const Item*>(binData);
|
||||
uint32 numEvents = binSize / sizeof(Item);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
items_.assign(events, events + numEvents);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ public:
|
|||
virtual ~IConvertibleToMessage() {}
|
||||
|
||||
IPtr<Vst::IMessage> convertToMessage(Vst::ComponentBase* sender) const;
|
||||
static IPtr<T> convertFromMessage(Vst::IMessage& message);
|
||||
bool convertFromMessage(Vst::IMessage& message);
|
||||
static IPtr<T> 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<NoteUpdate> {
|
||||
public:
|
||||
using Item = std::pair<uint32_t, float>;
|
||||
using Item = std::pair<uint32, float>;
|
||||
|
||||
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<uint32>(events_.size()); }
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(NoteUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::unique_ptr<Item[]> events_;
|
||||
uint32_t count_ = 0;
|
||||
std::vector<Item> 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<SfzUpdate> {
|
||||
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<ScalaUpdate> {
|
||||
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<SfzDescriptionUpdate> {
|
||||
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<std::mutex> lock(mutex_);
|
||||
path_ = std::move(newPath);
|
||||
}
|
||||
|
||||
std::string getPath() const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<PlayStateUpdate> {
|
||||
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<AutomationUpdate> {
|
||||
public:
|
||||
using Item = std::pair<Vst::ParamID, float>;
|
||||
|
||||
AutomationUpdate() = default;
|
||||
|
||||
void setItems(std::vector<Item> newItems)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
items_ = std::move(newItems);
|
||||
}
|
||||
|
||||
std::vector<Item> getItems() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return items_;
|
||||
}
|
||||
|
||||
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||
|
||||
OBJ_METHODS(AutomationUpdate, FObject)
|
||||
|
||||
private:
|
||||
std::vector<Item> items_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
#include "SfizzVstUpdates.hpp"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
template <class T>
|
||||
IPtr<Vst::IMessage> IConvertibleToMessage<T>::convertToMessage(Vst::ComponentBase* sender) const
|
||||
{
|
||||
IPtr<Vst::IMessage> message = owned(sender->allocateMessage());
|
||||
IPtr<Vst::IMessage> message = Steinberg::owned(sender->allocateMessage());
|
||||
if (!message)
|
||||
return nullptr;
|
||||
message->setMessageID(static_cast<const T*>(this)->isA());
|
||||
|
|
@ -20,13 +20,22 @@ IPtr<Vst::IMessage> IConvertibleToMessage<T>::convertToMessage(Vst::ComponentBas
|
|||
}
|
||||
|
||||
template <class T>
|
||||
IPtr<T> IConvertibleToMessage<T>::convertFromMessage(Vst::IMessage& message)
|
||||
IPtr<T> IConvertibleToMessage<T>::createFromMessage(Vst::IMessage& message)
|
||||
{
|
||||
IPtr<T> 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 <class T>
|
||||
bool IConvertibleToMessage<T>::convertFromMessage(Vst::IMessage& message)
|
||||
{
|
||||
bool success = false;
|
||||
if (!strcmp(T::getFClassID(), message.getMessageID()))
|
||||
success = loadFromAttributes(message.getAttributes());
|
||||
return success;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue