Add other parameter: volume + the UI
This commit is contained in:
parent
092b95fc3c
commit
d8b1a79fe7
11 changed files with 515 additions and 135 deletions
|
|
@ -40,6 +40,7 @@ add_library(${VSTPLUGIN_PRJ_NAME} MODULE
|
|||
SfizzVstController.cpp
|
||||
SfizzVstEditor.cpp
|
||||
SfizzVstState.cpp
|
||||
GUIComponents.cpp
|
||||
VstPluginFactory.cpp)
|
||||
target_link_libraries(${VSTPLUGIN_PRJ_NAME}
|
||||
PRIVATE ${PROJECT_NAME}::${PROJECT_NAME})
|
||||
|
|
|
|||
33
vst/GUIComponents.cpp
Normal file
33
vst/GUIComponents.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// 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 "GUIComponents.h"
|
||||
#include "vstgui/lib/cdrawcontext.h"
|
||||
|
||||
SimpleSlider::SimpleSlider(const CRect& bounds, IControlListener* listener, int32_t tag)
|
||||
: CSliderBase(bounds, listener, tag)
|
||||
{
|
||||
setStyle(kHorizontal|kLeft);
|
||||
|
||||
CPoint offsetHandle(2.0, 2.0);
|
||||
setOffsetHandle(offsetHandle);
|
||||
|
||||
CCoord handleSize = 20.0;
|
||||
setHandleSizePrivate(handleSize, bounds.bottom - bounds.top - 2 * offsetHandle.y);
|
||||
setHandleRangePrivate(bounds.right - bounds.left - handleSize - 2 * offsetHandle.x);
|
||||
}
|
||||
|
||||
void SimpleSlider::draw(CDrawContext* dc)
|
||||
{
|
||||
CRect bounds = getViewSize();
|
||||
CRect handle = calculateHandleRect(getValueNormalized());
|
||||
|
||||
dc->setFrameColor(_frame);
|
||||
dc->drawRect(bounds, kDrawStroked);
|
||||
|
||||
dc->setFillColor(_fill);
|
||||
dc->drawRect(handle, kDrawFilled);
|
||||
}
|
||||
23
vst/GUIComponents.h
Normal file
23
vst/GUIComponents.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// 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 "vstgui/lib/controls/cslider.h"
|
||||
#include "vstgui/lib/ccolor.h"
|
||||
|
||||
using namespace VSTGUI;
|
||||
|
||||
class SimpleSlider : public CSliderBase {
|
||||
public:
|
||||
SimpleSlider(const CRect& bounds, IControlListener* listener, int32_t tag);
|
||||
void draw(CDrawContext* dc) override;
|
||||
|
||||
CLASS_METHODS(SimpleSlider, CSliderBase)
|
||||
|
||||
private:
|
||||
CColor _frame = CColor(0x00, 0x00, 0x00);
|
||||
CColor _fill = CColor(0x00, 0x00, 0x00);
|
||||
};
|
||||
|
|
@ -17,8 +17,14 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
|
||||
Vst::ParamID pid = 0;
|
||||
|
||||
// Ordinary parameters
|
||||
parameters.addParameter(
|
||||
kParamVolumeRange.createParameter(
|
||||
Steinberg::String("Volume"), pid++, Steinberg::String("dB"),
|
||||
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
||||
|
||||
// MIDI controllers
|
||||
for (unsigned i = 0; i < numControllerParams; ++i) {
|
||||
for (unsigned i = 0; i < kNumControllerParams; ++i) {
|
||||
Steinberg::String title;
|
||||
Steinberg::String shortTitle;
|
||||
title.printf("Controller %u", i);
|
||||
|
|
@ -53,7 +59,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::getMidiControllerAssignment(int32 bus
|
|||
return kResultTrue;
|
||||
|
||||
default:
|
||||
if (midiControllerNumber < 0 || midiControllerNumber >= numControllerParams)
|
||||
if (midiControllerNumber < 0 || midiControllerNumber >= kNumControllerParams)
|
||||
return kResultFalse;
|
||||
|
||||
id = kPidMidiCC0 + midiControllerNumber;
|
||||
|
|
@ -73,15 +79,53 @@ IPlugView* PLUGIN_API SfizzVstController::createView(FIDString _name)
|
|||
return new SfizzVstEditor(this);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst::ParamValue value)
|
||||
tresult PLUGIN_API SfizzVstController::setParamNormalized(Vst::ParamID tag, Vst::ParamValue normValue)
|
||||
{
|
||||
tresult r = SfizzVstControllerNoUi::setParamNormalized(tag, value);
|
||||
tresult r = SfizzVstControllerNoUi::setParamNormalized(tag, normValue);
|
||||
if (r != kResultTrue)
|
||||
return r;
|
||||
|
||||
float *slot = nullptr;
|
||||
float value = 0;
|
||||
|
||||
switch (tag) {
|
||||
case kPidVolume: {
|
||||
slot = &_state.volume;
|
||||
value = kParamVolumeRange.denormalize(normValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot && *slot != value) {
|
||||
*slot = value;
|
||||
for (StateListener* listener : _stateListeners)
|
||||
listener->onStateChanged();
|
||||
}
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstController::setState(IBStream* state)
|
||||
{
|
||||
SfizzUiState s;
|
||||
|
||||
tresult r = s.load(state);
|
||||
if (r != kResultTrue)
|
||||
return r;
|
||||
|
||||
_uiState = s;
|
||||
|
||||
for (StateListener* listener : _stateListeners)
|
||||
listener->onStateChanged();
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstController::getState(IBStream* state)
|
||||
{
|
||||
return _uiState.store(state);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* state)
|
||||
{
|
||||
SfizzVstState s;
|
||||
|
|
@ -90,19 +134,22 @@ tresult PLUGIN_API SfizzVstController::setComponentState(IBStream* state)
|
|||
if (r != kResultTrue)
|
||||
return r;
|
||||
|
||||
_state = s;
|
||||
|
||||
setParamNormalized(kPidVolume, kParamVolumeRange.normalize(s.volume));
|
||||
|
||||
for (StateListener* listener : _stateListeners)
|
||||
listener->onStateChanged();
|
||||
|
||||
_state = s;
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
void SfizzVstController::addStateListener(StateListener* listener)
|
||||
void SfizzVstController::addSfizzStateListener(StateListener* listener)
|
||||
{
|
||||
_stateListeners.push_back(listener);
|
||||
}
|
||||
|
||||
void SfizzVstController::removeStateListener(StateListener* listener)
|
||||
void SfizzVstController::removeSfizzStateListener(StateListener* listener)
|
||||
{
|
||||
auto it = std::find(_stateListeners.begin(), _stateListeners.end(), listener);
|
||||
if (it != _stateListeners.end())
|
||||
|
|
|
|||
|
|
@ -24,22 +24,12 @@ public:
|
|||
|
||||
tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, Vst::CtrlNumber midiControllerNumber, Vst::ParamID& id) override;
|
||||
|
||||
enum { numControllerParams = 128 };
|
||||
|
||||
// interfaces
|
||||
OBJ_METHODS(SfizzVstControllerNoUi, Vst::EditController)
|
||||
DEFINE_INTERFACES
|
||||
DEF_INTERFACE(Vst::IMidiMapping)
|
||||
END_DEFINE_INTERFACES(Vst::EditController)
|
||||
REFCOUNT_METHODS(Vst::EditController)
|
||||
|
||||
enum {
|
||||
kPidMidiCC0,
|
||||
kPidMidiCCLast = kPidMidiCC0 + numControllerParams - 1,
|
||||
kPidMidiAftertouch,
|
||||
kPidMidiPitchBend,
|
||||
/* Reserved */
|
||||
};
|
||||
};
|
||||
|
||||
class SfizzVstController : public SfizzVstControllerNoUi, public VSTGUI::VST3EditorDelegate {
|
||||
|
|
@ -47,6 +37,8 @@ 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;
|
||||
|
||||
struct StateListener {
|
||||
|
|
@ -55,8 +47,11 @@ public:
|
|||
|
||||
const SfizzVstState& getSfizzState() const { return _state; }
|
||||
|
||||
void addStateListener(StateListener* listener);
|
||||
void removeStateListener(StateListener* listener);
|
||||
const SfizzUiState& getSfizzUiState() const { return _uiState; }
|
||||
SfizzUiState& getSfizzUiState() { return _uiState; }
|
||||
|
||||
void addSfizzStateListener(StateListener* listener);
|
||||
void removeSfizzStateListener(StateListener* listener);
|
||||
|
||||
///
|
||||
static FUnknown* createInstance(void*);
|
||||
|
|
@ -65,5 +60,6 @@ public:
|
|||
|
||||
private:
|
||||
SfizzVstState _state;
|
||||
SfizzUiState _uiState;
|
||||
std::vector<StateListener*> _stateListeners;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "SfizzVstEditor.h"
|
||||
#include "SfizzVstState.h"
|
||||
#include "GUIComponents.h"
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
#include "x11runloop.h"
|
||||
#endif
|
||||
|
|
@ -16,12 +17,12 @@ SfizzVstEditor::SfizzVstEditor(void *controller)
|
|||
: VSTGUIEditor(controller),
|
||||
_logo("logo.png")
|
||||
{
|
||||
static_cast<SfizzVstController*>(getController())->addStateListener(this);
|
||||
getController()->addSfizzStateListener(this);
|
||||
}
|
||||
|
||||
SfizzVstEditor::~SfizzVstEditor()
|
||||
{
|
||||
static_cast<SfizzVstController*>(getController())->removeStateListener(this);
|
||||
getController()->removeSfizzStateListener(this);
|
||||
}
|
||||
|
||||
bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& platformType)
|
||||
|
|
@ -59,6 +60,8 @@ void SfizzVstEditor::valueChanged(CControl* ctl)
|
|||
{
|
||||
int32_t tag = ctl->getTag();
|
||||
float value = ctl->getValue();
|
||||
float valueNorm = ctl->getValueNormalized();
|
||||
SfizzVstController* controller = getController();
|
||||
|
||||
switch (tag) {
|
||||
case kTagLoadSfzFile:
|
||||
|
|
@ -68,6 +71,11 @@ void SfizzVstEditor::valueChanged(CControl* ctl)
|
|||
Call::later([this]() { chooseSfzFile(); });
|
||||
break;
|
||||
|
||||
case kTagSetVolume:
|
||||
controller->setParamNormalized(kPidVolume, valueNorm);
|
||||
controller->performEdit(kPidVolume, valueNorm);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (tag >= kTagFirstChangePanel && tag <= kTagLastChangePanel)
|
||||
setActivePanel(tag - kTagFirstChangePanel);
|
||||
|
|
@ -75,6 +83,33 @@ void SfizzVstEditor::valueChanged(CControl* ctl)
|
|||
}
|
||||
}
|
||||
|
||||
void SfizzVstEditor::enterOrLeaveEdit(CControl* ctl, bool enter)
|
||||
{
|
||||
int32_t tag = ctl->getTag();
|
||||
Vst::ParamID id;
|
||||
|
||||
switch (tag) {
|
||||
case kTagSetVolume: id = kPidVolume; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
SfizzVstController* controller = getController();
|
||||
if (enter)
|
||||
controller->beginEdit(id);
|
||||
else
|
||||
controller->endEdit(id);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::controlBeginEdit(CControl* ctl)
|
||||
{
|
||||
enterOrLeaveEdit(ctl, true);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::controlEndEdit(CControl* ctl)
|
||||
{
|
||||
enterOrLeaveEdit(ctl, false);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::onStateChanged()
|
||||
{
|
||||
updateStateDisplay();
|
||||
|
|
@ -97,8 +132,7 @@ void SfizzVstEditor::chooseSfzFile()
|
|||
|
||||
void SfizzVstEditor::loadSfzFile(const std::string& filePath)
|
||||
{
|
||||
Vst::EditController* ctl = getController();
|
||||
|
||||
SfizzVstController* ctl = getController();
|
||||
|
||||
Vst::IMessage *msg = ctl->allocateMessage();
|
||||
if (!msg) {
|
||||
|
|
@ -118,6 +152,9 @@ void SfizzVstEditor::loadSfzFile(const std::string& filePath)
|
|||
|
||||
void SfizzVstEditor::createFrameContents()
|
||||
{
|
||||
SfizzVstController* controller = getController();
|
||||
const SfizzUiState& uiState = controller->getSfizzUiState();
|
||||
|
||||
CFrame* frame = this->frame;
|
||||
CRect bounds = frame->getViewSize();
|
||||
|
||||
|
|
@ -130,7 +167,7 @@ void SfizzVstEditor::createFrameContents()
|
|||
topRow.bottom = topRow.top + 30;
|
||||
|
||||
CViewContainer* panel;
|
||||
_activePanel = kPanelGeneral;
|
||||
_activePanel = std::max(0, std::min(kNumPanels - 1, static_cast<int>(uiState.activePanel)));
|
||||
|
||||
CRect topLeftLabelBox = topRow;
|
||||
topLeftLabelBox.right -= 20 * kNumPanels;
|
||||
|
|
@ -164,6 +201,88 @@ void SfizzVstEditor::createFrameContents()
|
|||
topLeftLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
panel->addView(topLeftLabel);
|
||||
|
||||
CRect row = topRow;
|
||||
row.top += 200.0;
|
||||
row.bottom += 200.0;
|
||||
row.left += 100.0;
|
||||
row.right -= 100.0;
|
||||
|
||||
CCoord interRow = 35.0;
|
||||
|
||||
auto leftSide = [&row]() -> CRect {
|
||||
CRect div = row;
|
||||
div.right = 0.5 * (div.left + div.right);
|
||||
return div;
|
||||
};
|
||||
|
||||
auto rightSide = [&row]() -> CRect {
|
||||
CRect div = row;
|
||||
div.left = 0.5 * (div.left + div.right);
|
||||
return div;
|
||||
};
|
||||
|
||||
CTextLabel* label;
|
||||
SimpleSlider* slider;
|
||||
|
||||
label = new CTextLabel(leftSide(), "Volume");
|
||||
label->setFontColor(CColor(0x00, 0x00, 0x00));
|
||||
label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
label->setHoriAlign(kLeftText);
|
||||
panel->addView(label);
|
||||
slider = new SimpleSlider(rightSide(), this, kTagSetVolume);
|
||||
adjustMinMaxToRangeParam(slider, kPidVolume);
|
||||
panel->addView(slider);
|
||||
_volumeSlider = slider;
|
||||
|
||||
// row.top += interRow;
|
||||
// row.bottom += interRow;
|
||||
|
||||
// label = new CTextLabel(leftSide(), "Polyphony");
|
||||
// label->setFontColor(CColor(0x00, 0x00, 0x00));
|
||||
// label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setHoriAlign(kLeftText);
|
||||
// panel->addView(label);
|
||||
// slider = new SimpleSlider(rightSide(), this, -1);
|
||||
// panel->addView(slider);
|
||||
|
||||
// row.top += interRow;
|
||||
// row.bottom += interRow;
|
||||
|
||||
// label = new CTextLabel(leftSide(), "Oversampling");
|
||||
// label->setFontColor(CColor(0x00, 0x00, 0x00));
|
||||
// label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setHoriAlign(kLeftText);
|
||||
// panel->addView(label);
|
||||
// slider = new SimpleSlider(rightSide(), this, -1);
|
||||
// panel->addView(slider);
|
||||
|
||||
// row.top += interRow;
|
||||
// row.bottom += interRow;
|
||||
|
||||
// label = new CTextLabel(leftSide(), "Preload size");
|
||||
// label->setFontColor(CColor(0x00, 0x00, 0x00));
|
||||
// label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setHoriAlign(kLeftText);
|
||||
// panel->addView(label);
|
||||
// slider = new SimpleSlider(rightSide(), this, -1);
|
||||
// panel->addView(slider);
|
||||
|
||||
// row.top += interRow;
|
||||
// row.bottom += interRow;
|
||||
|
||||
// label = new CTextLabel(leftSide(), "Freewheel");
|
||||
// label->setFontColor(CColor(0x00, 0x00, 0x00));
|
||||
// label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
// label->setHoriAlign(kLeftText);
|
||||
// panel->addView(label);
|
||||
// slider = new SimpleSlider(rightSide(), this, -1);
|
||||
// panel->addView(slider);
|
||||
|
||||
_subPanels[kPanelSettings] = panel;
|
||||
}
|
||||
|
||||
|
|
@ -204,17 +323,31 @@ void SfizzVstEditor::updateStateDisplay()
|
|||
if (!frame)
|
||||
return;
|
||||
|
||||
const SfizzVstState& state = static_cast<SfizzVstController*>(getController())->getSfizzState();
|
||||
SfizzVstController* controller = getController();
|
||||
const SfizzVstState& state = controller->getSfizzState();
|
||||
const SfizzUiState& uiState = controller->getSfizzUiState();
|
||||
|
||||
if (_fileLabel)
|
||||
_fileLabel->setText(("File: " + state.sfzFile).c_str());
|
||||
if (_volumeSlider)
|
||||
_volumeSlider->setValue(state.volume);
|
||||
|
||||
setActivePanel(uiState.activePanel);
|
||||
}
|
||||
|
||||
void SfizzVstEditor::setActivePanel(unsigned panelId)
|
||||
{
|
||||
panelId = std::max(0, std::min(kNumPanels - 1, static_cast<int>(panelId)));
|
||||
|
||||
getController()->getSfizzUiState().activePanel = panelId;
|
||||
|
||||
if (_activePanel != panelId) {
|
||||
_subPanels[_activePanel]->setVisible(false);
|
||||
_subPanels[panelId]->setVisible(true);
|
||||
if (frame)
|
||||
_subPanels[_activePanel]->setVisible(false);
|
||||
|
||||
_activePanel = panelId;
|
||||
|
||||
if (frame)
|
||||
_subPanels[panelId]->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,16 @@ public:
|
|||
bool PLUGIN_API open(void* parent, const VSTGUI::PlatformType& platformType = VSTGUI::kDefaultNative) override;
|
||||
void PLUGIN_API close() override;
|
||||
|
||||
SfizzVstController* getController() const
|
||||
{
|
||||
return static_cast<SfizzVstController*>(Vst::VSTGUIEditor::getController());
|
||||
}
|
||||
|
||||
// IControlListener
|
||||
void valueChanged(CControl* ctl) override;
|
||||
void enterOrLeaveEdit(CControl* ctl, bool enter);
|
||||
void controlBeginEdit(CControl* ctl) override;
|
||||
void controlEndEdit(CControl* ctl) override;
|
||||
|
||||
// SfizzVstController::StateListener
|
||||
void onStateChanged() override;
|
||||
|
|
@ -33,6 +41,14 @@ private:
|
|||
void updateStateDisplay();
|
||||
void setActivePanel(unsigned panelId);
|
||||
|
||||
template <class Control>
|
||||
void adjustMinMaxToRangeParam(Control* c, Vst::ParamID id)
|
||||
{
|
||||
auto* p = static_cast<Vst::RangeParameter*>(getController()->getParameterObject(id));
|
||||
c->setMin(p->getMin());
|
||||
c->setMax(p->getMax());
|
||||
}
|
||||
|
||||
enum {
|
||||
kPanelGeneral,
|
||||
// kPanelControls,
|
||||
|
|
@ -45,10 +61,12 @@ private:
|
|||
|
||||
enum {
|
||||
kTagLoadSfzFile,
|
||||
kTagSetVolume,
|
||||
kTagFirstChangePanel,
|
||||
kTagLastChangePanel = kTagFirstChangePanel + kNumPanels - 1,
|
||||
};
|
||||
|
||||
CBitmap _logo;
|
||||
CTextLabel* _fileLabel = nullptr;
|
||||
CSliderBase *_volumeSlider = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
addAudioOutput(STR16("Audio Output"), Vst::SpeakerArr::kStereo);
|
||||
addEventInput(STR16("Event Input"), 1);
|
||||
|
||||
_state = SfizzVstState();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -47,28 +49,37 @@ tresult PLUGIN_API SfizzVstProcessor::setBusArrangements(Vst::SpeakerArrangement
|
|||
return AudioEffect::setBusArrangements(inputs, numIns, outputs, numOuts);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* state)
|
||||
tresult PLUGIN_API SfizzVstProcessor::setState(IBStream* stream)
|
||||
{
|
||||
SfizzVstState s;
|
||||
|
||||
tresult r = s.load(state);
|
||||
tresult r = s.load(stream);
|
||||
if (r != kResultTrue)
|
||||
return r;
|
||||
|
||||
loadSfzFile(s.sfzFile);
|
||||
std::lock_guard<std::mutex> lock(_processMutex);
|
||||
_state = s;
|
||||
|
||||
syncStateToSynth();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::getState(IBStream* state)
|
||||
tresult PLUGIN_API SfizzVstProcessor::getState(IBStream* stream)
|
||||
{
|
||||
SfizzVstState s;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_processMutex);
|
||||
s.sfzFile = _sfzFile;
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(_processMutex);
|
||||
return _state.store(stream);
|
||||
}
|
||||
|
||||
return s.store(state);
|
||||
void SfizzVstProcessor::syncStateToSynth()
|
||||
{
|
||||
sfz::Sfizz* synth = _synth.get();
|
||||
|
||||
if (!synth)
|
||||
return;
|
||||
|
||||
synth->loadSfzFile(_state.sfzFile);
|
||||
synth->setVolume(_state.volume);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::canProcessSampleSize(int32 symbolicSampleSize)
|
||||
|
|
@ -92,7 +103,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
|||
synth->setSampleRate(processSetup.sampleRate);
|
||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||
|
||||
loadSfzFile(_sfzFile);
|
||||
syncStateToSynth();
|
||||
|
||||
_workRunning = true;
|
||||
_worker = std::thread([this]() { doBackgroundWork(); });
|
||||
|
|
@ -105,6 +116,11 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges) {
|
||||
std::unique_lock<std::mutex> lock(_processMutex, std::try_to_lock);
|
||||
processParameterChanges(*pc);
|
||||
}
|
||||
|
||||
if (data.numOutputs < 1) // flush mode
|
||||
return kResultTrue;
|
||||
|
||||
|
|
@ -118,6 +134,7 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
outputs[c] = data.outputs[0].channelBuffers32[c];
|
||||
|
||||
std::unique_lock<std::mutex> lock(_processMutex, std::try_to_lock);
|
||||
|
||||
if (!lock.owns_lock()) {
|
||||
for (unsigned c = 0; c < numChannels; ++c)
|
||||
std::memset(outputs[c], 0, numFrames * sizeof(float));
|
||||
|
|
@ -125,78 +142,113 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
return kResultTrue;
|
||||
}
|
||||
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges) {
|
||||
uint32 paramCount = pc->getParameterCount();
|
||||
if (Vst::IParameterChanges* pc = data.inputParameterChanges)
|
||||
processControllerChanges(*pc);
|
||||
|
||||
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
|
||||
Vst::IParamValueQueue* vq = pc->getParameterData(paramIndex);
|
||||
if (Vst::IEventList* events = data.inputEvents)
|
||||
processEvents(*events);
|
||||
|
||||
Vst::ParamID id = vq->getParameterId();
|
||||
|
||||
switch (id) {
|
||||
default:
|
||||
if (id >= SfizzVstController::kPidMidiCC0 && id <= SfizzVstController::kPidMidiCCLast) {
|
||||
int ccNumber = id - SfizzVstController::kPidMidiCC0;
|
||||
for (uint32 pointIndex = 0, pointCount = vq->getPointCount(); pointIndex < pointCount; ++pointIndex) {
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.cc(sampleOffset, ccNumber, (int)(0.5 + value * 127.0));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SfizzVstController::kPidMidiAftertouch:
|
||||
for (uint32 pointIndex = 0, pointCount = vq->getPointCount(); pointIndex < pointCount; ++pointIndex) {
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.aftertouch(sampleOffset, (int)(0.5 + value * 127.0));
|
||||
}
|
||||
break;
|
||||
|
||||
case SfizzVstController::kPidMidiPitchBend:
|
||||
for (uint32 pointIndex = 0, pointCount = vq->getPointCount(); pointIndex < pointCount; ++pointIndex) {
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.pitchWheel(sampleOffset, (int)(0.5 + value * 16383) - 8192);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Vst::IEventList* events = data.inputEvents) {
|
||||
uint32 numEvents = events->getEventCount();
|
||||
|
||||
for (uint32 i = 0; i < numEvents; i++) {
|
||||
Vst::Event e;
|
||||
if (events->getEvent(i, e) != kResultTrue)
|
||||
continue;
|
||||
|
||||
auto convertVelocityFromFloat = [](float x) -> int {
|
||||
return std::min(127, std::max(0, (int)(x * 127.0f)));
|
||||
};
|
||||
|
||||
switch (e.type) {
|
||||
case Vst::Event::kNoteOnEvent:
|
||||
synth.noteOn(e.sampleOffset, e.noteOn.pitch, convertVelocityFromFloat(e.noteOn.velocity));
|
||||
break;
|
||||
case Vst::Event::kNoteOffEvent:
|
||||
synth.noteOff(e.sampleOffset, e.noteOff.pitch, convertVelocityFromFloat(e.noteOff.velocity));
|
||||
break;
|
||||
// case Vst::Event::kPolyPressureEvent:
|
||||
// synth.aftertouch(e.sampleOffset, convertVelocityFromFloat(e.polyPressure.pressure));
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
synth.setVolume(_state.volume);
|
||||
|
||||
synth.renderBlock(outputs, numFrames, numChannels);
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc)
|
||||
{
|
||||
uint32 paramCount = pc.getParameterCount();
|
||||
|
||||
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
|
||||
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
|
||||
if (!vq)
|
||||
continue;
|
||||
|
||||
Vst::ParamID id = vq->getParameterId();
|
||||
uint32 pointCount = vq->getPointCount();
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
|
||||
switch (id) {
|
||||
case kPidVolume:
|
||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue)
|
||||
_state.volume = kParamVolumeRange.denormalize(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processControllerChanges(Vst::IParameterChanges& pc)
|
||||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
uint32 paramCount = pc.getParameterCount();
|
||||
|
||||
for (uint32 paramIndex = 0; paramIndex < paramCount; ++paramIndex) {
|
||||
Vst::IParamValueQueue* vq = pc.getParameterData(paramIndex);
|
||||
if (!vq)
|
||||
continue;
|
||||
|
||||
Vst::ParamID id = vq->getParameterId();
|
||||
uint32 pointCount = vq->getPointCount();
|
||||
int32 sampleOffset;
|
||||
Vst::ParamValue value;
|
||||
|
||||
switch (id) {
|
||||
default:
|
||||
if (id >= kPidMidiCC0 && id <= kPidMidiCCLast) {
|
||||
int ccNumber = id - kPidMidiCC0;
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.cc(sampleOffset, ccNumber, (int)(0.5 + value * 127.0));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kPidMidiAftertouch:
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.aftertouch(sampleOffset, (int)(0.5 + value * 127.0));
|
||||
}
|
||||
break;
|
||||
|
||||
case kPidMidiPitchBend:
|
||||
for (uint32 pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
|
||||
if (vq->getPoint(pointIndex, sampleOffset, value) == kResultTrue)
|
||||
synth.pitchWheel(sampleOffset, (int)(0.5 + value * 16383) - 8192);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::processEvents(Vst::IEventList& events)
|
||||
{
|
||||
sfz::Sfizz& synth = *_synth;
|
||||
uint32 numEvents = events.getEventCount();
|
||||
|
||||
for (uint32 i = 0; i < numEvents; i++) {
|
||||
Vst::Event e;
|
||||
if (events.getEvent(i, e) != kResultTrue)
|
||||
continue;
|
||||
|
||||
switch (e.type) {
|
||||
case Vst::Event::kNoteOnEvent:
|
||||
synth.noteOn(e.sampleOffset, e.noteOn.pitch, convertVelocityFromFloat(e.noteOn.velocity));
|
||||
break;
|
||||
case Vst::Event::kNoteOffEvent:
|
||||
synth.noteOff(e.sampleOffset, e.noteOff.pitch, convertVelocityFromFloat(e.noteOff.velocity));
|
||||
break;
|
||||
// case Vst::Event::kPolyPressureEvent:
|
||||
// synth.aftertouch(e.sampleOffset, convertVelocityFromFloat(e.polyPressure.pressure));
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SfizzVstProcessor::convertVelocityFromFloat(float x)
|
||||
{
|
||||
return std::min(127, std::max(0, (int)(x * 127.0f)));
|
||||
}
|
||||
|
||||
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
||||
{
|
||||
tresult result = AudioEffect::notify(message);
|
||||
|
|
@ -217,18 +269,6 @@ FUnknown* SfizzVstProcessor::createInstance(void*)
|
|||
return static_cast<Vst::IAudioProcessor*>(new SfizzVstProcessor);
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::loadSfzFile(std::string file)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_processMutex);
|
||||
|
||||
if (_synth) {
|
||||
fprintf(stderr, "[Sfizz] load SFZ file: %s\n", file.c_str());
|
||||
_synth->loadSfzFile(file);
|
||||
}
|
||||
|
||||
_sfzFile = std::move(file);
|
||||
}
|
||||
|
||||
void SfizzVstProcessor::doBackgroundWork()
|
||||
{
|
||||
constexpr uint32 maxPathLen = 32768;
|
||||
|
|
@ -250,8 +290,11 @@ void SfizzVstProcessor::doBackgroundWork()
|
|||
|
||||
if (!std::strcmp(id, "LoadSfz")) {
|
||||
std::vector<Vst::TChar> path(maxPathLen + 1);
|
||||
if (attr->getString("File", path.data(), maxPathLen) == kResultTrue)
|
||||
loadSfzFile(Steinberg::String(path.data()).text8());
|
||||
if (attr->getString("File", path.data(), maxPathLen) == kResultTrue) {
|
||||
std::lock_guard<std::mutex> lock(_processMutex);
|
||||
_state.sfzFile = Steinberg::String(path.data()).text8();
|
||||
_synth->loadSfzFile(_state.sfzFile);
|
||||
}
|
||||
}
|
||||
|
||||
msg->release();
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#pragma once
|
||||
#include "SfizzVstState.h"
|
||||
#include "RTSemaphore.h"
|
||||
#include "public.sdk/source/vst/vstaudioeffect.h"
|
||||
#include "public.sdk/source/vst/utility/ringbuffer.h"
|
||||
#include "RTSemaphore.h"
|
||||
#include <sfizz.hpp>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
|
@ -23,12 +24,17 @@ public:
|
|||
tresult PLUGIN_API initialize(FUnknown* context) override;
|
||||
tresult PLUGIN_API setBusArrangements(Vst::SpeakerArrangement* inputs, int32 numIns, Vst::SpeakerArrangement* outputs, int32 numOuts) override;
|
||||
|
||||
tresult PLUGIN_API setState(IBStream* state) override;
|
||||
tresult PLUGIN_API getState(IBStream* state) override;
|
||||
tresult PLUGIN_API setState(IBStream* stream) override;
|
||||
tresult PLUGIN_API getState(IBStream* stream) override;
|
||||
void syncStateToSynth();
|
||||
|
||||
tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override;
|
||||
tresult PLUGIN_API setActive(TBool state) override;
|
||||
tresult PLUGIN_API process(Vst::ProcessData& data) override;
|
||||
void processParameterChanges(Vst::IParameterChanges& pc);
|
||||
void processControllerChanges(Vst::IParameterChanges& pc);
|
||||
void processEvents(Vst::IEventList& events);
|
||||
static int convertVelocityFromFloat(float x);
|
||||
|
||||
tresult PLUGIN_API notify(Vst::IMessage* message) override;
|
||||
|
||||
|
|
@ -38,19 +44,17 @@ public:
|
|||
|
||||
// --- Sfizz stuff here below ---
|
||||
private:
|
||||
// synth state. acquire processMutex before accessing
|
||||
std::unique_ptr<sfz::Sfizz> _synth;
|
||||
SfizzVstState _state;
|
||||
|
||||
// worker and thread sync
|
||||
std::thread _worker;
|
||||
volatile bool _workRunning = false;
|
||||
Steinberg::OneReaderOneWriter::RingBuffer<Vst::IMessage*> _fifoToWorker;
|
||||
RTSemaphore _semaToWorker;
|
||||
std::mutex _processMutex;
|
||||
|
||||
// state
|
||||
std::string _sfzFile;
|
||||
|
||||
//
|
||||
void loadSfzFile(std::string file);
|
||||
|
||||
// worker
|
||||
void doBackgroundWork();
|
||||
void stopBackgroundWork();
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@ tresult SfizzVstState::load(IBStream* state)
|
|||
if (!s.readInt64u(version))
|
||||
return kResultFalse;
|
||||
|
||||
while (const char* key = s.readStr8()) {
|
||||
if (!std::strcmp(key, "SfzFile")) {
|
||||
const char* value = s.readStr8();
|
||||
if (!value)
|
||||
return kResultFalse;
|
||||
sfzFile = value;
|
||||
}
|
||||
}
|
||||
if (const char* str = s.readStr8())
|
||||
sfzFile = str;
|
||||
else
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.readFloat(volume))
|
||||
return kResultFalse;
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
|
@ -35,7 +34,37 @@ tresult SfizzVstState::store(IBStream* state) const
|
|||
if (!s.writeInt64u(currentStateVersion))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.writeStr8("SfzFile") || !s.writeStr8(sfzFile.c_str()))
|
||||
if (!s.writeStr8(sfzFile.c_str()))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.writeFloat(volume))
|
||||
return kResultFalse;
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
tresult SfizzUiState::load(IBStream* state)
|
||||
{
|
||||
IBStreamer s(state, kLittleEndian);
|
||||
|
||||
uint64 version = 0;
|
||||
if (!s.readInt64u(version))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.readInt32u(activePanel))
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -6,16 +6,69 @@
|
|||
|
||||
#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,
|
||||
kPidMidiCC0,
|
||||
kPidMidiCCLast = kPidMidiCC0 + kNumControllerParams - 1,
|
||||
kPidMidiAftertouch,
|
||||
kPidMidiPitchBend,
|
||||
/* Reserved */
|
||||
};
|
||||
|
||||
class SfizzVstState {
|
||||
public:
|
||||
std::string sfzFile;
|
||||
float volume = 0;
|
||||
|
||||
static constexpr uint64 currentStateVersion = 0;
|
||||
|
||||
tresult load(IBStream* state);
|
||||
tresult store(IBStream* state) const;
|
||||
};
|
||||
|
||||
class SfizzUiState {
|
||||
public:
|
||||
unsigned activePanel = 0;
|
||||
|
||||
static constexpr uint64 currentStateVersion = 0;
|
||||
|
||||
tresult load(IBStream* state);
|
||||
tresult store(IBStream* state) const;
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue