Volume meter for plugin editors
This commit is contained in:
parent
9411bd458d
commit
9952d28b97
23 changed files with 514 additions and 134 deletions
|
|
@ -45,6 +45,11 @@ if(WIN32)
|
|||
add_compile_definitions(_WIN32_WINNT=0x601)
|
||||
endif()
|
||||
|
||||
# Define the math constants everywhere
|
||||
if(WIN32)
|
||||
add_compile_definitions(_USE_MATH_DEFINES)
|
||||
endif()
|
||||
|
||||
# Set macOS compatibility level
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
add_library(plugins-common STATIC EXCLUDE_FROM_ALL
|
||||
"common/plugin/RMSFollower.h"
|
||||
"common/plugin/MessageUtils.h"
|
||||
"common/plugin/MessageUtils.cpp"
|
||||
"common/plugin/InstrumentDescription.h"
|
||||
|
|
@ -28,7 +29,7 @@ endif()
|
|||
target_include_directories(plugins-common PUBLIC "common")
|
||||
target_link_libraries(plugins-common
|
||||
PUBLIC sfizz::spin_mutex
|
||||
PUBLIC sfizz::filesystem absl::strings
|
||||
PUBLIC sfizz::simde sfizz::filesystem absl::strings
|
||||
PRIVATE sfizz::pugixml
|
||||
PRIVATE sfizz::internal sfizz::sfizz)
|
||||
add_library(sfizz::plugins-common ALIAS plugins-common)
|
||||
|
|
|
|||
73
plugins/common/plugin/RMSFollower.h
Normal file
73
plugins/common/plugin/RMSFollower.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// 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 <simde/x86/sse.h>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
|
||||
class RMSFollower {
|
||||
public:
|
||||
RMSFollower()
|
||||
{
|
||||
updatePole();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
mem_ = simde_mm_setzero_ps();
|
||||
}
|
||||
|
||||
void init(float sampleRate)
|
||||
{
|
||||
sampleRate_ = sampleRate;
|
||||
updatePole();
|
||||
clear();
|
||||
}
|
||||
|
||||
void setT60(float t60)
|
||||
{
|
||||
t60_ = t60;
|
||||
updatePole();
|
||||
}
|
||||
|
||||
void process(const float* leftBlock, const float* rightBlock, size_t numFrames)
|
||||
{
|
||||
simde__m128 mem = mem_;
|
||||
const simde__m128 pole = simde_mm_load1_ps(&pole_);
|
||||
for (size_t i = 0; i < numFrames; ++i) {
|
||||
float left = leftBlock[i];
|
||||
float right = rightBlock[i];
|
||||
simde__m128 input = simde_mm_setr_ps(left, right, 0.0f, 0.0f);
|
||||
input = simde_mm_mul_ps(input, input);
|
||||
simde__m128 output = simde_mm_add_ps(input, simde_mm_mul_ps(pole, simde_mm_sub_ps(mem, input)));
|
||||
mem = output;
|
||||
}
|
||||
mem_ = mem;
|
||||
}
|
||||
|
||||
simde__m128 getMS() const
|
||||
{
|
||||
return mem_;
|
||||
}
|
||||
|
||||
simde__m128 getRMS() const
|
||||
{
|
||||
return simde_mm_sqrt_ps(mem_);
|
||||
}
|
||||
|
||||
private:
|
||||
void updatePole()
|
||||
{
|
||||
pole_ = std::exp(float(-2.0 * M_PI) / (t60_ * sampleRate_));
|
||||
}
|
||||
|
||||
private:
|
||||
simde__m128 mem_ {};
|
||||
float pole_ {};
|
||||
float t60_ = 300e-3;
|
||||
float sampleRate_ = 44100;
|
||||
};
|
||||
|
|
@ -134,10 +134,6 @@ widget_class mainView {open
|
|||
xywh {610 70 60 5} labelsize 12 hide
|
||||
class ValueLabel
|
||||
}
|
||||
Fl_Box {} {
|
||||
xywh {745 20 35 55} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
Fl_Box volumeCCKnob_ {
|
||||
label Volume
|
||||
comment {tag=kTagSetCCVolume}
|
||||
|
|
@ -150,6 +146,14 @@ widget_class mainView {open
|
|||
xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17
|
||||
class KnobCCBox
|
||||
}
|
||||
Fl_Box leftMeter_ {
|
||||
xywh {740 10 15 90} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
Fl_Box rightMeter_ {selected
|
||||
xywh {760 10 15 90} box BORDER_BOX
|
||||
class VMeter
|
||||
}
|
||||
}
|
||||
}
|
||||
Fl_Group {subPanels_[kPanelGeneral]} {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ enum class EditId : int {
|
|||
CC_RANGE(ControllerDefault),
|
||||
CC_RANGE(ControllerLabel),
|
||||
//
|
||||
LeftLevel,
|
||||
RightLevel,
|
||||
//
|
||||
UINumCurves,
|
||||
UINumMasters,
|
||||
UINumGroups,
|
||||
|
|
|
|||
|
|
@ -153,6 +153,9 @@ struct Editor::Impl : EditorController::Receiver,
|
|||
SKnobCCBox* volumeCCKnob_ = nullptr;
|
||||
SKnobCCBox* panCCKnob_ = nullptr;
|
||||
|
||||
SLevelMeter* leftMeter_ = nullptr;
|
||||
SLevelMeter* rightMeter_ = nullptr;
|
||||
|
||||
SAboutDialog* aboutDialog_ = nullptr;
|
||||
|
||||
SharedPointer<CBitmap> backgroundBitmap_;
|
||||
|
|
@ -504,6 +507,20 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
updateBackgroundImage(value.c_str());
|
||||
}
|
||||
break;
|
||||
case EditId::LeftLevel:
|
||||
{
|
||||
const float value = v.to_float();
|
||||
if (SLevelMeter* meter = leftMeter_)
|
||||
meter->setValue(value);
|
||||
}
|
||||
break;
|
||||
case EditId::RightLevel:
|
||||
{
|
||||
const float value = v.to_float();
|
||||
if (SLevelMeter* meter = rightMeter_)
|
||||
meter->setValue(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (editIdIsKey(id)) {
|
||||
const int key = keyForEditId(id);
|
||||
|
|
@ -703,11 +720,15 @@ void Editor::Impl::createFrameContents()
|
|||
lbl->setFont(font);
|
||||
return lbl;
|
||||
};
|
||||
auto createVMeter = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
|
||||
// TODO the volume meter...
|
||||
CViewContainer* container = new CViewContainer(bounds);
|
||||
container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
|
||||
return container;
|
||||
auto createVMeter = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
|
||||
SLevelMeter* meter = new SLevelMeter(bounds);
|
||||
meter->setNormalFillColor(CColor(0x00, 0xaa, 0x11));
|
||||
meter->setDangerFillColor(CColor(0xaa, 0x00, 0x00));
|
||||
OnThemeChanged.push_back([meter, palette]() {
|
||||
meter->setFrameColor(palette->inactiveText);
|
||||
meter->setBackColor(palette->knobInactiveTrack);
|
||||
});
|
||||
return meter;
|
||||
};
|
||||
#if 0
|
||||
auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) {
|
||||
|
|
|
|||
|
|
@ -979,6 +979,75 @@ void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl)
|
|||
panel_->EndEditFunction(pControl->getTag());
|
||||
}
|
||||
|
||||
///
|
||||
SLevelMeter::SLevelMeter(const CRect& size)
|
||||
: CView(size)
|
||||
{
|
||||
}
|
||||
|
||||
void SLevelMeter::setValue(float value)
|
||||
{
|
||||
if (value_ == value)
|
||||
return;
|
||||
|
||||
value_ = value;
|
||||
invalid();
|
||||
}
|
||||
|
||||
void SLevelMeter::draw(CDrawContext* dc)
|
||||
{
|
||||
float dbValue = 20.0f * std::log10(value_);
|
||||
float fill = (dbValue - dbMin_) / (dbMax_ - dbMin_);
|
||||
fill = (fill < 0.0f) ? 0.0f : fill;
|
||||
fill = (fill > 1.0f) ? 1.0f : fill;
|
||||
|
||||
CRect largeBounds = getViewSize();
|
||||
CRect fillBounds = largeBounds;
|
||||
fillBounds.top = largeBounds.bottom - fill * largeBounds.getHeight();
|
||||
|
||||
const CColor safeColor = safeFillColor_;
|
||||
const CColor dangerColor = dangerFillColor_;
|
||||
|
||||
CColor fillColor;
|
||||
if (safeColor == dangerColor) {
|
||||
fillColor = safeColor;
|
||||
}
|
||||
else {
|
||||
float thres = dangerThreshold_;
|
||||
float mix = (fill - thres) / (1.0f - thres);
|
||||
mix = (mix < 0.0f) ? 0.0f : mix;
|
||||
|
||||
CCoord safeH, safeS, safeV, safeA;
|
||||
CCoord dangerH, dangerS, dangerV, dangerA;
|
||||
safeColor.toHSV(safeH, safeS, safeV);
|
||||
dangerColor.toHSV(dangerH, dangerS, dangerV);
|
||||
safeA = safeColor.alpha / 255.0;
|
||||
dangerA = dangerColor.alpha / 255.0;
|
||||
|
||||
CCoord H, S, V, A;
|
||||
H = safeH + mix * (dangerH - safeH);
|
||||
S = safeS + mix * (dangerS - safeS);
|
||||
V = safeV + mix * (dangerV - safeV);
|
||||
A = safeA + mix * (dangerA - safeA);
|
||||
|
||||
fillColor.fromHSV(H, S, V);
|
||||
fillColor.alpha = static_cast<uint8_t>(A * 255.0);
|
||||
}
|
||||
|
||||
dc->setDrawMode(kAliasing);
|
||||
|
||||
if (backColor_.alpha > 0) {
|
||||
dc->setFillColor(backColor_);
|
||||
dc->drawRect(largeBounds, kDrawFilled);
|
||||
}
|
||||
|
||||
dc->setFrameColor(frameColor_);
|
||||
dc->setFillColor(fillColor);
|
||||
|
||||
dc->drawRect(fillBounds, kDrawFilled);
|
||||
dc->drawRect(largeBounds);
|
||||
}
|
||||
|
||||
///
|
||||
SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color)
|
||||
: CView(size), color_(color)
|
||||
|
|
|
|||
|
|
@ -365,6 +365,41 @@ private:
|
|||
SharedPointer<CVSTGUITimer> relayoutTrigger_;
|
||||
};
|
||||
|
||||
///
|
||||
class SLevelMeter : public CView {
|
||||
public:
|
||||
explicit SLevelMeter(const CRect& size);
|
||||
|
||||
float getValue() const { return value_; }
|
||||
void setValue(float value);
|
||||
|
||||
float getDangerThreshold() const { return dangerThreshold_; }
|
||||
void setDangerThreshold(float thres) { dangerThreshold_ = thres; invalid(); }
|
||||
|
||||
CColor getFrameColor() const { return frameColor_; }
|
||||
void setFrameColor(CColor color) { frameColor_ = color; invalid(); }
|
||||
CColor getBackColor() const { return backColor_; }
|
||||
void setBackColor(CColor color) { backColor_ = color; invalid(); }
|
||||
|
||||
CColor getNormalFillColor() const { return safeFillColor_; }
|
||||
void setNormalFillColor(CColor color) { safeFillColor_ = color; invalid(); }
|
||||
CColor getDangerFillColor() const { return dangerFillColor_; }
|
||||
void setDangerFillColor(CColor color) { dangerFillColor_ = color; invalid(); }
|
||||
|
||||
protected:
|
||||
void draw(CDrawContext* dc) override;
|
||||
|
||||
private:
|
||||
float value_ = 0;
|
||||
float dangerThreshold_ = 0.5;
|
||||
float dbMin_ = -40;
|
||||
float dbMax_ = 0;
|
||||
CColor frameColor_;
|
||||
CColor safeFillColor_;
|
||||
CColor dangerFillColor_;
|
||||
CColor backColor_;
|
||||
};
|
||||
|
||||
///
|
||||
class SPlaceHolder : public CView {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -72,124 +72,128 @@ view__26->setVisible(false);
|
|||
auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12);
|
||||
view__25->addView(view__27);
|
||||
view__27->setVisible(false);
|
||||
auto* const view__28 = createVMeter(CRect(175, 15, 210, 70), -1, "", kCenterText, 14);
|
||||
auto* const view__28 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12);
|
||||
volumeCCKnob_ = view__28;
|
||||
view__25->addView(view__28);
|
||||
auto* const view__29 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12);
|
||||
volumeCCKnob_ = view__29;
|
||||
auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12);
|
||||
panCCKnob_ = view__29;
|
||||
view__25->addView(view__29);
|
||||
auto* const view__30 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12);
|
||||
panCCKnob_ = view__30;
|
||||
auto* const view__30 = createVMeter(CRect(170, 5, 185, 95), -1, "", kCenterText, 14);
|
||||
leftMeter_ = view__30;
|
||||
view__25->addView(view__30);
|
||||
auto* const view__31 = createVMeter(CRect(190, 5, 205, 95), -1, "", kCenterText, 14);
|
||||
rightMeter_ = view__31;
|
||||
view__25->addView(view__31);
|
||||
enterPalette(defaultPalette);
|
||||
auto* const view__31 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelGeneral] = view__31;
|
||||
view__0->addView(view__31);
|
||||
view__31->setVisible(false);
|
||||
auto* const view__32 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14);
|
||||
view__31->addView(view__32);
|
||||
auto* const view__33 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
|
||||
auto* const view__32 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelGeneral] = view__32;
|
||||
view__0->addView(view__32);
|
||||
view__32->setVisible(false);
|
||||
auto* const view__33 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14);
|
||||
view__32->addView(view__33);
|
||||
auto* const view__34 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
|
||||
view__32->addView(view__34);
|
||||
auto* const view__35 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
|
||||
view__32->addView(view__35);
|
||||
auto* const view__36 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
|
||||
view__32->addView(view__36);
|
||||
auto* const view__37 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
|
||||
view__32->addView(view__37);
|
||||
auto* const view__38 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
|
||||
infoCurvesLabel_ = view__38;
|
||||
view__32->addView(view__38);
|
||||
auto* const view__39 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
|
||||
infoMastersLabel_ = view__39;
|
||||
view__32->addView(view__39);
|
||||
auto* const view__40 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
|
||||
infoGroupsLabel_ = view__40;
|
||||
view__32->addView(view__40);
|
||||
auto* const view__41 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
|
||||
infoRegionsLabel_ = view__41;
|
||||
view__32->addView(view__41);
|
||||
auto* const view__42 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
|
||||
infoSamplesLabel_ = view__42;
|
||||
view__32->addView(view__42);
|
||||
auto* const view__43 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelControls] = view__43;
|
||||
view__0->addView(view__43);
|
||||
view__43->setVisible(false);
|
||||
auto* const view__44 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
|
||||
view__43->addView(view__44);
|
||||
auto* const view__45 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12);
|
||||
controlsPanel_ = view__45;
|
||||
auto* const view__34 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
|
||||
view__33->addView(view__34);
|
||||
auto* const view__35 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
|
||||
view__33->addView(view__35);
|
||||
auto* const view__36 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
|
||||
view__33->addView(view__36);
|
||||
auto* const view__37 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
|
||||
view__33->addView(view__37);
|
||||
auto* const view__38 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
|
||||
view__33->addView(view__38);
|
||||
auto* const view__39 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
|
||||
infoCurvesLabel_ = view__39;
|
||||
view__33->addView(view__39);
|
||||
auto* const view__40 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
|
||||
infoMastersLabel_ = view__40;
|
||||
view__33->addView(view__40);
|
||||
auto* const view__41 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
|
||||
infoGroupsLabel_ = view__41;
|
||||
view__33->addView(view__41);
|
||||
auto* const view__42 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
|
||||
infoRegionsLabel_ = view__42;
|
||||
view__33->addView(view__42);
|
||||
auto* const view__43 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
|
||||
infoSamplesLabel_ = view__43;
|
||||
view__33->addView(view__43);
|
||||
auto* const view__44 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelControls] = view__44;
|
||||
view__0->addView(view__44);
|
||||
view__44->setVisible(false);
|
||||
auto* const view__45 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
|
||||
view__44->addView(view__45);
|
||||
auto* const view__46 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelSettings] = view__46;
|
||||
view__0->addView(view__46);
|
||||
auto* const view__47 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12);
|
||||
view__46->addView(view__47);
|
||||
auto* const view__48 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12);
|
||||
oversamplingSlider_ = view__48;
|
||||
auto* const view__46 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12);
|
||||
controlsPanel_ = view__46;
|
||||
view__45->addView(view__46);
|
||||
auto* const view__47 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14);
|
||||
subPanels_[kPanelSettings] = view__47;
|
||||
view__0->addView(view__47);
|
||||
auto* const view__48 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12);
|
||||
view__47->addView(view__48);
|
||||
auto* const view__49 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12);
|
||||
view__47->addView(view__49);
|
||||
auto* const view__50 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12);
|
||||
view__47->addView(view__50);
|
||||
auto* const view__51 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12);
|
||||
preloadSizeSlider_ = view__51;
|
||||
view__47->addView(view__51);
|
||||
auto* const view__52 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12);
|
||||
view__46->addView(view__52);
|
||||
auto* const view__53 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12);
|
||||
view__52->addView(view__53);
|
||||
auto* const view__54 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12);
|
||||
tuningFrequencySlider_ = view__54;
|
||||
view__52->addView(view__54);
|
||||
auto* const view__55 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12);
|
||||
view__52->addView(view__55);
|
||||
auto* const view__56 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14);
|
||||
stretchedTuningSlider_ = view__56;
|
||||
view__52->addView(view__56);
|
||||
auto* const view__57 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12);
|
||||
view__52->addView(view__57);
|
||||
auto* const view__58 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
|
||||
view__52->addView(view__58);
|
||||
auto* const view__59 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
|
||||
scalaFileButton_ = view__59;
|
||||
view__52->addView(view__59);
|
||||
auto* const view__60 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootKeySlider_ = view__60;
|
||||
view__52->addView(view__60);
|
||||
auto* const view__61 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootOctaveSlider_ = view__61;
|
||||
view__52->addView(view__61);
|
||||
auto* const view__62 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12);
|
||||
scalaResetButton_ = view__62;
|
||||
view__52->addView(view__62);
|
||||
auto* const view__63 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12);
|
||||
userFilesGroup_ = view__63;
|
||||
view__46->addView(view__63);
|
||||
auto* const view__64 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12);
|
||||
view__63->addView(view__64);
|
||||
auto* const view__65 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
|
||||
userFilesDirButton_ = view__65;
|
||||
view__63->addView(view__65);
|
||||
auto* const view__66 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12);
|
||||
view__46->addView(view__66);
|
||||
auto* const view__67 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
|
||||
sampleQualitySlider_ = view__67;
|
||||
view__66->addView(view__67);
|
||||
auto* const view__68 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
|
||||
view__66->addView(view__68);
|
||||
auto* const view__69 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
|
||||
view__66->addView(view__69);
|
||||
auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
|
||||
oscillatorQualitySlider_ = view__70;
|
||||
view__66->addView(view__70);
|
||||
auto* const view__71 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12);
|
||||
view__46->addView(view__71);
|
||||
view__71->setVisible(false);
|
||||
auto* const view__72 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
|
||||
themeMenu_ = view__72;
|
||||
view__71->addView(view__72);
|
||||
auto* const view__73 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__73;
|
||||
view__0->addView(view__73);
|
||||
auto* const view__49 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12);
|
||||
oversamplingSlider_ = view__49;
|
||||
view__48->addView(view__49);
|
||||
auto* const view__50 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12);
|
||||
view__48->addView(view__50);
|
||||
auto* const view__51 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12);
|
||||
view__48->addView(view__51);
|
||||
auto* const view__52 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12);
|
||||
preloadSizeSlider_ = view__52;
|
||||
view__48->addView(view__52);
|
||||
auto* const view__53 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12);
|
||||
view__47->addView(view__53);
|
||||
auto* const view__54 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12);
|
||||
view__53->addView(view__54);
|
||||
auto* const view__55 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12);
|
||||
tuningFrequencySlider_ = view__55;
|
||||
view__53->addView(view__55);
|
||||
auto* const view__56 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12);
|
||||
view__53->addView(view__56);
|
||||
auto* const view__57 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14);
|
||||
stretchedTuningSlider_ = view__57;
|
||||
view__53->addView(view__57);
|
||||
auto* const view__58 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12);
|
||||
view__53->addView(view__58);
|
||||
auto* const view__59 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
|
||||
view__53->addView(view__59);
|
||||
auto* const view__60 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
|
||||
scalaFileButton_ = view__60;
|
||||
view__53->addView(view__60);
|
||||
auto* const view__61 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootKeySlider_ = view__61;
|
||||
view__53->addView(view__61);
|
||||
auto* const view__62 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12);
|
||||
scalaRootOctaveSlider_ = view__62;
|
||||
view__53->addView(view__62);
|
||||
auto* const view__63 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12);
|
||||
scalaResetButton_ = view__63;
|
||||
view__53->addView(view__63);
|
||||
auto* const view__64 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12);
|
||||
userFilesGroup_ = view__64;
|
||||
view__47->addView(view__64);
|
||||
auto* const view__65 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12);
|
||||
view__64->addView(view__65);
|
||||
auto* const view__66 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
|
||||
userFilesDirButton_ = view__66;
|
||||
view__64->addView(view__66);
|
||||
auto* const view__67 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12);
|
||||
view__47->addView(view__67);
|
||||
auto* const view__68 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
|
||||
sampleQualitySlider_ = view__68;
|
||||
view__67->addView(view__68);
|
||||
auto* const view__69 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
|
||||
view__67->addView(view__69);
|
||||
auto* const view__70 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
|
||||
view__67->addView(view__70);
|
||||
auto* const view__71 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
|
||||
oscillatorQualitySlider_ = view__71;
|
||||
view__67->addView(view__71);
|
||||
auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12);
|
||||
view__47->addView(view__72);
|
||||
view__72->setVisible(false);
|
||||
auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
|
||||
themeMenu_ = view__73;
|
||||
view__72->addView(view__73);
|
||||
auto* const view__74 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__74;
|
||||
view__0->addView(view__74);
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ if(SFIZZ_LV2_UI)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Define a preprocessor variable to indicate a build with UI enabled
|
||||
if(SFIZZ_LV2_UI)
|
||||
target_compile_definitions(${LV2PLUGIN_PRJ_NAME} PRIVATE "SFIZZ_LV2_UI=1")
|
||||
target_compile_definitions(${LV2PLUGIN_PRJ_NAME}_ui PRIVATE "SFIZZ_LV2_UI=1")
|
||||
endif()
|
||||
|
||||
# Remove the "lib" prefix, rename the target name and build it in the .lv build dir
|
||||
# <build_dir>/lv2/<plugin_name>_lv2.<ext> to
|
||||
# <build_dir>/lv2/<plugin_name>.lv2/<plugin_name>.<ext>
|
||||
|
|
|
|||
|
|
@ -121,9 +121,9 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
|
|||
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
||||
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
||||
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
|
||||
self->sfizz_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
|
||||
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
|
||||
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
|
||||
self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel);
|
||||
self->time_position_uri = map->map(map->handle, LV2_TIME__Position);
|
||||
self->time_bar_uri = map->map(map->handle, LV2_TIME__bar);
|
||||
self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat);
|
||||
|
|
@ -532,6 +532,9 @@ activate(LV2_Handle instance)
|
|||
sfizz_set_samples_per_block(self->synth, self->max_block_size);
|
||||
sfizz_set_sample_rate(self->synth, self->sample_rate);
|
||||
self->must_update_midnam.store(1);
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
self->rms_follower.init(self->sample_rate);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -576,6 +579,30 @@ sfizz_lv2_send_controller(sfizz_plugin_t *self, LV2_Atom_Forge* forge, unsigned
|
|||
lv2_atom_forge_pop(forge, &frame);
|
||||
}
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
static void
|
||||
sfizz_lv2_send_levels(sfizz_plugin_t *self, LV2_Atom_Forge* forge, float left, float right)
|
||||
{
|
||||
const float levels[] = {left, right};
|
||||
uint32_t num_levels = sizeof(levels) / sizeof(levels[0]);
|
||||
|
||||
bool write_ok = lv2_atom_forge_frame_time(forge, 0);
|
||||
|
||||
LV2_Atom_Vector *vector = nullptr;
|
||||
if (write_ok) {
|
||||
LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector(forge, sizeof(float), self->atom_float_uri, num_levels, levels);
|
||||
write_ok = ref;
|
||||
if (write_ok)
|
||||
vector = (LV2_Atom_Vector *)lv2_atom_forge_deref(forge, ref);
|
||||
}
|
||||
|
||||
if (write_ok)
|
||||
vector->atom.type = self->sfizz_audio_level_uri;
|
||||
|
||||
(void)write_ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj)
|
||||
{
|
||||
|
|
@ -1046,6 +1073,20 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
|
||||
spin_mutex_unlock(self->synth_mutex);
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
if (self->ui_active)
|
||||
{
|
||||
self->rms_follower.process(self->output_buffers[0], self->output_buffers[1], sample_count);
|
||||
const simde__m128 rms = self->rms_follower.getRMS();
|
||||
const float *levels = (const float *)&rms;
|
||||
sfizz_lv2_send_levels(self, &self->forge_notify, levels[0], levels[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->rms_follower.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
lv2_atom_forge_pop(&self->forge_notify, ¬ify_frame);
|
||||
lv2_atom_forge_pop(&self->forge_automate, &automate_frame);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@
|
|||
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
|
||||
// OSC atoms
|
||||
#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob"
|
||||
// Level atoms
|
||||
#define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel"
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
@ -89,6 +91,10 @@ bool sfizz_lv2_fetch_description(
|
|||
sfizz_plugin_t *self, const int *serial,
|
||||
uint8_t **descp, uint32_t *sizep, int *serialp);
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active);
|
||||
#endif
|
||||
|
||||
// Mapping URID to CC and vice-versa
|
||||
struct sfizz_lv2_ccmap;
|
||||
sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@ bool sfizz_lv2_fetch_description(
|
|||
return true;
|
||||
}
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
void sfizz_lv2_set_ui_active(sfizz_plugin_t *self, bool ui_active)
|
||||
{
|
||||
self->ui_active = ui_active;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sfizz_lv2_ccmap {
|
||||
LV2_URID *cc_to_urid;
|
||||
int *urid_to_cc;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "sfizz_lv2.h"
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
#include "plugin/RMSFollower.h"
|
||||
#endif
|
||||
#include <spin_mutex.h>
|
||||
#include <sfizz.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -89,6 +92,7 @@ struct sfizz_plugin_t
|
|||
LV2_URID sfizz_check_modification_uri {};
|
||||
LV2_URID sfizz_active_voices_uri {};
|
||||
LV2_URID sfizz_osc_blob_uri {};
|
||||
LV2_URID sfizz_audio_level_uri {};
|
||||
LV2_URID time_position_uri {};
|
||||
LV2_URID time_bar_uri {};
|
||||
LV2_URID time_bar_beat_uri {};
|
||||
|
|
@ -145,4 +149,10 @@ struct sfizz_plugin_t
|
|||
|
||||
// OSC
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE] {};
|
||||
|
||||
#if defined(SFIZZ_LV2_UI)
|
||||
// UI
|
||||
volatile bool ui_active = false;
|
||||
RMSFollower rms_follower;
|
||||
#endif
|
||||
};
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
|
|||
LV2_URID sfizz_sfz_file_uri;
|
||||
LV2_URID sfizz_scala_file_uri;
|
||||
LV2_URID sfizz_osc_blob_uri;
|
||||
LV2_URID sfizz_audio_level_uri;
|
||||
std::unique_ptr<sfizz_lv2_ccmap, sfizz_lv2_ccmap_delete> ccmap;
|
||||
|
||||
uint8_t osc_temp[OSC_TEMP_SIZE];
|
||||
|
|
@ -217,6 +218,7 @@ instantiate(const LV2UI_Descriptor *descriptor,
|
|||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
|
||||
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob);
|
||||
self->sfizz_audio_level_uri = map->map(map->handle, SFIZZ__AudioLevel);
|
||||
self->ccmap.reset(sfizz_lv2_ccmap_create(map));
|
||||
|
||||
// set up the resource path
|
||||
|
|
@ -287,6 +289,7 @@ static void
|
|||
cleanup(LV2UI_Handle ui)
|
||||
{
|
||||
sfizz_ui_t *self = (sfizz_ui_t *)ui;
|
||||
sfizz_lv2_set_ui_active(self->plugin, false);
|
||||
delete self;
|
||||
}
|
||||
|
||||
|
|
@ -391,6 +394,27 @@ port_event(LV2UI_Handle ui,
|
|||
if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
|
||||
self->uiReceiveMessage(path, sig, args);
|
||||
}
|
||||
else if (atom->type == self->sfizz_audio_level_uri) {
|
||||
const LV2_Atom_Vector *vector = reinterpret_cast<const LV2_Atom_Vector *>(atom);
|
||||
|
||||
if (vector->body.child_type == self->atom_float_uri &&
|
||||
vector->body.child_size == sizeof(float))
|
||||
{
|
||||
const uint8_t *vec_body = reinterpret_cast<const uint8_t*>(
|
||||
LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vector));
|
||||
const uint8_t *vec_end = reinterpret_cast<const uint8_t*>(
|
||||
LV2_ATOM_BODY_CONST(&vector->atom)) + vector->atom.size;
|
||||
|
||||
const float *levels = reinterpret_cast<const float *>(vec_body);
|
||||
uint32_t count = static_cast<uint32_t>((vec_end - vec_body) / sizeof(float));
|
||||
|
||||
float left = (count > 0) ? levels[0] : 0.0f;
|
||||
float right = (count > 1) ? levels[1] : 0.0f;
|
||||
|
||||
self->uiReceiveValue(EditId::LeftLevel, left);
|
||||
self->uiReceiveValue(EditId::RightLevel, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)buffer_size;
|
||||
|
|
@ -466,6 +490,8 @@ idle(LV2UI_Handle ui)
|
|||
(void)self;
|
||||
#endif
|
||||
|
||||
sfizz_lv2_set_ui_active(self->plugin, self->uiFrame->isVisible());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,4 +12,4 @@
|
|||
lv2:optionalFeature ui:touch ;
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:requiredFeature urid:unmap ;
|
||||
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .
|
||||
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .
|
||||
|
|
|
|||
|
|
@ -92,6 +92,22 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
Vst::kRootUnitId, shortTitle));
|
||||
}
|
||||
|
||||
// Volume levels
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidLeftLevel).createParameter(
|
||||
Steinberg::String("Left level"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidRightLevel).createParameter(
|
||||
Steinberg::String("Right level"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
|
||||
// Editor status
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidEditorOpen).createParameter(
|
||||
Steinberg::String("Editor open"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kIsReadOnly|Vst::ParameterInfo::kIsHidden, Vst::kRootUnitId));
|
||||
|
||||
// Initial MIDI mapping
|
||||
for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) {
|
||||
Vst::ParamID id = Vst::kNoParamId;
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
|
|||
uiReceiveValue(EditId::UserFilesDir, userFilesDir.value_or(fs::path()).u8string());
|
||||
uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string());
|
||||
|
||||
updateEditorIsOpenParameter();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +155,16 @@ void PLUGIN_API SfizzVstEditor::close()
|
|||
std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
|
||||
noteEventQueue_.reset();
|
||||
}
|
||||
|
||||
updateEditorIsOpenParameter();
|
||||
}
|
||||
|
||||
void SfizzVstEditor::updateEditorIsOpenParameter()
|
||||
{
|
||||
SfizzVstController* ctrl = getController();
|
||||
bool editorIsOpen = frame && frame->isVisible();
|
||||
ctrl->setParamNormalized(kPidEditorOpen, editorIsOpen);
|
||||
ctrl->performEdit(kPidEditorOpen, editorIsOpen);
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -180,6 +192,7 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
|
|||
if (message == CVSTGUITimer::kMsgTimer) {
|
||||
processOscQueue();
|
||||
processNoteEventQueue();
|
||||
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -296,6 +309,12 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
case kPidOscillatorQuality:
|
||||
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
|
||||
break;
|
||||
case kPidLeftLevel:
|
||||
uiReceiveValue(EditId::LeftLevel, range.denormalize(value));
|
||||
break;
|
||||
case kPidRightLevel:
|
||||
uiReceiveValue(EditId::RightLevel, range.denormalize(value));
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int cc = int(id - kPidCC0);
|
||||
|
|
|
|||
|
|
@ -37,15 +37,14 @@ public:
|
|||
return static_cast<SfizzVstController*>(Vst::VSTGUIEditor::getController());
|
||||
}
|
||||
|
||||
void updateEditorIsOpenParameter();
|
||||
|
||||
// VSTGUIEditor
|
||||
CMessageResult notify(CBaseObject* sender, const char* message) override;
|
||||
// FObject
|
||||
void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override;
|
||||
|
||||
//
|
||||
void updateState(const SfizzVstState& state);
|
||||
void updatePlayState(const SfizzPlayState& playState);
|
||||
|
||||
private:
|
||||
void processOscQueue();
|
||||
void processNoteEventQueue();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ enum {
|
|||
kPidPitchBend,
|
||||
kPidCC0,
|
||||
kPidCCLast = kPidCC0 + sfz::config::numCCs - 1,
|
||||
kPidLeftLevel,
|
||||
kPidRightLevel,
|
||||
kPidEditorOpen,
|
||||
/* Reserved */
|
||||
kNumParameters,
|
||||
};
|
||||
|
|
@ -78,6 +81,12 @@ struct SfizzRange {
|
|||
return {0.0, 0.0, 1.0};
|
||||
case kPidPitchBend:
|
||||
return {0.0, -1.0, 1.0};
|
||||
case kPidLeftLevel:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidRightLevel:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidEditorOpen:
|
||||
return {0.0, 0.0, 1.0};
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast)
|
||||
return {0.0, 0.0, 1.0};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
|
|||
static const char* kMsgIdReceiveMessage = "ReceiveMessage";
|
||||
static const char* kMsgIdNoteEvents = "NoteEvents";
|
||||
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 };
|
||||
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 };
|
||||
|
||||
SfizzVstProcessor::SfizzVstProcessor()
|
||||
: _oscTemp(new uint8_t[kOscTempSize]),
|
||||
|
|
@ -106,6 +106,8 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
|
|||
|
||||
_noteEventsCurrentCycle.fill(-1.0f);
|
||||
|
||||
_editorIsOpen = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -221,6 +223,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
|||
if (state) {
|
||||
synth->setSampleRate(processSetup.sampleRate);
|
||||
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
|
||||
_rmsFollower.init(processSetup.sampleRate);
|
||||
initializeEventProcessor(processSetup, kNumParameters);
|
||||
startBackgroundWork();
|
||||
} else {
|
||||
|
|
@ -286,6 +289,24 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
|
||||
synth.renderBlock(outputs, numFrames, numChannels);
|
||||
|
||||
// Update levels, if editor is open, otherwise skip
|
||||
RMSFollower& rmsFollower = _rmsFollower;
|
||||
if (_editorIsOpen) {
|
||||
rmsFollower.process(outputs[0], outputs[1], numFrames);
|
||||
simde__m128 rms = _rmsFollower.getRMS();
|
||||
float left = reinterpret_cast<float*>(&rms)[0];
|
||||
float right = reinterpret_cast<float*>(&rms)[1];
|
||||
if (Vst::IParameterChanges* pcs = data.outputParameterChanges) {
|
||||
int32 index;
|
||||
if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidLeftLevel, index))
|
||||
vq->addPoint(0, left, index);
|
||||
if (Vst::IParamValueQueue* vq = pcs->addParameterData(kPidRightLevel, index))
|
||||
vq->addPoint(0, right, index);
|
||||
}
|
||||
}
|
||||
else
|
||||
rmsFollower.clear();
|
||||
|
||||
// Request OSC updates
|
||||
sfz::Client& client = *_client;
|
||||
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
|
||||
|
|
@ -388,6 +409,9 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id
|
|||
case kPidPitchBend:
|
||||
synth.hdPitchWheel(sampleOffset, range.denormalize(value));
|
||||
break;
|
||||
case kPidEditorOpen:
|
||||
_editorIsOpen = value != 0;
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int32 ccNumber = static_cast<int32>(id - kPidCC0);
|
||||
|
|
@ -713,7 +737,7 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
|
|||
sendMessage(notification);
|
||||
}
|
||||
|
||||
if (idleCounter % 10 == 0) {
|
||||
if (idleCounter % 25 == 0) {
|
||||
if (_synth->shouldReloadFile()) {
|
||||
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
|
||||
std::lock_guard<SpinMutex> lock(_processMutex);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
#include "SfizzVstState.h"
|
||||
#include "OrderedEventProcessor.h"
|
||||
#include "plugin/RMSFollower.h"
|
||||
#include "sfizz/RTSemaphore.h"
|
||||
#include "ring_buffer/ring_buffer.h"
|
||||
#include "public.sdk/source/vst/vstaudioeffect.h"
|
||||
|
|
@ -63,6 +64,10 @@ private:
|
|||
// whether allowed to perform events (owns the processing lock)
|
||||
bool _canPerformEventsAndParameters {};
|
||||
|
||||
// level meters
|
||||
RMSFollower _rmsFollower;
|
||||
bool _editorIsOpen = false;
|
||||
|
||||
// client
|
||||
sfz::ClientPtr _client;
|
||||
std::unique_ptr<uint8_t[]> _oscTemp;
|
||||
|
|
|
|||
|
|
@ -296,9 +296,6 @@ if(SFIZZ_USE_SNDFILE)
|
|||
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1")
|
||||
target_link_libraries(sfizz_internal PUBLIC st_audiofile)
|
||||
endif()
|
||||
if(WIN32)
|
||||
target_compile_definitions(sfizz_internal PRIVATE _USE_MATH_DEFINES)
|
||||
endif()
|
||||
if(SFIZZ_RELEASE_ASSERTS)
|
||||
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
|
||||
endif()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue