Add more thread-safety in VST

This commit is contained in:
Jean Pierre Cimalando 2020-12-07 09:41:36 +01:00
parent 5709fedb94
commit bea7195c65
4 changed files with 166 additions and 128 deletions

View file

@ -140,16 +140,20 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
return nullptr; return nullptr;
if (_editor) { if (_editor) {
_uiState = _editor->getCurrentUiState(); withStateLock([this]() {
_uiState = _editor->getCurrentUiState();
});
_editor.reset(); _editor.reset();
} }
SfizzVstEditor* editor = new SfizzVstEditor(this); SfizzVstEditor* editor = new SfizzVstEditor(this);
_editor = Steinberg::owned(editor); _editor = Steinberg::owned(editor);
editor->updateState(_state); withStateLock([this, editor]() {
editor->updateUiState(_uiState); editor->updateState(_state);
editor->updatePlayState(_playState); editor->updateUiState(_uiState);
editor->updatePlayState(_playState);
});
editor->remember(); editor->remember();
return editor; return editor;
@ -204,53 +208,61 @@ tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst:
} }
if (slotF32 && *slotF32 != value) { if (slotF32 && *slotF32 != value) {
*slotF32 = value; withStateLock([this, slotF32, value]() {
if (SfizzVstEditor* editor = _editor) *slotF32 = value;
editor->updateState(_state); if (SfizzVstEditor* editor = _editor)
editor->updateState(_state);
});
} }
else if (slotI32 && *slotI32 != (int32)value) { else if (slotI32 && *slotI32 != (int32)value) {
*slotI32 = (int32)value; withStateLock([this, slotI32, value]() {
if (SfizzVstEditor* editor = _editor) *slotI32 = (int32)value;
editor->updateState(_state); if (SfizzVstEditor* editor = _editor)
editor->updateState(_state);
});
} }
return kResultTrue; return kResultTrue;
} }
tresult PLUGIN_API SfizzVstController::setState(IBStream* state) tresult PLUGIN_API SfizzVstController::setState(IBStream* stream)
{ {
SfizzUiState s; SfizzUiState s;
tresult r = s.load(state); tresult r = s.load(stream);
if (r != kResultTrue) if (r != kResultTrue)
return r; return r;
_uiState = s; withStateLock([this, &s]() {
_uiState = s;
if (SfizzVstEditor* editor = _editor) if (SfizzVstEditor* editor = _editor)
editor->updateUiState(_uiState); editor->updateUiState(_uiState);
});
return kResultTrue; return kResultTrue;
} }
tresult PLUGIN_API SfizzVstController::getState(IBStream* state) tresult PLUGIN_API SfizzVstController::getState(IBStream* stream)
{ {
if (_editor) tresult result;
_uiState = _editor->getCurrentUiState();
return _uiState.store(state); 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; SfizzVstState s;
tresult r = s.load(state); tresult r = s.load(stream);
if (r != kResultTrue) if (r != kResultTrue)
return r; return r;
_state = s;
setParamNormalized(kPidVolume, kParamVolumeRange.normalize(s.volume)); setParamNormalized(kPidVolume, kParamVolumeRange.normalize(s.volume));
setParamNormalized(kPidNumVoices, kParamNumVoicesRange.normalize(s.numVoices)); setParamNormalized(kPidNumVoices, kParamNumVoicesRange.normalize(s.numVoices));
setParamNormalized(kPidOversampling, kParamOversamplingRange.normalize(s.oversamplingLog2)); setParamNormalized(kPidOversampling, kParamOversamplingRange.normalize(s.oversamplingLog2));
@ -259,14 +271,19 @@ tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* state)
setParamNormalized(kPidTuningFrequency, kParamTuningFrequencyRange.normalize(s.tuningFrequency)); setParamNormalized(kPidTuningFrequency, kParamTuningFrequencyRange.normalize(s.tuningFrequency));
setParamNormalized(kPidStretchedTuning, kParamStretchedTuningRange.normalize(s.stretchedTuning)); setParamNormalized(kPidStretchedTuning, kParamStretchedTuningRange.normalize(s.stretchedTuning));
if (SfizzVstEditor* editor = _editor) withStateLock([this, &s]() {
editor->updateState(_state); _state = s;
if (SfizzVstEditor* editor = _editor)
editor->updateState(_state);
});
return kResultTrue; return kResultTrue;
} }
tresult SfizzVstController::notify(Vst::IMessage* message) tresult SfizzVstController::notify(Vst::IMessage* message)
{ {
// Note: may be called from any thread (Reaper)
tresult result = SfizzVstControllerNoUi::notify(message); tresult result = SfizzVstControllerNoUi::notify(message);
if (result != kResultFalse) if (result != kResultFalse)
return result; return result;
@ -282,10 +299,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message)
if (result != kResultTrue) if (result != kResultTrue)
return result; return result;
_state.sfzFile.assign(static_cast<const char *>(data), size); withStateLock([this, data, size]() {
_state.sfzFile.assign(static_cast<const char *>(data), size);
if (SfizzVstEditor* editor = _editor) if (SfizzVstEditor* editor = _editor)
editor->updateState(_state); editor->updateState(_state);
});
} }
else if (!strcmp(id, "LoadedScala")) { else if (!strcmp(id, "LoadedScala")) {
const void* data = nullptr; const void* data = nullptr;
@ -295,10 +313,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message)
if (result != kResultTrue) if (result != kResultTrue)
return result; return result;
_state.scalaFile.assign(static_cast<const char *>(data), size); withStateLock([this, data, size]() {
_state.scalaFile.assign(static_cast<const char *>(data), size);
if (SfizzVstEditor* editor = _editor) if (SfizzVstEditor* editor = _editor)
editor->updateState(_state); editor->updateState(_state);
});
} }
else if (!strcmp(id, "NotifiedPlayState")) { else if (!strcmp(id, "NotifiedPlayState")) {
const void* data = nullptr; const void* data = nullptr;
@ -308,10 +327,11 @@ tresult SfizzVstController::notify(Vst::IMessage* message)
if (result != kResultTrue) if (result != kResultTrue)
return result; return result;
_playState = *static_cast<const SfizzPlayState*>(data); withStateLock([this, data]() {
_playState = *static_cast<const SfizzPlayState*>(data);
if (SfizzVstEditor* editor = _editor) if (SfizzVstEditor* editor = _editor)
editor->updatePlayState(_playState); editor->updatePlayState(_playState);
});
} }
else if (!strcmp(id, "ReceivedMessage")) { else if (!strcmp(id, "ReceivedMessage")) {
const void* data = nullptr; const void* data = nullptr;

View file

@ -10,6 +10,7 @@
#include "public.sdk/source/vst/vstparameters.h" #include "public.sdk/source/vst/vstparameters.h"
#include "vstgui/plugin-bindings/vst3editor.h" #include "vstgui/plugin-bindings/vst3editor.h"
#include <sfizz_message.h> #include <sfizz_message.h>
#include <mutex>
class SfizzVstState; class SfizzVstState;
class SfizzVstEditor; class SfizzVstEditor;
@ -43,9 +44,9 @@ public:
IPlugView* PLUGIN_API createView(FIDString name) override; IPlugView* PLUGIN_API createView(FIDString name) override;
tresult PLUGIN_API setParamNormalized(Vst::ParamID tag, Vst::ParamValue value) override; tresult PLUGIN_API setParamNormalized(Vst::ParamID tag, Vst::ParamValue value) override;
tresult PLUGIN_API setState(IBStream* state) override; tresult PLUGIN_API setState(IBStream* stream) override;
tresult PLUGIN_API getState(IBStream* state) override; tresult PLUGIN_API getState(IBStream* stream) override;
tresult PLUGIN_API setComponentState(IBStream* state) override; tresult PLUGIN_API setComponentState(IBStream* stream) override;
tresult PLUGIN_API notify(Vst::IMessage* message) override; tresult PLUGIN_API notify(Vst::IMessage* message) override;
/// ///
@ -54,8 +55,16 @@ public:
static FUID cid; static FUID cid;
private: private:
SfizzVstState _state; template <class F> void withStateLock(F&& fn) const
SfizzUiState _uiState; {
std::lock_guard<std::mutex> 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 {}; SfizzPlayState _playState {};
Steinberg::IPtr<SfizzVstEditor> _editor; Steinberg::IPtr<SfizzVstEditor> _editor;
}; };

View file

@ -25,7 +25,6 @@ SfizzVstEditor::SfizzVstEditor(SfizzVstController* controller)
: VSTGUIEditor(controller, &sfizzUiViewRect), : VSTGUIEditor(controller, &sfizzUiViewRect),
oscTemp_(new uint8_t[kOscTempSize]) oscTemp_(new uint8_t[kOscTempSize])
{ {
oscQueue_.reserve(kOscQueueSize);
} }
SfizzVstEditor::~SfizzVstEditor() SfizzVstEditor::~SfizzVstEditor()
@ -56,10 +55,14 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
editor_.reset(editor); editor_.reset(editor);
} }
mustRedisplayState_ = true; withStateLock([this]() {
mustRedisplayUiState_ = true; mustRedisplayState_ = true;
mustRedisplayPlayState_ = true; mustRedisplayUiState_ = true;
flushOscQueue(); mustRedisplayPlayState_ = true;
OscByteVec* queue = new OscByteVec;
oscQueue_.reset(queue);
queue->reserve(kOscQueueSize);
});
updateStateDisplay(); updateStateDisplay();
@ -86,7 +89,9 @@ void PLUGIN_API SfizzVstEditor::close()
this->frame = nullptr; this->frame = nullptr;
} }
flushOscQueue(); withStateLock([this]() {
oscQueue_.reset();
});
} }
/// ///
@ -121,71 +126,73 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
void SfizzVstEditor::updateState(const SfizzVstState& state) void SfizzVstEditor::updateState(const SfizzVstState& state)
{ {
std::lock_guard<std::recursive_mutex> lock(stateMutex_); withStateLock([this, &state]() {
state_ = state; state_ = state;
mustRedisplayState_ = true; mustRedisplayState_ = true;
});
} }
void SfizzVstEditor::updateUiState(const SfizzUiState& uiState) void SfizzVstEditor::updateUiState(const SfizzUiState& uiState)
{ {
std::lock_guard<std::recursive_mutex> lock(stateMutex_); withStateLock([this, &uiState]() {
uiState_ = uiState; uiState_ = uiState;
mustRedisplayUiState_ = true; mustRedisplayUiState_ = true;
});
} }
void SfizzVstEditor::updatePlayState(const SfizzPlayState& playState) void SfizzVstEditor::updatePlayState(const SfizzPlayState& playState)
{ {
std::lock_guard<std::recursive_mutex> lock(stateMutex_); withStateLock([this, &playState]() {
playState_ = playState; playState_ = playState;
mustRedisplayPlayState_ = true; mustRedisplayPlayState_ = true;
});
} }
SfizzUiState SfizzVstEditor::getCurrentUiState() const SfizzUiState SfizzVstEditor::getCurrentUiState() const
{ {
std::lock_guard<std::recursive_mutex> lock(stateMutex_); SfizzUiState uiState;
return uiState_; withStateLock([this, &uiState]() {
uiState = uiState_;
});
return uiState;
} }
void SfizzVstEditor::receiveMessage(const void* data, uint32_t size) void SfizzVstEditor::receiveMessage(const void* data, uint32_t size)
{ {
if (!frame) { // Note: may be called from non-UI thread (Reaper)
// only accumulate if message processing is active
return;
}
std::lock_guard<std::recursive_mutex> lock(stateMutex_); withStateLock([this, data, size]() {
std::copy( if (OscByteVec* queue = oscQueue_.get()) {
reinterpret_cast<const uint8_t*>(data), const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
reinterpret_cast<const uint8_t*>(data) + size, std::copy(bytes, bytes + size, std::back_inserter(*queue));
std::back_inserter(oscQueue_)); }
});
} }
void SfizzVstEditor::processOscQueue() void SfizzVstEditor::processOscQueue()
{ {
std::lock_guard<std::recursive_mutex> lock(stateMutex_); withStateLock([this]() {
OscByteVec* queue = oscQueue_.get();
if (!queue)
return;
const uint8_t* oscData = oscQueue_.data(); const uint8_t* oscData = queue->data();
size_t oscSize = oscQueue_.size(); size_t oscSize = queue->size();
const char* path; const char* path;
const char* sig; const char* sig;
const sfizz_arg_t* args; const sfizz_arg_t* args;
uint8_t buffer[1024]; uint8_t buffer[1024];
uint32_t msgSize; uint32_t msgSize;
while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { while ((msgSize = sfizz_extract_message(oscData, oscSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) {
uiReceiveMessage(path, sig, args); uiReceiveMessage(path, sig, args);
oscData += msgSize; oscData += msgSize;
oscSize -= msgSize; oscSize -= msgSize;
} }
oscQueue_.clear(); queue->clear();
} });
void SfizzVstEditor::flushOscQueue()
{
std::lock_guard<std::recursive_mutex> lock(stateMutex_);
oscQueue_.clear();
} }
/// ///
@ -333,41 +340,37 @@ void SfizzVstEditor::updateStateDisplay()
if (!frame) if (!frame)
return; return;
if (!(mustRedisplayState_ || mustRedisplayUiState_ || mustRedisplayPlayState_)) withStateLock([this]() {
return; 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;
}
std::lock_guard<std::recursive_mutex> lock(stateMutex_); ///
if (mustRedisplayUiState_) {
uiReceiveValue(EditId::UIActivePanel, uiState_.activePanel);
mustRedisplayUiState_ = false;
}
/// ///
if (mustRedisplayState_) { if (mustRedisplayPlayState_) {
uiReceiveValue(EditId::SfzFile, state_.sfzFile); uiReceiveValue(EditId::UINumCurves, playState_.curves);
uiReceiveValue(EditId::Volume, state_.volume); uiReceiveValue(EditId::UINumMasters, playState_.masters);
uiReceiveValue(EditId::Polyphony, state_.numVoices); uiReceiveValue(EditId::UINumGroups, playState_.groups);
uiReceiveValue(EditId::Oversampling, 1u << state_.oversamplingLog2); uiReceiveValue(EditId::UINumRegions, playState_.regions);
uiReceiveValue(EditId::PreloadSize, state_.preloadSize); uiReceiveValue(EditId::UINumPreloadedSamples, playState_.preloadedSamples);
uiReceiveValue(EditId::ScalaFile, state_.scalaFile); uiReceiveValue(EditId::UINumActiveVoices, playState_.activeVoices);
uiReceiveValue(EditId::ScalaRootKey, state_.scalaRootKey); mustRedisplayPlayState_ = false;
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) Vst::ParamID SfizzVstEditor::parameterOfEditId(EditId id)

View file

@ -43,7 +43,12 @@ public:
private: private:
void processOscQueue(); void processOscQueue();
void flushOscQueue();
template <class F> void withStateLock(F&& fn) const
{
std::lock_guard<std::recursive_mutex> lock(stateMutex_);
fn();
}
protected: protected:
// EditorController // EditorController
@ -72,12 +77,13 @@ private:
// editor state // editor state
// note: might be updated from a non-UI thread // note: might be updated from a non-UI thread
mutable std::recursive_mutex stateMutex_; mutable std::recursive_mutex stateMutex_; // for R/W the state data, and OSC queue
SfizzVstState state_; SfizzVstState state_ {};
SfizzUiState uiState_; SfizzUiState uiState_ {};
SfizzPlayState playState_; SfizzPlayState playState_ {};
volatile bool mustRedisplayState_ = false; volatile bool mustRedisplayState_ = false;
volatile bool mustRedisplayUiState_ = false; volatile bool mustRedisplayUiState_ = false;
volatile bool mustRedisplayPlayState_ = false; volatile bool mustRedisplayPlayState_ = false;
std::vector<uint8_t> oscQueue_; typedef std::vector<uint8_t> OscByteVec;
std::unique_ptr<OscByteVec> oscQueue_;
}; };