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/fstreamer.h"
|
||||||
#include "base/source/updatehandler.h"
|
#include "base/source/updatehandler.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
||||||
{
|
{
|
||||||
|
|
@ -226,7 +227,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
||||||
|
|
||||||
tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
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);
|
tresult result = EditController::notify(message);
|
||||||
if (result != kResultFalse)
|
if (result != kResultFalse)
|
||||||
|
|
@ -239,103 +240,62 @@ tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* id = message->getMessageID();
|
const char* id = message->getMessageID();
|
||||||
Vst::IAttributeList* attr = message->getAttributes();
|
|
||||||
|
|
||||||
///
|
///
|
||||||
auto stringFromBinaryAttribute = [attr](const char* id, absl::string_view& string) -> tresult {
|
if (!strcmp(id, SfzUpdate::getFClassID())) {
|
||||||
const void* data = nullptr;
|
if (!sfzUpdate_->convertFromMessage(*message)) {
|
||||||
uint32 size = 0;
|
assert(false);
|
||||||
tresult result = attr->getBinary(id, data, size);
|
return kResultFalse;
|
||||||
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));
|
|
||||||
sfzUpdate_->deferUpdate();
|
sfzUpdate_->deferUpdate();
|
||||||
sfzDescriptionUpdate_->setDescription(std::string(sfzDescriptionBlob));
|
}
|
||||||
|
else if (!strcmp(id, SfzDescriptionUpdate::getFClassID())) {
|
||||||
|
if (!sfzDescriptionUpdate_->convertFromMessage(*message)) {
|
||||||
|
assert(false);
|
||||||
|
return kResultFalse;
|
||||||
|
}
|
||||||
sfzDescriptionUpdate_->deferUpdate();
|
sfzDescriptionUpdate_->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (!strcmp(id, "LoadedScala")) {
|
else if (!strcmp(id, ScalaUpdate::getFClassID())) {
|
||||||
absl::string_view scalaFile;
|
if (!scalaUpdate_->convertFromMessage(*message)) {
|
||||||
|
assert(false);
|
||||||
result = stringFromBinaryAttribute("File", scalaFile);
|
return kResultFalse;
|
||||||
if (result != kResultTrue)
|
}
|
||||||
return result;
|
|
||||||
|
|
||||||
scalaUpdate_->setPath(std::string(scalaFile));
|
|
||||||
scalaUpdate_->deferUpdate();
|
scalaUpdate_->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (!strcmp(id, "NotifiedPlayState")) {
|
else if (!strcmp(id, PlayStateUpdate::getFClassID())) {
|
||||||
const void* data = nullptr;
|
if (!playStateUpdate_->convertFromMessage(*message)) {
|
||||||
uint32 size = 0;
|
assert(false);
|
||||||
result = attr->getBinary("PlayState", data, size);
|
return kResultFalse;
|
||||||
|
}
|
||||||
if (result != kResultTrue)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
playStateUpdate_->setState(*static_cast<const SfizzPlayState*>(data));
|
|
||||||
playStateUpdate_->deferUpdate();
|
playStateUpdate_->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (!strcmp(id, "ReceivedMessage")) {
|
else if (!strcmp(id, OSCUpdate::getFClassID())) {
|
||||||
const void* data = nullptr;
|
IPtr<OSCUpdate> update = OSCUpdate::createFromMessage(*message);
|
||||||
uint32 size = 0;
|
if (!update) {
|
||||||
result = attr->getBinary("Message", data, size);
|
assert(false);
|
||||||
|
return kResultFalse;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,20 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
||||||
// initialize the update handler
|
// initialize the update handler
|
||||||
Steinberg::UpdateHandler::instance();
|
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);
|
addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo);
|
||||||
addEventInput(STR16("Event Input"), 1);
|
addEventInput(STR16("Event Input"), 1);
|
||||||
|
|
||||||
|
|
@ -115,6 +129,18 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
||||||
return result;
|
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)
|
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;
|
bool isStereo = numIns == 0 && numOuts == 1 && outputs[0] == Vst::SpeakerArr::kStereo;
|
||||||
|
|
@ -132,10 +158,7 @@ tresult PLUGIN_API SfizzVstProcessor::connect(IConnectionPoint* other)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// when controller connects, send these messages that we couldn't earlier
|
// when controller connects, send these messages that we couldn't earlier
|
||||||
if (_loadedSfzMessage)
|
_queuedMessages->deferUpdate();
|
||||||
sendMessage(_loadedSfzMessage);
|
|
||||||
if (_automateMessage)
|
|
||||||
sendMessage(_automateMessage);
|
|
||||||
|
|
||||||
return kResultTrue;
|
return kResultTrue;
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +339,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
||||||
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
||||||
|
|
||||||
//
|
//
|
||||||
std::pair<uint32, float> noteEvents[128];
|
NoteUpdate::Item noteEvents[128];
|
||||||
size_t numNoteEvents = 0;
|
size_t numNoteEvents = 0;
|
||||||
for (uint32 key = 0; key < 128; ++key) {
|
for (uint32 key = 0; key < 128; ++key) {
|
||||||
float value = _noteEventsCurrentCycle[key];
|
float value = _noteEventsCurrentCycle[key];
|
||||||
|
|
@ -528,7 +551,7 @@ void SfizzVstProcessor::processMessagesFromUi()
|
||||||
|
|
||||||
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
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);
|
tresult result = AudioEffect::notify(message);
|
||||||
if (result != kResultFalse)
|
if (result != kResultFalse)
|
||||||
|
|
@ -563,10 +586,8 @@ tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
||||||
_synth->loadScalaFile(_state.scalaFile);
|
_synth->loadScalaFile(_state.scalaFile);
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
Steinberg::OPtr<Vst::IMessage> reply { allocateMessage() };
|
_scalaUpdate->setPath(_state.scalaFile);
|
||||||
reply->setMessageID("LoadedScala");
|
_scalaUpdate->deferUpdate();
|
||||||
reply->getAttributes()->setBinary("File", _state.scalaFile.data(), _state.scalaFile.size());
|
|
||||||
sendMessage(reply);
|
|
||||||
}
|
}
|
||||||
else if (!std::strcmp(id, "MidiMessage")) {
|
else if (!std::strcmp(id, "MidiMessage")) {
|
||||||
const void* data = nullptr;
|
const void* data = nullptr;
|
||||||
|
|
@ -601,7 +622,48 @@ bool SfizzVstProcessor::processUpdate(FUnknown* changedUnknown, int32 message)
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -637,12 +699,6 @@ void SfizzVstProcessor::loadSfzFileOrDefault(const std::string& filePath, bool i
|
||||||
|
|
||||||
const std::string descBlob = getDescriptionBlob(synth.handle());
|
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);
|
std::vector<absl::optional<float>> newControllers(sfz::config::numCCs);
|
||||||
const std::vector<absl::optional<float>> oldControllers = std::move(_state.controllers);
|
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
|
// create a message which requests the controller to automate initial parameters
|
||||||
Steinberg::OPtr<Vst::IMessage> automateMessage { allocateMessage() };
|
std::vector<AutomationUpdate::Item> automationItems;
|
||||||
automateMessage->setMessageID("Automate");
|
automationItems.reserve(sfz::config::numCCs);
|
||||||
Vst::IAttributeList* automateAttrs = automateMessage->getAttributes();
|
|
||||||
std::string automateBlob;
|
|
||||||
automateBlob.reserve(sfz::config::numCCs * (sizeof(uint32) + sizeof(float)));
|
|
||||||
for (uint32 cc = 0; cc < sfz::config::numCCs; ++cc) {
|
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);
|
float value = _state.controllers[cc].value_or(0.0f);
|
||||||
automateBlob.append(reinterpret_cast<const char*>(&pid), sizeof(uint32));
|
automationItems.emplace_back(pid, value);
|
||||||
automateBlob.append(reinterpret_cast<const char*>(&value), sizeof(float));
|
|
||||||
}
|
}
|
||||||
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
|
// send message
|
||||||
sendMessage(loadMessage);
|
_sfzUpdate->setPath(filePath);
|
||||||
sendMessage(automateMessage);
|
_sfzUpdate->deferUpdate();
|
||||||
|
_sfzDescriptionUpdate->setDescription(descBlob);
|
||||||
|
_sfzDescriptionUpdate->deferUpdate();
|
||||||
|
_automationUpdate->setItems(std::move(automationItems));
|
||||||
|
_automationUpdate->deferUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SfizzVstProcessor::doBackgroundWork()
|
void SfizzVstProcessor::doBackgroundWork()
|
||||||
|
|
@ -729,16 +780,16 @@ void SfizzVstProcessor::doBackgroundWork()
|
||||||
_synth->setPreloadSize(value);
|
_synth->setPreloadSize(value);
|
||||||
}
|
}
|
||||||
else if (id == kMsgIdReceiveOSC) {
|
else if (id == kMsgIdReceiveOSC) {
|
||||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
IPtr<OSCUpdate> update = Steinberg::owned(
|
||||||
notification->setMessageID("ReceivedMessage");
|
new OSCUpdate(msg->payload<uint8>(), msg->size));
|
||||||
notification->getAttributes()->setBinary("Message", msg->payload<uint8_t>(), msg->size);
|
_queuedMessages->enqueue(update);
|
||||||
sendMessage(notification);
|
_queuedMessages->deferUpdate();
|
||||||
}
|
}
|
||||||
else if (id == kMsgIdNoteEvents) {
|
else if (id == kMsgIdNoteEvents) {
|
||||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
IPtr<NoteUpdate> update = Steinberg::owned(
|
||||||
notification->setMessageID("NoteEvents");
|
new NoteUpdate(msg->payload<NoteUpdate::Item>(), msg->size / sizeof(NoteUpdate::Item)));
|
||||||
notification->getAttributes()->setBinary("Events", msg->payload<uint8_t>(), msg->size);
|
_queuedMessages->enqueue(update);
|
||||||
sendMessage(notification);
|
_queuedMessages->deferUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Clock::time_point currentTime = Clock::now();
|
Clock::time_point currentTime = Clock::now();
|
||||||
|
|
@ -755,10 +806,8 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
|
||||||
{
|
{
|
||||||
SfizzPlayState ps;
|
SfizzPlayState ps;
|
||||||
ps.activeVoices = _synth->getNumActiveVoices();
|
ps.activeVoices = _synth->getNumActiveVoices();
|
||||||
Steinberg::OPtr<Vst::IMessage> notification { allocateMessage() };
|
_playStateUpdate->setState(ps);
|
||||||
notification->setMessageID("NotifiedPlayState");
|
_playStateUpdate->deferUpdate();
|
||||||
notification->getAttributes()->setBinary("PlayState", &ps, sizeof(ps));
|
|
||||||
sendMessage(notification);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idleCounter % 25 == 0) {
|
if (idleCounter % 25 == 0) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "SfizzVstState.h"
|
#include "SfizzVstState.h"
|
||||||
|
#include "SfizzVstUpdates.h"
|
||||||
#include "OrderedEventProcessor.h"
|
#include "OrderedEventProcessor.h"
|
||||||
#include "plugin/RMSFollower.h"
|
#include "plugin/RMSFollower.h"
|
||||||
#include "sfizz/RTSemaphore.h"
|
#include "sfizz/RTSemaphore.h"
|
||||||
|
|
@ -27,6 +28,7 @@ public:
|
||||||
~SfizzVstProcessor();
|
~SfizzVstProcessor();
|
||||||
|
|
||||||
tresult PLUGIN_API initialize(FUnknown* context) override;
|
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 setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override;
|
||||||
|
|
||||||
tresult PLUGIN_API connect(IConnectionPoint* other) override;
|
tresult PLUGIN_API connect(IConnectionPoint* other) override;
|
||||||
|
|
@ -56,8 +58,6 @@ public:
|
||||||
private:
|
private:
|
||||||
// synth state. acquire processMutex before accessing
|
// synth state. acquire processMutex before accessing
|
||||||
std::unique_ptr<sfz::Sfizz> _synth;
|
std::unique_ptr<sfz::Sfizz> _synth;
|
||||||
Steinberg::IPtr<Vst::IMessage> _loadedSfzMessage;
|
|
||||||
Steinberg::IPtr<Vst::IMessage> _automateMessage;
|
|
||||||
bool _isActive = false;
|
bool _isActive = false;
|
||||||
SfizzVstState _state;
|
SfizzVstState _state;
|
||||||
float _currentStretchedTuning = 0;
|
float _currentStretchedTuning = 0;
|
||||||
|
|
@ -70,6 +70,12 @@ private:
|
||||||
bool _editorIsOpen = false;
|
bool _editorIsOpen = false;
|
||||||
|
|
||||||
// updates
|
// 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);
|
bool processUpdate(FUnknown* changedUnknown, int32 message);
|
||||||
|
|
||||||
// client
|
// 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];
|
return attrs->setBinary("Events", events_.data(), events_.size() * sizeof(Item)) == kResultTrue;
|
||||||
std::copy_n(items, count, copy);
|
}
|
||||||
events_.reset(copy);
|
|
||||||
count_ = count;
|
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() {}
|
virtual ~IConvertibleToMessage() {}
|
||||||
|
|
||||||
IPtr<Vst::IMessage> convertToMessage(Vst::ComponentBase* sender) const;
|
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:
|
protected:
|
||||||
virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0;
|
virtual bool saveToAttributes(Vst::IAttributeList* attrs) const = 0;
|
||||||
|
|
@ -75,25 +76,32 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief Update which notifies one or more note on/off events
|
* @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:
|
public:
|
||||||
using Item = std::pair<uint32_t, float>;
|
using Item = std::pair<uint32, float>;
|
||||||
|
|
||||||
NoteUpdate(const Item* items, uint32 count);
|
NoteUpdate() = default;
|
||||||
const Item* events() const noexcept { return events_.get(); }
|
NoteUpdate(const Item* items, uint32 count) : events_(items, items + count) {}
|
||||||
const uint32_t count() const noexcept { return 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)
|
OBJ_METHODS(NoteUpdate, FObject)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Item[]> events_;
|
std::vector<Item> events_;
|
||||||
uint32_t count_ = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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:
|
public:
|
||||||
void setPath(std::string newPath)
|
void setPath(std::string newPath)
|
||||||
{
|
{
|
||||||
|
|
@ -107,17 +115,46 @@ public:
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
OBJ_METHODS(SfzUpdate, FObject)
|
OBJ_METHODS(FilePathUpdate, FObject)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool saveFilePathAttributes_(Vst::IAttributeList* attrs) const;
|
||||||
|
bool loadFilePathAttributes_(Vst::IAttributeList* attrs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string path_;
|
std::string path_;
|
||||||
mutable std::mutex mutex_;
|
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.
|
* @brief Update which notifies a change of SFZ description.
|
||||||
*/
|
*/
|
||||||
class SfzDescriptionUpdate : public Steinberg::FObject {
|
class SfzDescriptionUpdate : public Steinberg::FObject,
|
||||||
|
public IConvertibleToMessage<SfzDescriptionUpdate> {
|
||||||
public:
|
public:
|
||||||
void setDescription(std::string newDescription)
|
void setDescription(std::string newDescription)
|
||||||
{
|
{
|
||||||
|
|
@ -131,6 +168,9 @@ public:
|
||||||
return description_;
|
return description_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||||
|
bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||||
|
|
||||||
OBJ_METHODS(SfzDescriptionUpdate, FObject)
|
OBJ_METHODS(SfzDescriptionUpdate, FObject)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -138,34 +178,11 @@ private:
|
||||||
mutable std::mutex mutex_;
|
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.
|
* @brief Update which indicates the playing SFZ status.
|
||||||
*/
|
*/
|
||||||
class PlayStateUpdate : public Steinberg::FObject {
|
class PlayStateUpdate : public Steinberg::FObject,
|
||||||
|
public IConvertibleToMessage<PlayStateUpdate> {
|
||||||
public:
|
public:
|
||||||
void setState(SfizzPlayState newState)
|
void setState(SfizzPlayState newState)
|
||||||
{
|
{
|
||||||
|
|
@ -179,6 +196,9 @@ public:
|
||||||
return state_;
|
return state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool saveToAttributes(Vst::IAttributeList* attrs) const override;
|
||||||
|
virtual bool loadFromAttributes(Vst::IAttributeList* attrs) override;
|
||||||
|
|
||||||
OBJ_METHODS(PlayStateUpdate, FObject)
|
OBJ_METHODS(PlayStateUpdate, FObject)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -186,4 +206,36 @@ private:
|
||||||
mutable std::mutex mutex_;
|
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"
|
#include "SfizzVstUpdates.hpp"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
template <class T>
|
template <class T>
|
||||||
IPtr<Vst::IMessage> IConvertibleToMessage<T>::convertToMessage(Vst::ComponentBase* sender) const
|
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)
|
if (!message)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
message->setMessageID(static_cast<const T*>(this)->isA());
|
message->setMessageID(static_cast<const T*>(this)->isA());
|
||||||
|
|
@ -20,13 +20,22 @@ IPtr<Vst::IMessage> IConvertibleToMessage<T>::convertToMessage(Vst::ComponentBas
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
IPtr<T> IConvertibleToMessage<T>::convertFromMessage(Vst::IMessage& message)
|
IPtr<T> IConvertibleToMessage<T>::createFromMessage(Vst::IMessage& message)
|
||||||
{
|
{
|
||||||
IPtr<T> object;
|
IPtr<T> object;
|
||||||
if (!strcmp(T::getFClassID(), message.getMessageID())) {
|
if (!strcmp(T::getFClassID(), message.getMessageID())) {
|
||||||
object = owned(new T);
|
object = Steinberg::owned(new T);
|
||||||
if (!object->loadFromAttributes(message.getAttributes()))
|
if (!object->loadFromAttributes(message.getAttributes()))
|
||||||
object = nullptr;
|
object = nullptr;
|
||||||
}
|
}
|
||||||
return object;
|
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