diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index cc73ce59..8f328512 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -19,6 +19,7 @@ set(VSTPLUGIN_SOURCES SfizzVstController.cpp SfizzVstEditor.cpp SfizzVstState.cpp + SfizzVstUpdates.cpp SfizzFileScan.cpp SfizzForeignPaths.cpp SfizzSettings.cpp @@ -32,13 +33,15 @@ set(VSTPLUGIN_HEADERS SfizzVstController.h SfizzVstEditor.h SfizzVstState.h + SfizzVstParameters.h + SfizzVstUpdates.h SfizzFileScan.h SfizzForeignPaths.h SfizzSettings.h X11RunLoop.h + IdleUpdateHandler.h NativeHelpers.h - FileTrie.h - WeakPtr.h) + FileTrie.h) if(APPLE) set(VSTPLUGIN_MAC_SOURCES diff --git a/vst/IdleUpdateHandler.h b/vst/IdleUpdateHandler.h new file mode 100644 index 00000000..37488c9b --- /dev/null +++ b/vst/IdleUpdateHandler.h @@ -0,0 +1,47 @@ +// This file is part of VSTGUI. It is subject to the license terms +// in the LICENSE file found in the top-level directory of this +// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE + +#pragma once +#include "base/source/updatehandler.h" +#include + +/// @cond ignore +namespace Steinberg { + +class IdleUpdateHandler +{ +public: + static void start () + { + auto& instance = get (); + if (++instance.users == 1) + { + instance.timer = VSTGUI::makeOwned ( + [] (VSTGUI::CVSTGUITimer*) { return UpdateHandler::instance ()->triggerDeferedUpdates (); }, + 1000 / 30); + } + } + + static void stop () + { + auto& instance = get (); + if (--instance.users == 0) + { + instance.timer = nullptr; + } + } + +protected: + static IdleUpdateHandler& get () + { + static IdleUpdateHandler gInstance; + return gInstance; + } + + VSTGUI::SharedPointer timer; + std::atomic users {0}; +}; + +} // namespace Steinberg +/// @endcond ignore diff --git a/vst/SfizzVstController.cpp b/vst/SfizzVstController.cpp index 9ac894c4..c1ab5c73 100644 --- a/vst/SfizzVstController.cpp +++ b/vst/SfizzVstController.cpp @@ -6,7 +6,9 @@ #include "SfizzVstController.h" #include "SfizzVstEditor.h" +#include "SfizzVstParameters.h" #include "base/source/fstreamer.h" +#include "base/source/updatehandler.h" #include "pluginterfaces/vst/ivstmidicontrollers.h" tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) @@ -15,35 +17,46 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context) if (result != kResultTrue) return result; + // initialize the update handler + Steinberg::UpdateHandler::instance(); + + // create update objects + oscUpdate_ = Steinberg::owned(new OSCUpdate); + sfzPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateSfz)); + scalaPathUpdate_ = Steinberg::owned(new FilePathUpdate(kFilePathUpdateScala)); + processorStateUpdate_ = Steinberg::owned(new ProcessorStateUpdate); + playStateUpdate_ = Steinberg::owned(new PlayStateUpdate); + + // Parameters Vst::ParamID pid = 0; // Ordinary parameters parameters.addParameter( - kParamVolumeRange.createParameter( + SfizzRange::getForParameter(kPidVolume).createParameter( Steinberg::String("Volume"), pid++, Steinberg::String("dB"), 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamNumVoicesRange.createParameter( + SfizzRange::getForParameter(kPidNumVoices).createParameter( Steinberg::String("Polyphony"), pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamOversamplingRange.createParameter( + SfizzRange::getForParameter(kPidOversampling).createParameter( Steinberg::String("Oversampling"), pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamPreloadSizeRange.createParameter( + SfizzRange::getForParameter(kPidPreloadSize).createParameter( Steinberg::String("Preload size"), pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamScalaRootKeyRange.createParameter( + SfizzRange::getForParameter(kPidScalaRootKey).createParameter( Steinberg::String("Scala root key"), pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamTuningFrequencyRange.createParameter( + SfizzRange::getForParameter(kPidTuningFrequency).createParameter( Steinberg::String("Tuning frequency"), pid++, Steinberg::String("Hz"), 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); parameters.addParameter( - kParamStretchedTuningRange.createParameter( + SfizzRange::getForParameter(kPidStretchedTuning).createParameter( Steinberg::String("Stretched tuning"), pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId)); @@ -96,7 +109,8 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamStringByValue(Vst::ParamID ta switch (tag) { case kPidOversampling: { - auto factorLog2 = static_cast(kParamOversamplingRange.denormalize(valueNormalized)); + const SfizzRange range = SfizzRange::getForParameter(tag); + const int factorLog2 = static_cast(range.denormalize(valueNormalized)); Steinberg::String buf; buf.printf("%dX", 1 << factorLog2); buf.copyTo(string); @@ -113,14 +127,11 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID ta case kPidOversampling: { int32 factor; - if (!Steinberg::String::scanInt32(string, factor, false) || factor < 1) + if (!Steinberg::String::scanInt32(string, factor, false)) factor = 1; - int32 log2Factor = 0; - for (int32 f = factor; f > 1; f /= 2) - ++log2Factor; - - valueNormalized = kParamOversamplingRange.normalize(log2Factor); + const SfizzRange range = SfizzRange::getForParameter(tag); + valueNormalized = range.normalize(integerLog2(factor)); return kResultTrue; } } @@ -128,133 +139,49 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getParamValueByString(Vst::ParamID ta return EditController::getParamValueByString(tag, string, valueNormalized); } -// --- Controller with UI --- // - -IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) +tresult SfizzVstControllerNoUi::setParam(Vst::ParamID tag, float value) { - ConstString name(_name); - - fprintf(stderr, "[sfizz] about to create view: %s\n", _name); - - if (name != Vst::ViewType::kEditor) - return nullptr; - - if (IPtr editor = _editor.lock()) { - withStateLock([this, editor]() { - _uiState = editor->getCurrentUiState(); - }); - } - - IPtr editor = Steinberg::owned(new SfizzVstEditor(this)); - _editor = editor->getWeakPtr(); - - withStateLock([this, editor]() { - editor->updateState(_state); - editor->updateUiState(_uiState); - editor->updatePlayState(_playState); - }); - - editor->remember(); - return editor; + const SfizzRange range = SfizzRange::getForParameter(tag); + return setParamNormalized(tag, range.normalize(value)); } -tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst::ParamValue normValue) +tresult PLUGIN_API SfizzVstControllerNoUi::setParamNormalized(Vst::ParamID tag, Vst::ParamValue normValue) { - tresult r = SfizzVstControllerNoUi::setParamNormalized(tag, normValue); + tresult r = EditController::setParamNormalized(tag, normValue); if (r != kResultTrue) return r; - float *slotF32 = nullptr; - int32 *slotI32 = nullptr; - float value = 0; - - switch (tag) { - case kPidVolume: { - slotF32 = &_state.volume; - value = kParamVolumeRange.denormalize(normValue); - break; - } - case kPidNumVoices: { - slotI32 = &_state.numVoices; - value = kParamNumVoicesRange.denormalize(normValue); - break; - } - case kPidOversampling: { - slotI32 = &_state.oversamplingLog2; - value = kParamOversamplingRange.denormalize(normValue); - break; - } - case kPidPreloadSize: { - slotI32 = &_state.preloadSize; - value = kParamPreloadSizeRange.denormalize(normValue); - break; - } - case kPidScalaRootKey: { - slotI32 = &_state.scalaRootKey; - value = kParamScalaRootKeyRange.denormalize(normValue); - break; - } - case kPidTuningFrequency: { - slotF32 = &_state.tuningFrequency; - value = kParamTuningFrequencyRange.denormalize(normValue); - break; - } - case kPidStretchedTuning: { - slotF32 = &_state.stretchedTuning; - value = kParamStretchedTuningRange.denormalize(normValue); - break; - } - } - - if (slotF32 && *slotF32 != value) { - withStateLock([this, slotF32, value]() { - *slotF32 = value; - if (IPtr editor = _editor.lock()) - editor->updateState(_state); - }); - } - else if (slotI32 && *slotI32 != (int32)value) { - withStateLock([this, slotI32, value]() { - *slotI32 = (int32)value; - if (IPtr editor = _editor.lock()) - editor->updateState(_state); - }); - } - - return kResultTrue; -} - -tresult PLUGIN_API SfizzVstController::setState(IBStream* stream) -{ - SfizzUiState s; - - tresult r = s.load(stream); - if (r != kResultTrue) - return r; - - withStateLock([this, &s]() { - _uiState = s; - if (IPtr editor = _editor.lock()) - editor->updateUiState(_uiState); + processorStateUpdate_->access([tag, normValue](SfizzVstState& state) { + const SfizzRange range = SfizzRange::getForParameter(tag); + switch (tag) { + case kPidVolume: + state.volume = range.denormalize(normValue); + break; + case kPidNumVoices: + state.numVoices = (int32)range.denormalize(normValue); + break; + case kPidOversampling: + state.oversamplingLog2 = (int32)range.denormalize(normValue); + break; + case kPidPreloadSize: + state.preloadSize = (int32)range.denormalize(normValue); + break; + case kPidScalaRootKey: + state.scalaRootKey = (int32)range.denormalize(normValue); + break; + case kPidTuningFrequency: + state.tuningFrequency = (float)range.denormalize(normValue); + break; + case kPidStretchedTuning: + state.stretchedTuning = range.denormalize(normValue); + break; + } }); return kResultTrue; } -tresult PLUGIN_API SfizzVstController::getState(IBStream* stream) -{ - tresult result; - - withStateLock([this, stream, &result]() { - if (IPtr editor = _editor.lock()) - _uiState = editor->getCurrentUiState(); - result = _uiState.store(stream); - }); - - return result; -} - -tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* stream) +tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream) { SfizzVstState s; @@ -262,28 +189,29 @@ tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* stream) if (r != kResultTrue) return r; - setParamNormalized(kPidVolume, kParamVolumeRange.normalize(s.volume)); - setParamNormalized(kPidNumVoices, kParamNumVoicesRange.normalize(s.numVoices)); - setParamNormalized(kPidOversampling, kParamOversamplingRange.normalize(s.oversamplingLog2)); - setParamNormalized(kPidPreloadSize, kParamPreloadSizeRange.normalize(s.preloadSize)); - setParamNormalized(kPidScalaRootKey, kParamScalaRootKeyRange.normalize(s.scalaRootKey)); - setParamNormalized(kPidTuningFrequency, kParamTuningFrequencyRange.normalize(s.tuningFrequency)); - setParamNormalized(kPidStretchedTuning, kParamStretchedTuningRange.normalize(s.stretchedTuning)); + processorStateUpdate_->setState(s); - withStateLock([this, &s]() { - _state = s; - if (IPtr editor = _editor.lock()) - editor->updateState(_state); - }); + setParam(kPidVolume, s.volume); + setParam(kPidNumVoices, s.numVoices); + setParam(kPidOversampling, s.oversamplingLog2); + setParam(kPidPreloadSize, s.preloadSize); + setParam(kPidScalaRootKey, s.scalaRootKey); + setParam(kPidTuningFrequency, s.tuningFrequency); + setParam(kPidStretchedTuning, s.stretchedTuning); + + sfzPathUpdate_->setPath(s.sfzFile); + sfzPathUpdate_->deferUpdate(); + scalaPathUpdate_->setPath(s.scalaFile); + scalaPathUpdate_->deferUpdate(); return kResultTrue; } -tresult SfizzVstController::notify(Vst::IMessage* message) +tresult SfizzVstControllerNoUi::notify(Vst::IMessage* message) { // Note: may be called from any thread (Reaper) - tresult result = SfizzVstControllerNoUi::notify(message); + tresult result = EditController::notify(message); if (result != kResultFalse) return result; @@ -298,11 +226,12 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - withStateLock([this, data, size]() { - _state.sfzFile.assign(static_cast(data), size); - if (IPtr editor = _editor.lock()) - editor->updateState(_state); + std::string sfzFile(static_cast(data), size); + processorStateUpdate_->access([&sfzFile](SfizzVstState& state) { + state.sfzFile = sfzFile; }); + sfzPathUpdate_->setPath(std::move(sfzFile)); + sfzPathUpdate_->deferUpdate(); } else if (!strcmp(id, "LoadedScala")) { const void* data = nullptr; @@ -312,11 +241,12 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - withStateLock([this, data, size]() { - _state.scalaFile.assign(static_cast(data), size); - if (IPtr editor = _editor.lock()) - editor->updateState(_state); + std::string scalaFile(static_cast(data), size); + processorStateUpdate_->access([&scalaFile](SfizzVstState& state) { + state.scalaFile = scalaFile; }); + scalaPathUpdate_->setPath(std::move(scalaFile)); + scalaPathUpdate_->deferUpdate(); } else if (!strcmp(id, "NotifiedPlayState")) { const void* data = nullptr; @@ -326,11 +256,8 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - withStateLock([this, data]() { - _playState = *static_cast(data); - if (IPtr editor = _editor.lock()) - editor->updatePlayState(_playState); - }); + playStateUpdate_->setState(*static_cast(data)); + playStateUpdate_->deferUpdate(); } else if (!strcmp(id, "ReceivedMessage")) { const void* data = nullptr; @@ -340,13 +267,43 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - if (IPtr editor = _editor.lock()) - editor->receiveMessage(data, size); + // this is a synchronous send, because the update object gets reused + oscUpdate_->setMessage(data, size, false); + oscUpdate_->changed(); + oscUpdate_->clear(); } return result; } +// --- Controller with UI --- // + +IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) +{ + ConstString name(_name); + + fprintf(stderr, "[sfizz] about to create view: %s\n", _name); + + if (name != Vst::ViewType::kEditor) + return nullptr; + + std::vector continuousUpdates; + continuousUpdates.push_back(sfzPathUpdate_); + continuousUpdates.push_back(scalaPathUpdate_); + continuousUpdates.push_back(playStateUpdate_); + for (uint32 i = 0, n = parameters.getParameterCount(); i < n; ++i) + continuousUpdates.push_back(parameters.getParameterByIndex(i)); + + std::vector triggerUpdates; + triggerUpdates.push_back(oscUpdate_); + + IPtr editor = Steinberg::owned( + new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates))); + + editor->remember(); + return editor; +} + FUnknown* SfizzVstController::createInstance(void*) { return static_cast(new SfizzVstController); diff --git a/vst/SfizzVstController.h b/vst/SfizzVstController.h index 42094b8f..2365417b 100644 --- a/vst/SfizzVstController.h +++ b/vst/SfizzVstController.h @@ -6,10 +6,10 @@ #pragma once #include "SfizzVstState.h" +#include "SfizzVstUpdates.h" #include "public.sdk/source/vst/vsteditcontroller.h" #include "public.sdk/source/vst/vstparameters.h" #include "vstgui/plugin-bindings/vst3editor.h" -#include "WeakPtr.h" #include #include class SfizzVstState; @@ -31,6 +31,10 @@ public: tresult PLUGIN_API getParamStringByValue(Vst::ParamID tag, Vst::ParamValue valueNormalized, Vst::String128 string) override; tresult PLUGIN_API getParamValueByString(Vst::ParamID tag, Vst::TChar* string, Vst::ParamValue& valueNormalized) override; + tresult setParam(Vst::ParamID tag, float value); + tresult PLUGIN_API setParamNormalized(Vst::ParamID tag, Vst::ParamValue value) override; + tresult PLUGIN_API setComponentState(IBStream* stream) override; + tresult PLUGIN_API notify(Vst::IMessage* message) override; // interfaces OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditController) @@ -38,34 +42,20 @@ public: DEF_INTERFACE(Vst::IMidiMapping) END_DEFINE_INTERFACES(Vst::EditController) REFCOUNT_METHODS(Vst::EditController) + +protected: + Steinberg::IPtr oscUpdate_; + Steinberg::IPtr sfzPathUpdate_; + Steinberg::IPtr scalaPathUpdate_; + Steinberg::IPtr processorStateUpdate_; + Steinberg::IPtr playStateUpdate_; }; class SfizzVstController : public SfizzVstControllerNoUi, public VSTGUI::VST3EditorDelegate { public: IPlugView* PLUGIN_API createView(FIDString name) override; - tresult PLUGIN_API setParamNormalized(Vst::ParamID tag, Vst::ParamValue value) override; - tresult PLUGIN_API setState(IBStream* stream) override; - tresult PLUGIN_API getState(IBStream* stream) override; - tresult PLUGIN_API setComponentState(IBStream* stream) override; - tresult PLUGIN_API notify(Vst::IMessage* message) override; - - /// static FUnknown* createInstance(void*); static FUID cid; - -private: - template void withStateLock(F&& fn) const - { - std::lock_guard lock(_stateMutex); - fn(); - } - -private: - mutable std::mutex _stateMutex; // for R/W the state data - SfizzVstState _state {}; - SfizzUiState _uiState {}; // updated on UI open/close/state-request - SfizzPlayState _playState {}; - WeakPtr _editor; }; diff --git a/vst/SfizzVstEditor.cpp b/vst/SfizzVstEditor.cpp index 69bc7ace..1d920dfa 100644 --- a/vst/SfizzVstEditor.cpp +++ b/vst/SfizzVstEditor.cpp @@ -6,9 +6,12 @@ #include "SfizzVstEditor.h" #include "SfizzVstState.h" +#include "SfizzVstParameters.h" +#include "SfizzVstUpdates.h" #include "SfizzFileScan.h" #include "editor/Editor.h" #include "editor/EditIds.h" +#include "IdleUpdateHandler.h" #if !defined(__APPLE__) && !defined(_WIN32) #include "X11RunLoop.h" #endif @@ -22,9 +25,14 @@ enum { kOscQueueSize = 65536, }; -SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller) +SfizzVstEditor::SfizzVstEditor( + SfizzVstController* controller, + absl::Span continuousUpdates, + absl::Span triggerUpdates) : VSTGUIEditor(controller, &sfizzUiViewRect), - oscTemp_(new uint8_t[kOscTempSize]) + oscTemp_(new uint8_t[kOscTempSize]), + continuousUpdates_(continuousUpdates.begin(), continuousUpdates.end()), + triggerUpdates_(triggerUpdates.begin(), triggerUpdates.end()) { } @@ -56,16 +64,12 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor_.reset(editor); } - withStateLock([this]() { - mustRedisplayState_ = true; - mustRedisplayUiState_ = true; - mustRedisplayPlayState_ = true; + { + std::lock_guard lock(oscQueueMutex_); OscByteVec* queue = new OscByteVec; oscQueue_.reset(queue); queue->reserve(kOscQueueSize); - }); - - updateStateDisplay(); + } if (!frame->open(parent, platformType, config)) { fprintf(stderr, "[sfizz] error opening frame\n"); @@ -74,6 +78,16 @@ 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_) + update->addDependent(this); + + Steinberg::IdleUpdateHandler::start(); + + for (FObject* update : continuousUpdates_) + update->deferUpdate(); + absl::optional userFilesDir = SfizzPaths::getSfzConfigDefaultPath(); uiReceiveValue(EditId::CanEditUserFilesDir, 1); uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string()); @@ -85,6 +99,13 @@ void PLUGIN_API SfizzVstEditor::close() { CFrame *frame = this->frame; if (frame) { + Steinberg::IdleUpdateHandler::stop(); + + for (FObject* update : continuousUpdates_) + update->removeDependent(this); + for (FObject* update : triggerUpdates_) + update->removeDependent(this); + if (editor_) editor_->close(); if (frame->getNbReference() != 1) @@ -94,9 +115,8 @@ void PLUGIN_API SfizzVstEditor::close() this->frame = nullptr; } - withStateLock([this]() { - oscQueue_.reset(); - }); + std::lock_guard lock(oscQueueMutex_); + oscQueue_.reset(); } /// @@ -123,81 +143,119 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) if (message == CVSTGUITimer::kMsgTimer) { processOscQueue(); - updateStateDisplay(); } return result; } -void SfizzVstEditor::updateState(const SfizzVstState& state) +void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message) { - withStateLock([this, &state]() { - state_ = state; - mustRedisplayState_ = true; - }); -} - -void SfizzVstEditor::updateUiState(const SfizzUiState& uiState) -{ - withStateLock([this, &uiState]() { - uiState_ = uiState; - mustRedisplayUiState_ = true; - }); -} - -void SfizzVstEditor::updatePlayState(const SfizzPlayState& playState) -{ - withStateLock([this, &playState]() { - playState_ = playState; - mustRedisplayPlayState_ = true; - }); -} - -SfizzUiState SfizzVstEditor::getCurrentUiState() const -{ - SfizzUiState uiState; - withStateLock([this, &uiState]() { - uiState = uiState_; - }); - return uiState; -} - -void SfizzVstEditor::receiveMessage(const void* data, uint32_t size) -{ - // Note: may be called from non-UI thread (Reaper) - - withStateLock([this, data, size]() { - if (OscByteVec* queue = oscQueue_.get()) { - const uint8_t* bytes = reinterpret_cast(data); - std::copy(bytes, bytes + size, std::back_inserter(*queue)); + if (OSCUpdate* update = FCast(changedUnknown)) { + // this update is synchronous: may happen from non-UI thread + uint32 size = update->size(); + if (size > 0) { + const uint8_t* bytes = reinterpret_cast(update->data()); + std::lock_guard lock(oscQueueMutex_); + if (OscByteVec* queue = oscQueue_.get()) + std::copy(bytes, bytes + size, std::back_inserter(*queue)); } - }); + return; + } + + if (FilePathUpdate* update = FCast(changedUnknown)) { + const std::string path = update->getPath(); + switch (update->getType()) { + case kFilePathUpdateSfz: + uiReceiveValue(EditId::SfzFile, path); + break; + case kFilePathUpdateScala: + uiReceiveValue(EditId::ScalaFile, path); + break; + } + return; + } + + if (ProcessorStateUpdate* update = FCast(changedUnknown)) { + const SfizzVstState state = update->getState(); + uiReceiveValue(EditId::SfzFile, state.sfzFile); + uiReceiveValue(EditId::Volume, state.volume); + uiReceiveValue(EditId::Polyphony, state.numVoices); + uiReceiveValue(EditId::Oversampling, 1u << state.oversamplingLog2); + uiReceiveValue(EditId::PreloadSize, state.preloadSize); + uiReceiveValue(EditId::ScalaFile, state.scalaFile); + uiReceiveValue(EditId::ScalaRootKey, state.scalaRootKey); + uiReceiveValue(EditId::TuningFrequency, state.tuningFrequency); + uiReceiveValue(EditId::StretchTuning, state.stretchedTuning); + } + + if (PlayStateUpdate* update = FCast(changedUnknown)) { + const SfizzPlayState playState = update->getState(); + uiReceiveValue(EditId::UINumCurves, playState.curves); + uiReceiveValue(EditId::UINumMasters, playState.masters); + uiReceiveValue(EditId::UINumGroups, playState.groups); + uiReceiveValue(EditId::UINumRegions, playState.regions); + uiReceiveValue(EditId::UINumPreloadedSamples, playState.preloadedSamples); + uiReceiveValue(EditId::UINumActiveVoices, playState.activeVoices); + return; + } + + if (Vst::RangeParameter* param = Steinberg::FCast(changedUnknown)) { + const Vst::ParamValue value = param->getNormalized(); + const Vst::ParamID id = param->getInfo().id; + const SfizzRange range = SfizzRange::getForParameter(id); + switch (id) { + case kPidVolume: + uiReceiveValue(EditId::Volume, range.denormalize(value)); + break; + case kPidNumVoices: + uiReceiveValue(EditId::Polyphony, range.denormalize(value)); + break; + case kPidOversampling: + uiReceiveValue(EditId::Oversampling, 1u << (int32)range.denormalize(value)); + break; + case kPidPreloadSize: + uiReceiveValue(EditId::PreloadSize, range.denormalize(value)); + break; + case kPidScalaRootKey: + uiReceiveValue(EditId::ScalaRootKey, range.denormalize(value)); + break; + case kPidTuningFrequency: + uiReceiveValue(EditId::TuningFrequency, range.denormalize(value)); + break; + case kPidStretchedTuning: + uiReceiveValue(EditId::StretchTuning, range.denormalize(value)); + break; + } + return; + } + + Vst::VSTGUIEditor::update(changedUnknown, message); } void SfizzVstEditor::processOscQueue() { - withStateLock([this]() { - OscByteVec* queue = oscQueue_.get(); - if (!queue) - return; + std::lock_guard lock(oscQueueMutex_); - const uint8_t* oscData = queue->data(); - size_t oscSize = queue->size(); + OscByteVec* queue = oscQueue_.get(); + if (!queue) + return; - const char* path; - const char* sig; - const sfizz_arg_t* args; - uint8_t buffer[1024]; + const uint8_t* oscData = queue->data(); + size_t oscSize = queue->size(); - 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; - } + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; - queue->clear(); - }); + 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(); } /// @@ -210,51 +268,42 @@ void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v) else { SfizzVstController* ctrl = getController(); - auto normalizeAndSet = [ctrl](Vst::ParamID pid, const SfizzParameterRange& range, float value) { - float normValue = range.normalize(value); + auto normalizeAndSet = [ctrl](Vst::ParamID pid, float value) { + float normValue = SfizzRange::getForParameter(pid).normalize(value); ctrl->setParamNormalized(pid, normValue); ctrl->performEdit(pid, normValue); }; switch (id) { case EditId::Volume: - normalizeAndSet(kPidVolume, kParamVolumeRange, v.to_float()); + normalizeAndSet(kPidVolume, v.to_float()); break; case EditId::Polyphony: - normalizeAndSet(kPidNumVoices, kParamNumVoicesRange, v.to_float()); + normalizeAndSet(kPidNumVoices, v.to_float()); break; case EditId::Oversampling: { - const int32 value = static_cast(v.to_float()); - - int32 log2Value = 0; - for (int32 f = value; f > 1; f /= 2) - ++log2Value; - - normalizeAndSet(kPidOversampling, kParamOversamplingRange, log2Value); + const int32 factor = static_cast(v.to_float()); + normalizeAndSet(kPidOversampling, integerLog2(factor)); } break; case EditId::PreloadSize: - normalizeAndSet(kPidPreloadSize, kParamPreloadSizeRange, v.to_float()); + normalizeAndSet(kPidPreloadSize, v.to_float()); break; case EditId::ScalaRootKey: - normalizeAndSet(kPidScalaRootKey, kParamScalaRootKeyRange, v.to_float()); + normalizeAndSet(kPidScalaRootKey, v.to_float()); break; case EditId::TuningFrequency: - normalizeAndSet(kPidTuningFrequency, kParamTuningFrequencyRange, v.to_float()); + normalizeAndSet(kPidTuningFrequency, v.to_float()); break; case EditId::StretchTuning: - normalizeAndSet(kPidStretchedTuning, kParamStretchedTuningRange, v.to_float()); + normalizeAndSet(kPidStretchedTuning, v.to_float()); break; case EditId::UserFilesDir: SfizzPaths::setSfzConfigDefaultPath(fs::u8path(v.to_string())); break; - case EditId::UIActivePanel: - uiState_.activePanel = static_cast(v.to_float()); - break; - default: break; } @@ -344,44 +393,6 @@ void SfizzVstEditor::loadScalaFile(const std::string& filePath) ctl->sendMessage(msg); } -void SfizzVstEditor::updateStateDisplay() -{ - if (!frame) - return; - - withStateLock([this]() { - if (mustRedisplayState_) { - uiReceiveValue(EditId::SfzFile, state_.sfzFile); - uiReceiveValue(EditId::Volume, state_.volume); - uiReceiveValue(EditId::Polyphony, state_.numVoices); - uiReceiveValue(EditId::Oversampling, 1u << state_.oversamplingLog2); - uiReceiveValue(EditId::PreloadSize, state_.preloadSize); - uiReceiveValue(EditId::ScalaFile, state_.scalaFile); - uiReceiveValue(EditId::ScalaRootKey, state_.scalaRootKey); - uiReceiveValue(EditId::TuningFrequency, state_.tuningFrequency); - uiReceiveValue(EditId::StretchTuning, state_.stretchedTuning); - mustRedisplayState_ = false; - } - - /// - if (mustRedisplayUiState_) { - uiReceiveValue(EditId::UIActivePanel, uiState_.activePanel); - mustRedisplayUiState_ = false; - } - - /// - if (mustRedisplayPlayState_) { - uiReceiveValue(EditId::UINumCurves, playState_.curves); - uiReceiveValue(EditId::UINumMasters, playState_.masters); - uiReceiveValue(EditId::UINumGroups, playState_.groups); - uiReceiveValue(EditId::UINumRegions, playState_.regions); - uiReceiveValue(EditId::UINumPreloadedSamples, playState_.preloadedSamples); - uiReceiveValue(EditId::UINumActiveVoices, playState_.activeVoices); - mustRedisplayPlayState_ = false; - } - }); -} - Vst::ParamID SfizzVstEditor::parameterOfEditId(EditId id) { switch (id) { diff --git a/vst/SfizzVstEditor.h b/vst/SfizzVstEditor.h index b23f7a4f..a03302a5 100644 --- a/vst/SfizzVstEditor.h +++ b/vst/SfizzVstEditor.h @@ -7,8 +7,8 @@ #pragma once #include "SfizzVstController.h" #include "editor/EditorController.h" -#include "WeakPtr.h" #include "public.sdk/source/vst/vstguieditor.h" +#include #include class Editor; #if !defined(__APPLE__) && !defined(_WIN32) @@ -19,12 +19,14 @@ using namespace Steinberg; using namespace VSTGUI; class SfizzVstEditor : public Vst::VSTGUIEditor, - public EditorController, - public Weakable { + public EditorController { public: using Self = SfizzVstEditor; - explicit SfizzVstEditor(SfizzVstController* controller); + SfizzVstEditor( + SfizzVstController* controller, + absl::Span continuousUpdates, + absl::Span triggerUpdates); ~SfizzVstEditor(); bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType = VSTGUI::kDefaultNative) override; @@ -37,27 +39,16 @@ public: // VSTGUIEditor CMessageResult notify(CBaseObject* sender, const char* message) override; + // FObject + void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; // void updateState(const SfizzVstState& state); - void updateUiState(const SfizzUiState& uiState); void updatePlayState(const SfizzPlayState& playState); - SfizzUiState getCurrentUiState() const; - void receiveMessage(const void* data, uint32_t size); - - void remember() override { SfizzVstEditor::addRef(); } - void forget() override { SfizzVstEditor::release(); } - WEAKABLE_REFCOUNT_METHODS(SfizzVstEditor) private: void processOscQueue(); - template void withStateLock(F&& fn) const - { - std::lock_guard lock(stateMutex_); - fn(); - } - protected: // EditorController void uiSendValue(EditId id, const EditValue& v) override; @@ -70,8 +61,6 @@ private: void loadSfzFile(const std::string& filePath); void loadScalaFile(const std::string& filePath); - void updateStateDisplay(); - Vst::ParamID parameterOfEditId(EditId id); std::unique_ptr editor_; @@ -85,13 +74,11 @@ private: // editor state // note: might be updated from a non-UI thread - mutable std::recursive_mutex stateMutex_; // for R/W the state data, and OSC queue - SfizzVstState state_ {}; - SfizzUiState uiState_ {}; - SfizzPlayState playState_ {}; - volatile bool mustRedisplayState_ = false; - volatile bool mustRedisplayUiState_ = false; - volatile bool mustRedisplayPlayState_ = false; typedef std::vector OscByteVec; std::unique_ptr oscQueue_; + std::mutex oscQueueMutex_; + + // subscribed updates + std::vector> continuousUpdates_; + std::vector> triggerUpdates_; }; diff --git a/vst/SfizzVstParameters.h b/vst/SfizzVstParameters.h new file mode 100644 index 00000000..4b1ef007 --- /dev/null +++ b/vst/SfizzVstParameters.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "public.sdk/source/vst/vstparameters.h" +#include + +using namespace Steinberg; + +// number of MIDI CC +enum { + kNumControllerParams = 128, +}; + +// parameters +enum { + kPidVolume, + kPidNumVoices, + kPidOversampling, + kPidPreloadSize, + kPidScalaRootKey, + kPidTuningFrequency, + kPidStretchedTuning, + kPidMidiAftertouch, + kPidMidiPitchBend, + kPidMidiCC0, + kPidMidiCCLast = kPidMidiCC0 + kNumControllerParams - 1, + /* Reserved */ +}; + +struct SfizzRange { + float def = 0.0; + float min = 0.0; + float max = 1.0; + + constexpr SfizzRange() {} + constexpr SfizzRange(float def, float min, float max) : def(def), min(min), max(max) {} + + constexpr float normalize(float x) const noexcept + { + return (x - min) / (max - min); + } + + constexpr float denormalize(float x) const noexcept + { + return min + x * (max - min); + } + + Vst::RangeParameter* createParameter(const Vst::TChar *title, Vst::ParamID tag, const Vst::TChar *units = nullptr, int32 stepCount = 0, int32 flags = Vst::ParameterInfo::kCanAutomate, Vst::UnitID unitID = Vst::kRootUnitId, const Vst::TChar *shortTitle = nullptr) const + { + return new Vst::RangeParameter(title, tag, units, min, max, def, stepCount, flags, unitID, shortTitle); + } + + static SfizzRange getForParameter(Vst::ParamID id) + { + switch (id) { + case kPidVolume: + return {0.0, -60.0, +6.0}; + case kPidNumVoices: + return {64.0, 1.0, 256.0}; + case kPidOversampling: + return {0.0, 0.0, 3.0}; + case kPidPreloadSize: + return {8192.0, 1024.0, 65536.0}; + case kPidScalaRootKey: + return {60.0, 0.0, 127.0}; + case kPidTuningFrequency: + return {440.0, 300.0, 500.0}; + case kPidStretchedTuning: + return {0.0, 0.0, 1.0}; + case kPidMidiAftertouch: + return {0.0, 0.0, 1.0}; + case kPidMidiPitchBend: + return {0.0, 0.0, 1.0}; + default: + if (id >= kPidMidiCC0 && id <= kPidMidiCCLast) + return {0.0, 0.0, 1.0}; + throw std::runtime_error("Bad parameter ID"); + } + } +}; + +inline int32 integerLog2(int32 x) +{ + int32 l = 0; + for (; x > 1; x /= 2) ++l; + return l; +} diff --git a/vst/SfizzVstProcessor.cpp b/vst/SfizzVstProcessor.cpp index ec1d77d0..f318b269 100644 --- a/vst/SfizzVstProcessor.cpp +++ b/vst/SfizzVstProcessor.cpp @@ -7,6 +7,7 @@ #include "SfizzVstProcessor.h" #include "SfizzVstController.h" #include "SfizzVstState.h" +#include "SfizzVstParameters.h" #include "SfizzFileScan.h" #include "base/source/fstreamer.h" #include "pluginterfaces/vst/ivstevents.h" @@ -315,7 +316,9 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) if (!vq) continue; - Vst::ParamID id = vq->getParameterId(); + const Vst::ParamID id = vq->getParameterId(); + const SfizzRange range = SfizzRange::getForParameter(id); + uint32 pointCount = vq->getPointCount(); int32 sampleOffset; Vst::ParamValue value; @@ -323,11 +326,11 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) switch (id) { case kPidVolume: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) - _state.volume = kParamVolumeRange.denormalize(value); + _state.volume = range.denormalize(value); break; case kPidNumVoices: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - int32 data = static_cast(kParamNumVoicesRange.denormalize(value)); + int32 data = static_cast(range.denormalize(value)); _state.numVoices = data; if (writeWorkerMessage("SetNumVoices", &data, sizeof(data))) _semaToWorker.post(); @@ -335,7 +338,7 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) break; case kPidOversampling: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - int32 data = static_cast(kParamOversamplingRange.denormalize(value)); + int32 data = static_cast(range.denormalize(value)); _state.oversamplingLog2 = data; if (writeWorkerMessage("SetOversampling", &data, sizeof(data))) _semaToWorker.post(); @@ -343,7 +346,7 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) break; case kPidPreloadSize: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) { - int32 data = static_cast(kParamPreloadSizeRange.denormalize(value)); + int32 data = static_cast(range.denormalize(value)); _state.preloadSize = data; if (writeWorkerMessage("SetPreloadSize", &data, sizeof(data))) _semaToWorker.post(); @@ -351,15 +354,15 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc) break; case kPidScalaRootKey: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) - _state.scalaRootKey = static_cast(kParamScalaRootKeyRange.denormalize(value)); + _state.scalaRootKey = static_cast(range.denormalize(value)); break; case kPidTuningFrequency: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) - _state.tuningFrequency = kParamTuningFrequencyRange.denormalize(value); + _state.tuningFrequency = range.denormalize(value); break; case kPidStretchedTuning: if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) - _state.stretchedTuning = kParamStretchedTuningRange.denormalize(value); + _state.stretchedTuning = range.denormalize(value); break; } } diff --git a/vst/SfizzVstState.cpp b/vst/SfizzVstState.cpp index 1eaac01f..2c3a6d09 100644 --- a/vst/SfizzVstState.cpp +++ b/vst/SfizzVstState.cpp @@ -102,35 +102,3 @@ tresult SfizzVstState::store(IBStream* state) const } constexpr uint64 SfizzVstState::currentStateVersion; - -tresult SfizzUiState::load(IBStream* state) -{ - IBStreamer s(state, kLittleEndian); - - uint64 version = 0; - if (!s.readInt64u(version)) - return kResultFalse; - - if (!s.readInt32u(activePanel)) - return kResultFalse; - - if (version > 0) - return kResultFalse; - - return kResultTrue; -} - -tresult SfizzUiState::store(IBStream* state) const -{ - IBStreamer s(state, kLittleEndian); - - if (!s.writeInt64u(currentStateVersion)) - return kResultFalse; - - if (!s.writeInt32u(activePanel)) - return kResultFalse; - - return kResultTrue; -} - -constexpr uint64 SfizzUiState::currentStateVersion; diff --git a/vst/SfizzVstState.h b/vst/SfizzVstState.h index 531e2dac..de35acb8 100644 --- a/vst/SfizzVstState.h +++ b/vst/SfizzVstState.h @@ -6,32 +6,10 @@ #pragma once #include "base/source/fstreamer.h" -#include "public.sdk/source/vst/vstparameters.h" #include using namespace Steinberg; -// number of MIDI CC -enum { - kNumControllerParams = 128, -}; - -// parameters -enum { - kPidVolume, - kPidNumVoices, - kPidOversampling, - kPidPreloadSize, - kPidScalaRootKey, - kPidTuningFrequency, - kPidStretchedTuning, - kPidMidiAftertouch, - kPidMidiPitchBend, - kPidMidiCC0, - kPidMidiCCLast = kPidMidiCC0 + kNumControllerParams - 1, - /* Reserved */ -}; - class SfizzVstState { public: SfizzVstState() { sfzFile.reserve(8192); scalaFile.reserve(8192); } @@ -52,16 +30,6 @@ public: tresult store(IBStream* state) const; }; -class SfizzUiState { -public: - uint32 activePanel = 0; - - static constexpr uint64 currentStateVersion = 0; - - tresult load(IBStream* state); - tresult store(IBStream* state) const; -}; - struct SfizzPlayState { uint32 curves; uint32 masters; @@ -70,35 +38,3 @@ struct SfizzPlayState { uint32 preloadedSamples; uint32 activeVoices; }; - -struct SfizzParameterRange { - float def = 0.0; - float min = 0.0; - float max = 1.0; - - constexpr SfizzParameterRange() {} - constexpr SfizzParameterRange(float def, float min, float max) : def(def), min(min), max(max) {} - - constexpr float normalize(float x) const noexcept - { - return (x - min) / (max - min); - } - - constexpr float denormalize(float x) const noexcept - { - return min + x * (max - min); - } - - Vst::RangeParameter* createParameter(const Vst::TChar *title, Vst::ParamID tag, const Vst::TChar *units = nullptr, int32 stepCount = 0, int32 flags = Vst::ParameterInfo::kCanAutomate, Vst::UnitID unitID = Vst::kRootUnitId, const Vst::TChar *shortTitle = nullptr) const - { - return new Vst::RangeParameter(title, tag, units, min, max, def, stepCount, flags, unitID, shortTitle); - } -}; - -static constexpr SfizzParameterRange kParamVolumeRange(0.0, -60.0, +6.0); -static constexpr SfizzParameterRange kParamNumVoicesRange(64.0, 1.0, 256.0); -static constexpr SfizzParameterRange kParamOversamplingRange(0.0, 0.0, 3.0); -static constexpr SfizzParameterRange kParamPreloadSizeRange(8192.0, 1024.0, 65536.0); -static constexpr SfizzParameterRange kParamScalaRootKeyRange(60.0, 0.0, 127.0); -static constexpr SfizzParameterRange kParamTuningFrequencyRange(440.0, 300.0, 500.0); -static constexpr SfizzParameterRange kParamStretchedTuningRange(0.0, 0.0, 1.0); diff --git a/vst/SfizzVstUpdates.cpp b/vst/SfizzVstUpdates.cpp new file mode 100644 index 00000000..ab6c69f5 --- /dev/null +++ b/vst/SfizzVstUpdates.cpp @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "SfizzVstUpdates.h" +#include + +OSCUpdate::~OSCUpdate() +{ + clear(); +} + +void OSCUpdate::clear() +{ + if (allocated_) + delete[] reinterpret_cast(data_); + data_ = nullptr; + size_ = 0; + allocated_ = false; +} + +void OSCUpdate::setMessage(const void* data, uint32_t size, bool copy) +{ + clear(); + + if (copy) { + uint8_t *buffer = new uint8_t[size]; + std::memcpy(buffer, data, size); + data = buffer; + } + + data_ = data; + size_ = size; + allocated_ = copy; +} diff --git a/vst/SfizzVstUpdates.h b/vst/SfizzVstUpdates.h new file mode 100644 index 00000000..0837a047 --- /dev/null +++ b/vst/SfizzVstUpdates.h @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "SfizzVstState.h" +#include +#include +#include +#include +#include + +/** + * @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_; } + + 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; +}; + +/** + * @brief Update which notifies a change of file path pseudo-parameter + * The message ID is used to indicate which path it is. + */ +class FilePathUpdate : public Steinberg::FObject { +public: + explicit FilePathUpdate(int32 type) + : type_(type) + { + } + + int32 getType() const noexcept + { + return type_; + } + + void setPath(std::string newPath) + { + std::lock_guard lock(mutex_); + path_ = std::move(newPath); + } + + std::string getPath() const + { + std::lock_guard lock(mutex_); + return path_; + } + + OBJ_METHODS(FilePathUpdate, FObject) + +private: + int32 type_ {}; + std::string path_; + mutable std::mutex mutex_; +}; + +enum { + kFilePathUpdateSfz, + kFilePathUpdateScala, +}; + +/** + * @brief Update which indicates the processor status. + */ +class ProcessorStateUpdate : public Steinberg::FObject { +public: + void setState(SfizzVstState newState) + { + std::lock_guard lock(mutex_); + state_ = std::move(newState); + } + + SfizzVstState getState() const + { + std::lock_guard lock(mutex_); + return state_; + } + + template + void access(F&& fn) + { + std::lock_guard lock(mutex_); + fn(state_); + } + + OBJ_METHODS(ProcessorStateUpdate, FObject) + +private: + SfizzVstState state_; + mutable std::mutex mutex_; +}; + +/** + * @brief Update which indicates the playing SFZ status. + */ +class PlayStateUpdate : public Steinberg::FObject { +public: + void setState(SfizzPlayState newState) + { + std::lock_guard lock(mutex_); + state_ = std::move(newState); + } + + SfizzPlayState getState() const + { + std::lock_guard lock(mutex_); + return state_; + } + + OBJ_METHODS(PlayStateUpdate, FObject) + +private: + SfizzPlayState state_; + mutable std::mutex mutex_; +}; diff --git a/vst/WeakPtr.h b/vst/WeakPtr.h deleted file mode 100644 index 55481e35..00000000 --- a/vst/WeakPtr.h +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause - -// This code is part of the sfizz library and is licensed under a BSD 2-clause -// license. You should have receive a LICENSE.md file along with the code. -// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz - -#pragma once -#include "base/source/fobject.h" -#include -#include - -/** - * A weak reference implementation for Steinberg FObject. - * - * Implementation - * ============== - * - * This takes over the ordinary addRef() and release() methods. - * The variable `refCount` is accessed manually, under a shared mutex. - * There is a unique data block which is shared with all weak pointers, the - * system will null it atomically when the reference count hits zero. - * - * Usage - * ===== - * - * class MyObject : public FObject, public Weakable { - * [...] - * WEAKABLE_REFCOUNT_METHODS(MyObject) - * }; - * - * WeakPtr ptr = myObject.getWeakPtr(); - */ - -template -class Weakable; - -#define WEAKABLE_REFCOUNT_METHODS(T) \ -public: \ - Steinberg::uint32 PLUGIN_API addRef() SMTG_OVERRIDE { return weakAddRef(); } \ - Steinberg::uint32 PLUGIN_API release() SMTG_OVERRIDE { return weakRelease(); } \ -private: \ - friend class Weakable; \ - friend class WeakPtr; - -/// -template -struct WeakPtrSharedData : public std::enable_shared_from_this> { - explicit WeakPtrSharedData(T* self) : self_(self) {} - std::mutex mutex_; - T* self_ = nullptr; -}; - -/// -template -class WeakPtr { - friend class Weakable; - using SharedData = WeakPtrSharedData; - -public: - WeakPtr() = default; - - Steinberg::IPtr lock() - { - std::shared_ptr data = data_.lock(); - if (!data) - return nullptr; - std::lock_guard lock { data->mutex_ }; - T* self = data->self_; - if (self) - ++self->refCount; // manually because we are holding the lock - return Steinberg::IPtr(self, false); - } - -private: - explicit WeakPtr(std::weak_ptr data) : data_(data) {} - std::weak_ptr data_; -}; - -/// -template -class Weakable { - using SharedData = WeakPtrSharedData; - -public: - Weakable() - : weakData_(new SharedData(static_cast(this))) - { - } - - WeakPtr getWeakPtr() - { - return WeakPtr(weakData_); - } - -protected: - Steinberg::uint32 weakAddRef() //override - { - T* self = static_cast(this); - std::lock_guard lock { weakData_->mutex_ }; - return ++self->refCount; - } - - Steinberg::uint32 weakRelease() //override - { - T* self = static_cast(this); - std::shared_ptr data = weakData_; - std::unique_lock lock { data->mutex_ }; - Steinberg::uint32 count = --self->refCount; - if (count == 0) { - data->self_ = nullptr; - weakData_.reset(); - self->refCount = -1000; - lock.unlock(); - delete self; - return 0; - } - return count; - } - -private: - std::shared_ptr weakData_; -};