diff --git a/vst/SfizzVstController.cpp b/vst/SfizzVstController.cpp index 970f3f1a..2e6854f1 100644 --- a/vst/SfizzVstController.cpp +++ b/vst/SfizzVstController.cpp @@ -139,7 +139,24 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name) if (name != Vst::ViewType::kEditor) return nullptr; - return new SfizzVstEditor(this); + if (_editor) { + withStateLock([this]() { + _uiState = _editor->getCurrentUiState(); + }); + _editor.reset(); + } + + SfizzVstEditor* editor = new SfizzVstEditor(this); + _editor = Steinberg::owned(editor); + + withStateLock([this, editor]() { + editor->updateState(_state); + editor->updateUiState(_uiState); + editor->updatePlayState(_playState); + }); + + editor->remember(); + return editor; } tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst::ParamValue normValue) @@ -190,56 +207,62 @@ tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst: } } - bool update = false; - if (slotF32 && *slotF32 != value) { - *slotF32 = value; - update = true; + withStateLock([this, slotF32, value]() { + *slotF32 = value; + if (SfizzVstEditor* editor = _editor) + editor->updateState(_state); + }); } else if (slotI32 && *slotI32 != (int32)value) { - *slotI32 = (int32)value; - update = true; - } - - if (update) { - for (StateListener* listener : _stateListeners) - listener->onStateChanged(); + withStateLock([this, slotI32, value]() { + *slotI32 = (int32)value; + if (SfizzVstEditor* editor = _editor) + editor->updateState(_state); + }); } return kResultTrue; } -tresult PLUGIN_API SfizzVstController::setState(IBStream* state) +tresult PLUGIN_API SfizzVstController::setState(IBStream* stream) { SfizzUiState s; - tresult r = s.load(state); + tresult r = s.load(stream); if (r != kResultTrue) return r; - _uiState = s; - - for (StateListener* listener : _stateListeners) - listener->onStateChanged(); + withStateLock([this, &s]() { + _uiState = s; + if (SfizzVstEditor* editor = _editor) + editor->updateUiState(_uiState); + }); return kResultTrue; } -tresult PLUGIN_API SfizzVstController::getState(IBStream* state) +tresult PLUGIN_API SfizzVstController::getState(IBStream* stream) { - return _uiState.store(state); + tresult result; + + withStateLock([this, stream, &result]() { + if (_editor) + _uiState = _editor->getCurrentUiState(); + result = _uiState.store(stream); + }); + + return result; } -tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* state) +tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* stream) { SfizzVstState s; - tresult r = s.load(state); + tresult r = s.load(stream); if (r != kResultTrue) return r; - _state = s; - setParamNormalized(kPidVolume, kParamVolumeRange.normalize(s.volume)); setParamNormalized(kPidNumVoices, kParamNumVoicesRange.normalize(s.numVoices)); setParamNormalized(kPidOversampling, kParamOversamplingRange.normalize(s.oversamplingLog2)); @@ -248,14 +271,19 @@ tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* state) setParamNormalized(kPidTuningFrequency, kParamTuningFrequencyRange.normalize(s.tuningFrequency)); setParamNormalized(kPidStretchedTuning, kParamStretchedTuningRange.normalize(s.stretchedTuning)); - for (StateListener* listener : _stateListeners) - listener->onStateChanged(); + withStateLock([this, &s]() { + _state = s; + if (SfizzVstEditor* editor = _editor) + editor->updateState(_state); + }); return kResultTrue; } tresult SfizzVstController::notify(Vst::IMessage* message) { + // Note: may be called from any thread (Reaper) + tresult result = SfizzVstControllerNoUi::notify(message); if (result != kResultFalse) return result; @@ -271,7 +299,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - _state.sfzFile.assign(static_cast(data), size); + withStateLock([this, data, size]() { + _state.sfzFile.assign(static_cast(data), size); + if (SfizzVstEditor* editor = _editor) + editor->updateState(_state); + }); } else if (!strcmp(id, "LoadedScala")) { const void* data = nullptr; @@ -281,7 +313,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - _state.scalaFile.assign(static_cast(data), size); + withStateLock([this, data, size]() { + _state.scalaFile.assign(static_cast(data), size); + if (SfizzVstEditor* editor = _editor) + editor->updateState(_state); + }); } else if (!strcmp(id, "NotifiedPlayState")) { const void* data = nullptr; @@ -291,7 +327,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - _playState = *static_cast(data); + withStateLock([this, data]() { + _playState = *static_cast(data); + if (SfizzVstEditor* editor = _editor) + editor->updatePlayState(_playState); + }); } else if (!strcmp(id, "ReceivedMessage")) { const void* data = nullptr; @@ -301,47 +341,13 @@ tresult SfizzVstController::notify(Vst::IMessage* message) if (result != kResultTrue) return result; - const char* path; - const char* sig; - const sfizz_arg_t* args; - uint8_t buffer[1024]; - - if (sfizz_extract_message(data, size, buffer, sizeof(buffer), &path, &sig, &args) > 0) { - for (MessageListener* listener : _messageListeners) - listener->onMessageReceived(path, sig, args); - } + if (SfizzVstEditor* editor = _editor) + editor->receiveMessage(data, size); } - for (StateListener* listener : _stateListeners) - listener->onStateChanged(); - return result; } -void SfizzVstController::addSfizzStateListener(StateListener* listener) -{ - _stateListeners.push_back(listener); -} - -void SfizzVstController::removeSfizzStateListener(StateListener* listener) -{ - auto it = std::find(_stateListeners.begin(), _stateListeners.end(), listener); - if (it != _stateListeners.end()) - _stateListeners.erase(it); -} - -void SfizzVstController::addSfizzMessageListener(MessageListener* listener) -{ - _messageListeners.push_back(listener); -} - -void SfizzVstController::removeSfizzMessageListener(MessageListener* listener) -{ - auto it = std::find(_messageListeners.begin(), _messageListeners.end(), listener); - if (it != _messageListeners.end()) - _messageListeners.erase(it); -} - FUnknown* SfizzVstController::createInstance(void*) { return static_cast(new SfizzVstController); diff --git a/vst/SfizzVstController.h b/vst/SfizzVstController.h index 796d6628..9e5ba8d4 100644 --- a/vst/SfizzVstController.h +++ b/vst/SfizzVstController.h @@ -10,7 +10,9 @@ #include "public.sdk/source/vst/vstparameters.h" #include "vstgui/plugin-bindings/vst3editor.h" #include +#include class SfizzVstState; +class SfizzVstEditor; using namespace Steinberg; using namespace VSTGUI; @@ -42,42 +44,27 @@ public: IPlugView* PLUGIN_API createView(FIDString name) override; tresult PLUGIN_API setParamNormalized(Vst::ParamID tag, Vst::ParamValue value) override; - tresult PLUGIN_API setState(IBStream* state) override; - tresult PLUGIN_API getState(IBStream* state) override; - tresult PLUGIN_API setComponentState(IBStream* state) 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; - struct StateListener { - virtual void onStateChanged() = 0; - }; - struct MessageListener { - virtual void onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) = 0; - }; - - const SfizzVstState& getSfizzState() const { return _state; } - SfizzVstState& getSfizzState() { return _state; } - - const SfizzUiState& getSfizzUiState() const { return _uiState; } - SfizzUiState& getSfizzUiState() { return _uiState; } - - const SfizzPlayState& getSfizzPlayState() const { return _playState; } - SfizzPlayState& getSfizzPlayState() { return _playState; } - - void addSfizzStateListener(StateListener* listener); - void removeSfizzStateListener(StateListener* listener); - - void addSfizzMessageListener(MessageListener* listener); - void removeSfizzMessageListener(MessageListener* listener); - /// static FUnknown* createInstance(void*); static FUID cid; private: - SfizzVstState _state; - SfizzUiState _uiState; + 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 {}; - std::vector _stateListeners; - std::vector _messageListeners; + Steinberg::IPtr _editor; }; diff --git a/vst/SfizzVstEditor.cpp b/vst/SfizzVstEditor.cpp index e972b9bf..c04b5d1d 100644 --- a/vst/SfizzVstEditor.cpp +++ b/vst/SfizzVstEditor.cpp @@ -18,20 +18,17 @@ static ViewRect sfizzUiViewRect { 0, 0, Editor::viewWidth, Editor::viewHeight }; enum { kOscTempSize = 8192, + kOscQueueSize = 65536, }; -SfizzVstEditor::SfizzVstEditor(void *controller) +SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller) : VSTGUIEditor(controller, &sfizzUiViewRect), oscTemp_(new uint8_t[kOscTempSize]) { - getController()->addSfizzStateListener(this); - getController()->addSfizzMessageListener(this); } SfizzVstEditor::~SfizzVstEditor() { - getController()->removeSfizzStateListener(this); - getController()->removeSfizzMessageListener(this); } bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType) @@ -57,6 +54,16 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p editor = new Editor(*this); editor_.reset(editor); } + + withStateLock([this]() { + mustRedisplayState_ = true; + mustRedisplayUiState_ = true; + mustRedisplayPlayState_ = true; + OscByteVec* queue = new OscByteVec; + oscQueue_.reset(queue); + queue->reserve(kOscQueueSize); + }); + updateStateDisplay(); if (!frame->open(parent, platformType, config)) { @@ -81,6 +88,10 @@ void PLUGIN_API SfizzVstEditor::close() frame->close(); this->frame = nullptr; } + + withStateLock([this]() { + oscQueue_.reset(); + }); } /// @@ -105,17 +116,83 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message) } #endif + if (message == CVSTGUITimer::kMsgTimer) { + processOscQueue(); + updateStateDisplay(); + } + return result; } -void SfizzVstEditor::onStateChanged() +void SfizzVstEditor::updateState(const SfizzVstState& state) { - updateStateDisplay(); + withStateLock([this, &state]() { + state_ = state; + mustRedisplayState_ = true; + }); } -void SfizzVstEditor::onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) +void SfizzVstEditor::updateUiState(const SfizzUiState& uiState) { - uiReceiveMessage(path, sig, args); + 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)); + } + }); +} + +void SfizzVstEditor::processOscQueue() +{ + withStateLock([this]() { + OscByteVec* queue = oscQueue_.get(); + if (!queue) + return; + + const uint8_t* oscData = queue->data(); + size_t oscSize = queue->size(); + + const char* path; + const char* sig; + const sfizz_arg_t* args; + uint8_t buffer[1024]; + + uint32_t msgSize; + while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { + uiReceiveMessage(path, sig, args); + oscData += msgSize; + oscSize -= msgSize; + } + + queue->clear(); + }); } /// @@ -166,7 +243,7 @@ void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v) break; case EditId::UIActivePanel: - ctrl->getSfizzUiState().activePanel = static_cast(v.to_float()); + uiState_.activePanel = static_cast(v.to_float()); break; default: @@ -263,32 +340,37 @@ void SfizzVstEditor::updateStateDisplay() if (!frame) return; - SfizzVstController* controller = getController(); - const SfizzVstState& state = controller->getSfizzState(); - const SfizzUiState& uiState = controller->getSfizzUiState(); - const SfizzPlayState& playState = controller->getSfizzPlayState(); + 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; + } - /// - 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 (mustRedisplayUiState_) { + uiReceiveValue(EditId::UIActivePanel, uiState_.activePanel); + mustRedisplayUiState_ = false; + } - /// - 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); - - /// - uiReceiveValue(EditId::UIActivePanel, uiState.activePanel); + /// + 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) diff --git a/vst/SfizzVstEditor.h b/vst/SfizzVstEditor.h index ad45f352..e5fa0d8d 100644 --- a/vst/SfizzVstEditor.h +++ b/vst/SfizzVstEditor.h @@ -8,6 +8,7 @@ #include "SfizzVstController.h" #include "editor/EditorController.h" #include "public.sdk/source/vst/vstguieditor.h" +#include class Editor; #if !defined(__APPLE__) && !defined(_WIN32) namespace VSTGUI { class RunLoop; } @@ -17,11 +18,9 @@ using namespace Steinberg; using namespace VSTGUI; class SfizzVstEditor : public Vst::VSTGUIEditor, - public SfizzVstController::StateListener, - public SfizzVstController::MessageListener, public EditorController { public: - explicit SfizzVstEditor(void *controller); + explicit SfizzVstEditor(SfizzVstController* controller); ~SfizzVstEditor(); bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType = VSTGUI::kDefaultNative) override; @@ -35,11 +34,21 @@ public: // VSTGUIEditor CMessageResult notify(CBaseObject* sender, const char* message) override; - // SfizzVstController::StateListener - void onStateChanged() 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); - // SfizzVstController::MessageListener - void onMessageReceived(const char* path, const char* sig, const sfizz_arg_t* args) override; +private: + void processOscQueue(); + + template void withStateLock(F&& fn) const + { + std::lock_guard lock(stateMutex_); + fn(); + } protected: // EditorController @@ -65,4 +74,16 @@ private: // messaging std::unique_ptr oscTemp_; + + // 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_; }; diff --git a/vst/SfizzVstProcessor.cpp b/vst/SfizzVstProcessor.cpp index 93969467..77de7c43 100644 --- a/vst/SfizzVstProcessor.cpp +++ b/vst/SfizzVstProcessor.cpp @@ -558,8 +558,10 @@ void SfizzVstProcessor::receiveMessage(int delay, const char* path, const char* { uint8_t* oscTemp = _oscTemp.get(); uint32_t oscSize = sfizz_prepare_message(oscTemp, kOscTempSize, path, sig, args); - if (oscSize <= kOscTempSize) - writeWorkerMessage("ReceiveMessage", oscTemp, oscSize); + if (oscSize <= kOscTempSize) { + if (writeWorkerMessage("ReceiveMessage", oscTemp, oscSize)) + _semaToWorker.post(); + } } void SfizzVstProcessor::loadSfzFileOrDefault(sfz::Sfizz& synth, const std::string& filePath)