From d8b1a79fe7784f90569c9f666f1b7bbf0786a421 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Thu, 5 Mar 2020 18:21:46 +0100 Subject: [PATCH] Add other parameter: volume + the UI --- vst/CMakeLists.txt | 1 + vst/GUIComponents.cpp | 33 ++++++ vst/GUIComponents.h | 23 ++++ vst/SfizzVstController.cpp | 61 ++++++++-- vst/SfizzVstController.h | 20 ++-- vst/SfizzVstEditor.cpp | 149 +++++++++++++++++++++++-- vst/SfizzVstEditor.h | 18 +++ vst/SfizzVstProcessor.cpp | 223 ++++++++++++++++++++++--------------- vst/SfizzVstProcessor.h | 22 ++-- vst/SfizzVstState.cpp | 47 ++++++-- vst/SfizzVstState.h | 53 +++++++++ 11 files changed, 515 insertions(+), 135 deletions(-) create mode 100644 vst/GUIComponents.cpp create mode 100644 vst/GUIComponents.h diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt index b9217f22..0d8c505f 100644 --- a/vst/CMakeLists.txt +++ b/vst/CMakeLists.txt @@ -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}) diff --git a/vst/GUIComponents.cpp b/vst/GUIComponents.cpp new file mode 100644 index 00000000..314ba642 --- /dev/null +++ b/vst/GUIComponents.cpp @@ -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); +} diff --git a/vst/GUIComponents.h b/vst/GUIComponents.h new file mode 100644 index 00000000..003d219b --- /dev/null +++ b/vst/GUIComponents.h @@ -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); +}; diff --git a/vst/SfizzVstController.cpp b/vst/SfizzVstController.cpp index 88fcc6f0..fc7cfe5e 100644 --- a/vst/SfizzVstController.cpp +++ b/vst/SfizzVstController.cpp @@ -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()) diff --git a/vst/SfizzVstController.h b/vst/SfizzVstController.h index 47c3ba3c..e8ca3037 100644 --- a/vst/SfizzVstController.h +++ b/vst/SfizzVstController.h @@ -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 _stateListeners; }; diff --git a/vst/SfizzVstEditor.cpp b/vst/SfizzVstEditor.cpp index 4d5820d1..9b197331 100644 --- a/vst/SfizzVstEditor.cpp +++ b/vst/SfizzVstEditor.cpp @@ -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(getController())->addStateListener(this); + getController()->addSfizzStateListener(this); } SfizzVstEditor::~SfizzVstEditor() { - static_cast(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(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(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(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); } } diff --git a/vst/SfizzVstEditor.h b/vst/SfizzVstEditor.h index b293be5c..1acd7492 100644 --- a/vst/SfizzVstEditor.h +++ b/vst/SfizzVstEditor.h @@ -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(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 + void adjustMinMaxToRangeParam(Control* c, Vst::ParamID id) + { + auto* p = static_cast(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; }; diff --git a/vst/SfizzVstProcessor.cpp b/vst/SfizzVstProcessor.cpp index 88e71d1a..6a7160df 100644 --- a/vst/SfizzVstProcessor.cpp +++ b/vst/SfizzVstProcessor.cpp @@ -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 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 lock(_processMutex); - s.sfzFile = _sfzFile; - } + std::lock_guard 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 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 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(new SfizzVstProcessor); } -void SfizzVstProcessor::loadSfzFile(std::string file) -{ - std::lock_guard 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 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 lock(_processMutex); + _state.sfzFile = Steinberg::String(path.data()).text8(); + _synth->loadSfzFile(_state.sfzFile); + } } msg->release(); diff --git a/vst/SfizzVstProcessor.h b/vst/SfizzVstProcessor.h index 86bbab81..86811e75 100644 --- a/vst/SfizzVstProcessor.h +++ b/vst/SfizzVstProcessor.h @@ -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 #include #include @@ -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 _synth; + SfizzVstState _state; + + // worker and thread sync std::thread _worker; volatile bool _workRunning = false; Steinberg::OneReaderOneWriter::RingBuffer _fifoToWorker; RTSemaphore _semaToWorker; std::mutex _processMutex; - // state - std::string _sfzFile; - - // - void loadSfzFile(std::string file); - // worker void doBackgroundWork(); void stopBackgroundWork(); diff --git a/vst/SfizzVstState.cpp b/vst/SfizzVstState.cpp index 447f7ad0..fffeaae2 100644 --- a/vst/SfizzVstState.cpp +++ b/vst/SfizzVstState.cpp @@ -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; diff --git a/vst/SfizzVstState.h b/vst/SfizzVstState.h index df0adecc..f1cfc563 100644 --- a/vst/SfizzVstState.h +++ b/vst/SfizzVstState.h @@ -6,16 +6,69 @@ #pragma once #include "base/source/fstreamer.h" +#include "public.sdk/source/vst/vstparameters.h" #include 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);