Thread-safety for receiving parameters
This commit is contained in:
parent
7ca9c15a28
commit
9580ca84d5
2 changed files with 97 additions and 43 deletions
|
|
@ -18,6 +18,7 @@
|
||||||
#include "X11RunLoop.h"
|
#include "X11RunLoop.h"
|
||||||
#endif
|
#endif
|
||||||
#include <ghc/fs_std.hpp>
|
#include <ghc/fs_std.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
using namespace VSTGUI;
|
using namespace VSTGUI;
|
||||||
|
|
||||||
|
|
@ -93,6 +94,10 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
||||||
for (FObject* update : triggerUpdates_)
|
for (FObject* update : triggerUpdates_)
|
||||||
update->addDependent(this);
|
update->addDependent(this);
|
||||||
|
|
||||||
|
threadChecker_ = Vst::ThreadChecker::create();
|
||||||
|
|
||||||
|
parametersToUpdate_.clear();
|
||||||
|
|
||||||
Steinberg::IdleUpdateHandler::start();
|
Steinberg::IdleUpdateHandler::start();
|
||||||
|
|
||||||
for (FObject* update : continuousUpdates_)
|
for (FObject* update : continuousUpdates_)
|
||||||
|
|
@ -192,6 +197,7 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
||||||
if (message == CVSTGUITimer::kMsgTimer) {
|
if (message == CVSTGUITimer::kMsgTimer) {
|
||||||
processOscQueue();
|
processOscQueue();
|
||||||
processNoteEventQueue();
|
processNoteEventQueue();
|
||||||
|
processParameterUpdates();
|
||||||
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
|
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,49 +284,18 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
|
if (Vst::RangeParameter* param = Steinberg::FCast<Vst::RangeParameter>(changedUnknown)) {
|
||||||
const Vst::ParamValue value = param->getNormalized();
|
// Note(jpc) some hosts send us the parameters in the wrong thread...
|
||||||
const Vst::ParamID id = param->getInfo().id;
|
// store these parameters thread-safely and let the idle
|
||||||
const SfizzRange range = SfizzRange::getForParameter(id);
|
// callback process them later
|
||||||
switch (id) {
|
if (threadChecker_->test())
|
||||||
case kPidVolume:
|
updateParameter(param);
|
||||||
uiReceiveValue(EditId::Volume, range.denormalize(value));
|
else {
|
||||||
break;
|
static std::atomic_bool warn_once_flag { false };
|
||||||
case kPidNumVoices:
|
if (!warn_once_flag.exchange(true))
|
||||||
uiReceiveValue(EditId::Polyphony, range.denormalize(value));
|
fprintf(stderr, "[sfizz] using a thread-safety workaround for parameter updates\n");
|
||||||
break;
|
const Vst::ParamID id = param->getInfo().id;
|
||||||
case kPidOversampling:
|
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
|
||||||
uiReceiveValue(EditId::Oversampling, float(1u << (int32)range.denormalize(value)));
|
parametersToUpdate_.insert(id);
|
||||||
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;
|
|
||||||
case kPidSampleQuality:
|
|
||||||
uiReceiveValue(EditId::SampleQuality, range.denormalize(value));
|
|
||||||
break;
|
|
||||||
case kPidOscillatorQuality:
|
|
||||||
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
|
|
||||||
break;
|
|
||||||
case kPidLeftLevel:
|
|
||||||
uiReceiveValue(EditId::LeftLevel, range.denormalize(value));
|
|
||||||
break;
|
|
||||||
case kPidRightLevel:
|
|
||||||
uiReceiveValue(EditId::RightLevel, range.denormalize(value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
|
||||||
int cc = int(id - kPidCC0);
|
|
||||||
uiReceiveValue(editIdForCC(cc), range.denormalize(value));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -368,6 +343,73 @@ void SfizzVstEditor::processNoteEventQueue()
|
||||||
queue->clear();
|
queue->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SfizzVstEditor::processParameterUpdates()
|
||||||
|
{
|
||||||
|
auto extractNextParamID = [this]() -> Vst::ParamID {
|
||||||
|
Vst::ParamID id = Vst::kNoParamId;
|
||||||
|
std::lock_guard<std::mutex> lock(parametersToUpdateMutex_);
|
||||||
|
auto it = parametersToUpdate_.begin();
|
||||||
|
if (it != parametersToUpdate_.end()) {
|
||||||
|
id = *it;
|
||||||
|
parametersToUpdate_.erase(it);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (Vst::ParamID id; (id = extractNextParamID()) != Vst::kNoParamId; )
|
||||||
|
updateParameter(getController()->getParameterObject(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SfizzVstEditor::updateParameter(Vst::Parameter* parameterToUpdate)
|
||||||
|
{
|
||||||
|
if (Vst::RangeParameter* param = FCast<Vst::RangeParameter>(parameterToUpdate)) {
|
||||||
|
const Vst::ParamID id = param->getInfo().id;
|
||||||
|
const Vst::ParamValue value = param->getNormalized();
|
||||||
|
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, float(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;
|
||||||
|
case kPidSampleQuality:
|
||||||
|
uiReceiveValue(EditId::SampleQuality, range.denormalize(value));
|
||||||
|
break;
|
||||||
|
case kPidOscillatorQuality:
|
||||||
|
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
|
||||||
|
break;
|
||||||
|
case kPidLeftLevel:
|
||||||
|
uiReceiveValue(EditId::LeftLevel, range.denormalize(value));
|
||||||
|
break;
|
||||||
|
case kPidRightLevel:
|
||||||
|
uiReceiveValue(EditId::RightLevel, range.denormalize(value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||||
|
int cc = int(id - kPidCC0);
|
||||||
|
uiReceiveValue(editIdForCC(cc), range.denormalize(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
|
void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
#include "SfizzVstController.h"
|
#include "SfizzVstController.h"
|
||||||
#include "editor/EditorController.h"
|
#include "editor/EditorController.h"
|
||||||
#include "public.sdk/source/vst/vstguieditor.h"
|
#include "public.sdk/source/vst/vstguieditor.h"
|
||||||
|
#include "public.sdk/source/common/threadchecker.h"
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
class Editor;
|
class Editor;
|
||||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||||
namespace VSTGUI { class RunLoop; }
|
namespace VSTGUI { class RunLoop; }
|
||||||
|
|
@ -48,6 +50,8 @@ public:
|
||||||
private:
|
private:
|
||||||
void processOscQueue();
|
void processOscQueue();
|
||||||
void processNoteEventQueue();
|
void processNoteEventQueue();
|
||||||
|
void processParameterUpdates();
|
||||||
|
void updateParameter(Vst::Parameter* parameterToUpdate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// EditorController
|
// EditorController
|
||||||
|
|
@ -84,4 +88,12 @@ private:
|
||||||
// subscribed updates
|
// subscribed updates
|
||||||
std::vector<IPtr<FObject>> continuousUpdates_;
|
std::vector<IPtr<FObject>> continuousUpdates_;
|
||||||
std::vector<IPtr<FObject>> triggerUpdates_;
|
std::vector<IPtr<FObject>> triggerUpdates_;
|
||||||
|
|
||||||
|
// thread safety
|
||||||
|
std::unique_ptr<Vst::ThreadChecker> threadChecker_;
|
||||||
|
|
||||||
|
// parameters to process, whose values have received changes
|
||||||
|
// Note(jpc) it's because hosts send us parameter updates in the wrong thread..
|
||||||
|
std::set<Vst::ParamID> parametersToUpdate_;
|
||||||
|
std::mutex parametersToUpdateMutex_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue