Add other parameter: volume + the UI

This commit is contained in:
Jean Pierre Cimalando 2020-03-05 18:21:46 +01:00 committed by Paul Fd
parent 092b95fc3c
commit d8b1a79fe7
11 changed files with 515 additions and 135 deletions

View file

@ -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
View 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
View 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);
};

View file

@ -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())

View file

@ -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;
};

View file

@ -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);
}
}

View file

@ -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;
};

View file

@ -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();

View file

@ -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();

View file

@ -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;

View file

@ -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);