Merge pull request #628 from jpcima/new-vst
Refactor VST with UpdateHandler
This commit is contained in:
commit
324ee4b8ac
13 changed files with 607 additions and 563 deletions
|
|
@ -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
|
||||
|
|
|
|||
47
vst/IdleUpdateHandler.h
Normal file
47
vst/IdleUpdateHandler.h
Normal file
|
|
@ -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 <vstgui/lib/cvstguitimer.h>
|
||||
|
||||
/// @cond ignore
|
||||
namespace Steinberg {
|
||||
|
||||
class IdleUpdateHandler
|
||||
{
|
||||
public:
|
||||
static void start ()
|
||||
{
|
||||
auto& instance = get ();
|
||||
if (++instance.users == 1)
|
||||
{
|
||||
instance.timer = VSTGUI::makeOwned<VSTGUI::CVSTGUITimer> (
|
||||
[] (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<VSTGUI::CVSTGUITimer> timer;
|
||||
std::atomic<uint32_t> users {0};
|
||||
};
|
||||
|
||||
} // namespace Steinberg
|
||||
/// @endcond ignore
|
||||
|
|
@ -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<int>(kParamOversamplingRange.denormalize(valueNormalized));
|
||||
const SfizzRange range = SfizzRange::getForParameter(tag);
|
||||
const int factorLog2 = static_cast<int>(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<SfizzVstEditor> editor = _editor.lock()) {
|
||||
withStateLock([this, editor]() {
|
||||
_uiState = editor->getCurrentUiState();
|
||||
});
|
||||
}
|
||||
|
||||
IPtr<SfizzVstEditor> 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<SfizzVstEditor> editor = _editor.lock())
|
||||
editor->updateState(_state);
|
||||
});
|
||||
}
|
||||
else if (slotI32 && *slotI32 != (int32)value) {
|
||||
withStateLock([this, slotI32, value]() {
|
||||
*slotI32 = (int32)value;
|
||||
if (IPtr<SfizzVstEditor> 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<SfizzVstEditor> 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<SfizzVstEditor> 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<SfizzVstEditor> 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<const char *>(data), size);
|
||||
if (IPtr<SfizzVstEditor> editor = _editor.lock())
|
||||
editor->updateState(_state);
|
||||
std::string sfzFile(static_cast<const char *>(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<const char *>(data), size);
|
||||
if (IPtr<SfizzVstEditor> editor = _editor.lock())
|
||||
editor->updateState(_state);
|
||||
std::string scalaFile(static_cast<const char *>(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<const SfizzPlayState*>(data);
|
||||
if (IPtr<SfizzVstEditor> editor = _editor.lock())
|
||||
editor->updatePlayState(_playState);
|
||||
});
|
||||
playStateUpdate_->setState(*static_cast<const SfizzPlayState*>(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<SfizzVstEditor> 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<FObject*> 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<FObject*> triggerUpdates;
|
||||
triggerUpdates.push_back(oscUpdate_);
|
||||
|
||||
IPtr<SfizzVstEditor> editor = Steinberg::owned(
|
||||
new SfizzVstEditor(this, absl::MakeSpan(continuousUpdates), absl::MakeSpan(triggerUpdates)));
|
||||
|
||||
editor->remember();
|
||||
return editor;
|
||||
}
|
||||
|
||||
FUnknown* SfizzVstController::createInstance(void*)
|
||||
{
|
||||
return static_cast<Vst::IEditController*>(new SfizzVstController);
|
||||
|
|
|
|||
|
|
@ -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 <sfizz_message.h>
|
||||
#include <mutex>
|
||||
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> oscUpdate_;
|
||||
Steinberg::IPtr<FilePathUpdate> sfzPathUpdate_;
|
||||
Steinberg::IPtr<FilePathUpdate> scalaPathUpdate_;
|
||||
Steinberg::IPtr<ProcessorStateUpdate> processorStateUpdate_;
|
||||
Steinberg::IPtr<PlayStateUpdate> 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 <class F> void withStateLock(F&& fn) const
|
||||
{
|
||||
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 {};
|
||||
WeakPtr<SfizzVstEditor> _editor;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> 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<std::mutex> 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<fs::path> 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<std::mutex> 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<const uint8_t*>(data);
|
||||
std::copy(bytes, bytes + size, std::back_inserter(*queue));
|
||||
if (OSCUpdate* update = FCast<OSCUpdate>(changedUnknown)) {
|
||||
// this update is synchronous: may happen from non-UI thread
|
||||
uint32 size = update->size();
|
||||
if (size > 0) {
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(update->data());
|
||||
std::lock_guard<std::mutex> lock(oscQueueMutex_);
|
||||
if (OscByteVec* queue = oscQueue_.get())
|
||||
std::copy(bytes, bytes + size, std::back_inserter(*queue));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (FilePathUpdate* update = FCast<FilePathUpdate>(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<ProcessorStateUpdate>(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<PlayStateUpdate>(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<Vst::RangeParameter>(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<std::mutex> 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<int32>(v.to_float());
|
||||
|
||||
int32 log2Value = 0;
|
||||
for (int32 f = value; f > 1; f /= 2)
|
||||
++log2Value;
|
||||
|
||||
normalizeAndSet(kPidOversampling, kParamOversamplingRange, log2Value);
|
||||
const int32 factor = static_cast<int32>(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<int32>(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) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
#pragma once
|
||||
#include "SfizzVstController.h"
|
||||
#include "editor/EditorController.h"
|
||||
#include "WeakPtr.h"
|
||||
#include "public.sdk/source/vst/vstguieditor.h"
|
||||
#include <absl/types/span.h>
|
||||
#include <mutex>
|
||||
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<SfizzVstEditor> {
|
||||
public EditorController {
|
||||
public:
|
||||
using Self = SfizzVstEditor;
|
||||
|
||||
explicit SfizzVstEditor(SfizzVstController* controller);
|
||||
SfizzVstEditor(
|
||||
SfizzVstController* controller,
|
||||
absl::Span<FObject*> continuousUpdates,
|
||||
absl::Span<FObject*> 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 <class F> void withStateLock(F&& fn) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> 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> 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<uint8_t> OscByteVec;
|
||||
std::unique_ptr<OscByteVec> oscQueue_;
|
||||
std::mutex oscQueueMutex_;
|
||||
|
||||
// subscribed updates
|
||||
std::vector<IPtr<FObject>> continuousUpdates_;
|
||||
std::vector<IPtr<FObject>> triggerUpdates_;
|
||||
};
|
||||
|
|
|
|||
91
vst/SfizzVstParameters.h
Normal file
91
vst/SfizzVstParameters.h
Normal file
|
|
@ -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 <stdexcept>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -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<int32>(kParamNumVoicesRange.denormalize(value));
|
||||
int32 data = static_cast<int32>(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<int32>(kParamOversamplingRange.denormalize(value));
|
||||
int32 data = static_cast<int32>(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<int32>(kParamPreloadSizeRange.denormalize(value));
|
||||
int32 data = static_cast<int32>(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<int32>(kParamScalaRootKeyRange.denormalize(value));
|
||||
_state.scalaRootKey = static_cast<int32>(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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -6,32 +6,10 @@
|
|||
|
||||
#pragma once
|
||||
#include "base/source/fstreamer.h"
|
||||
#include "public.sdk/source/vst/vstparameters.h"
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
|
|
|
|||
37
vst/SfizzVstUpdates.cpp
Normal file
37
vst/SfizzVstUpdates.cpp
Normal file
|
|
@ -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 <cstring>
|
||||
|
||||
OSCUpdate::~OSCUpdate()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void OSCUpdate::clear()
|
||||
{
|
||||
if (allocated_)
|
||||
delete[] reinterpret_cast<const uint8_t*>(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;
|
||||
}
|
||||
136
vst/SfizzVstUpdates.h
Normal file
136
vst/SfizzVstUpdates.h
Normal file
|
|
@ -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 <base/source/fobject.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @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<std::mutex> lock(mutex_);
|
||||
path_ = std::move(newPath);
|
||||
}
|
||||
|
||||
std::string getPath() const
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
|
||||
state_ = std::move(newState);
|
||||
}
|
||||
|
||||
SfizzVstState getState() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return state_;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void access(F&& fn)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex_);
|
||||
state_ = std::move(newState);
|
||||
}
|
||||
|
||||
SfizzPlayState getState() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return state_;
|
||||
}
|
||||
|
||||
OBJ_METHODS(PlayStateUpdate, FObject)
|
||||
|
||||
private:
|
||||
SfizzPlayState state_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
122
vst/WeakPtr.h
122
vst/WeakPtr.h
|
|
@ -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 <memory>
|
||||
#include <mutex>
|
||||
|
||||
/**
|
||||
* 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<MyObject> {
|
||||
* [...]
|
||||
* WEAKABLE_REFCOUNT_METHODS(MyObject)
|
||||
* };
|
||||
*
|
||||
* WeakPtr<MyObject> ptr = myObject.getWeakPtr();
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
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<T>; \
|
||||
friend class WeakPtr<T>;
|
||||
|
||||
///
|
||||
template <class T>
|
||||
struct WeakPtrSharedData : public std::enable_shared_from_this<WeakPtrSharedData<T>> {
|
||||
explicit WeakPtrSharedData(T* self) : self_(self) {}
|
||||
std::mutex mutex_;
|
||||
T* self_ = nullptr;
|
||||
};
|
||||
|
||||
///
|
||||
template <class T>
|
||||
class WeakPtr {
|
||||
friend class Weakable<T>;
|
||||
using SharedData = WeakPtrSharedData<T>;
|
||||
|
||||
public:
|
||||
WeakPtr() = default;
|
||||
|
||||
Steinberg::IPtr<T> lock()
|
||||
{
|
||||
std::shared_ptr<SharedData> data = data_.lock();
|
||||
if (!data)
|
||||
return nullptr;
|
||||
std::lock_guard<std::mutex> lock { data->mutex_ };
|
||||
T* self = data->self_;
|
||||
if (self)
|
||||
++self->refCount; // manually because we are holding the lock
|
||||
return Steinberg::IPtr<T>(self, false);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit WeakPtr(std::weak_ptr<SharedData> data) : data_(data) {}
|
||||
std::weak_ptr<SharedData> data_;
|
||||
};
|
||||
|
||||
///
|
||||
template <class T>
|
||||
class Weakable {
|
||||
using SharedData = WeakPtrSharedData<T>;
|
||||
|
||||
public:
|
||||
Weakable()
|
||||
: weakData_(new SharedData(static_cast<T*>(this)))
|
||||
{
|
||||
}
|
||||
|
||||
WeakPtr<T> getWeakPtr()
|
||||
{
|
||||
return WeakPtr<T>(weakData_);
|
||||
}
|
||||
|
||||
protected:
|
||||
Steinberg::uint32 weakAddRef() //override
|
||||
{
|
||||
T* self = static_cast<T*>(this);
|
||||
std::lock_guard<std::mutex> lock { weakData_->mutex_ };
|
||||
return ++self->refCount;
|
||||
}
|
||||
|
||||
Steinberg::uint32 weakRelease() //override
|
||||
{
|
||||
T* self = static_cast<T*>(this);
|
||||
std::shared_ptr<SharedData> data = weakData_;
|
||||
std::unique_lock<std::mutex> 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<SharedData> weakData_;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue