Volume meter for plugin editors

This commit is contained in:
Jean Pierre Cimalando 2021-04-14 06:36:17 +02:00
parent 9411bd458d
commit 9952d28b97
23 changed files with 514 additions and 134 deletions

View file

@ -45,6 +45,11 @@ if(WIN32)
add_compile_definitions(_WIN32_WINNT=0x601) add_compile_definitions(_WIN32_WINNT=0x601)
endif() endif()
# Define the math constants everywhere
if(WIN32)
add_compile_definitions(_USE_MATH_DEFINES)
endif()
# Set macOS compatibility level # Set macOS compatibility level
if(APPLE) if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9")

View file

@ -1,4 +1,5 @@
add_library(plugins-common STATIC EXCLUDE_FROM_ALL add_library(plugins-common STATIC EXCLUDE_FROM_ALL
"common/plugin/RMSFollower.h"
"common/plugin/MessageUtils.h" "common/plugin/MessageUtils.h"
"common/plugin/MessageUtils.cpp" "common/plugin/MessageUtils.cpp"
"common/plugin/InstrumentDescription.h" "common/plugin/InstrumentDescription.h"
@ -28,7 +29,7 @@ endif()
target_include_directories(plugins-common PUBLIC "common") target_include_directories(plugins-common PUBLIC "common")
target_link_libraries(plugins-common target_link_libraries(plugins-common
PUBLIC sfizz::spin_mutex PUBLIC sfizz::spin_mutex
PUBLIC sfizz::filesystem absl::strings PUBLIC sfizz::simde sfizz::filesystem absl::strings
PRIVATE sfizz::pugixml PRIVATE sfizz::pugixml
PRIVATE sfizz::internal sfizz::sfizz) PRIVATE sfizz::internal sfizz::sfizz)
add_library(sfizz::plugins-common ALIAS plugins-common) add_library(sfizz::plugins-common ALIAS plugins-common)

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

View file

@ -134,10 +134,6 @@ widget_class mainView {open
xywh {610 70 60 5} labelsize 12 hide xywh {610 70 60 5} labelsize 12 hide
class ValueLabel class ValueLabel
} }
Fl_Box {} {
xywh {745 20 35 55} box BORDER_BOX
class VMeter
}
Fl_Box volumeCCKnob_ { Fl_Box volumeCCKnob_ {
label Volume label Volume
comment {tag=kTagSetCCVolume} comment {tag=kTagSetCCVolume}
@ -150,6 +146,14 @@ widget_class mainView {open
xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17 xywh {655 10 70 90} box BORDER_BOX labelsize 12 align 17
class KnobCCBox 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]} { Fl_Group {subPanels_[kPanelGeneral]} {

View file

@ -38,6 +38,9 @@ enum class EditId : int {
CC_RANGE(ControllerDefault), CC_RANGE(ControllerDefault),
CC_RANGE(ControllerLabel), CC_RANGE(ControllerLabel),
// //
LeftLevel,
RightLevel,
//
UINumCurves, UINumCurves,
UINumMasters, UINumMasters,
UINumGroups, UINumGroups,

View file

@ -153,6 +153,9 @@ struct Editor::Impl : EditorController::Receiver,
SKnobCCBox* volumeCCKnob_ = nullptr; SKnobCCBox* volumeCCKnob_ = nullptr;
SKnobCCBox* panCCKnob_ = nullptr; SKnobCCBox* panCCKnob_ = nullptr;
SLevelMeter* leftMeter_ = nullptr;
SLevelMeter* rightMeter_ = nullptr;
SAboutDialog* aboutDialog_ = nullptr; SAboutDialog* aboutDialog_ = nullptr;
SharedPointer<CBitmap> backgroundBitmap_; SharedPointer<CBitmap> backgroundBitmap_;
@ -504,6 +507,20 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
updateBackgroundImage(value.c_str()); updateBackgroundImage(value.c_str());
} }
break; 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: default:
if (editIdIsKey(id)) { if (editIdIsKey(id)) {
const int key = keyForEditId(id); const int key = keyForEditId(id);
@ -703,11 +720,15 @@ void Editor::Impl::createFrameContents()
lbl->setFont(font); lbl->setFont(font);
return lbl; return lbl;
}; };
auto createVMeter = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { auto createVMeter = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
// TODO the volume meter... SLevelMeter* meter = new SLevelMeter(bounds);
CViewContainer* container = new CViewContainer(bounds); meter->setNormalFillColor(CColor(0x00, 0xaa, 0x11));
container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); meter->setDangerFillColor(CColor(0xaa, 0x00, 0x00));
return container; OnThemeChanged.push_back([meter, palette]() {
meter->setFrameColor(palette->inactiveText);
meter->setBackColor(palette->knobInactiveTrack);
});
return meter;
}; };
#if 0 #if 0
auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) { auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) {

View file

@ -979,6 +979,75 @@ void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl)
panel_->EndEditFunction(pControl->getTag()); 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) SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color)
: CView(size), color_(color) : CView(size), color_(color)

View file

@ -365,6 +365,41 @@ private:
SharedPointer<CVSTGUITimer> relayoutTrigger_; 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 { class SPlaceHolder : public CView {
public: public:

View file

@ -72,124 +72,128 @@ view__26->setVisible(false);
auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12); auto* const view__27 = createValueLabel(CRect(40, 65, 100, 70), -1, "Center", kCenterText, 12);
view__25->addView(view__27); view__25->addView(view__27);
view__27->setVisible(false); 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); view__25->addView(view__28);
auto* const view__29 = createKnobCCBox(CRect(10, 5, 80, 95), kTagSetCCVolume, "Volume", kCenterText, 12); auto* const view__29 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12);
volumeCCKnob_ = view__29; panCCKnob_ = view__29;
view__25->addView(view__29); view__25->addView(view__29);
auto* const view__30 = createKnobCCBox(CRect(85, 5, 155, 95), kTagSetCCPan, "Pan", kCenterText, 12); auto* const view__30 = createVMeter(CRect(170, 5, 185, 95), -1, "", kCenterText, 14);
panCCKnob_ = view__30; leftMeter_ = view__30;
view__25->addView(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); enterPalette(defaultPalette);
auto* const view__31 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14); auto* const view__32 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
subPanels_[kPanelGeneral] = view__31; subPanels_[kPanelGeneral] = view__32;
view__0->addView(view__31); view__0->addView(view__32);
view__31->setVisible(false); view__32->setVisible(false);
auto* const view__32 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14); auto* const view__33 = 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);
view__32->addView(view__33); view__32->addView(view__33);
auto* const view__34 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14); auto* const view__34 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
view__32->addView(view__34); view__33->addView(view__34);
auto* const view__35 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14); auto* const view__35 = createLabel(CRect(15, 35, 75, 60), -1, "Masters:", kLeftText, 14);
view__32->addView(view__35); view__33->addView(view__35);
auto* const view__36 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14); auto* const view__36 = createLabel(CRect(15, 60, 75, 85), -1, "Groups:", kLeftText, 14);
view__32->addView(view__36); view__33->addView(view__36);
auto* const view__37 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14); auto* const view__37 = createLabel(CRect(15, 85, 75, 110), -1, "Regions:", kLeftText, 14);
view__32->addView(view__37); view__33->addView(view__37);
auto* const view__38 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14); auto* const view__38 = createLabel(CRect(15, 110, 75, 135), -1, "Samples:", kLeftText, 14);
infoCurvesLabel_ = view__38; view__33->addView(view__38);
view__32->addView(view__38); auto* const view__39 = createLabel(CRect(115, 10, 155, 35), -1, "0", kCenterText, 14);
auto* const view__39 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14); infoCurvesLabel_ = view__39;
infoMastersLabel_ = view__39; view__33->addView(view__39);
view__32->addView(view__39); auto* const view__40 = createLabel(CRect(115, 35, 155, 60), -1, "0", kCenterText, 14);
auto* const view__40 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14); infoMastersLabel_ = view__40;
infoGroupsLabel_ = view__40; view__33->addView(view__40);
view__32->addView(view__40); auto* const view__41 = createLabel(CRect(115, 60, 155, 85), -1, "0", kCenterText, 14);
auto* const view__41 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14); infoGroupsLabel_ = view__41;
infoRegionsLabel_ = view__41; view__33->addView(view__41);
view__32->addView(view__41); auto* const view__42 = createLabel(CRect(115, 85, 155, 110), -1, "0", kCenterText, 14);
auto* const view__42 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14); infoRegionsLabel_ = view__42;
infoSamplesLabel_ = view__42; view__33->addView(view__42);
view__32->addView(view__42); auto* const view__43 = createLabel(CRect(115, 110, 155, 135), -1, "0", kCenterText, 14);
auto* const view__43 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); infoSamplesLabel_ = view__43;
subPanels_[kPanelControls] = view__43; view__33->addView(view__43);
view__0->addView(view__43); auto* const view__44 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
view__43->setVisible(false); subPanels_[kPanelControls] = view__44;
auto* const view__44 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); view__0->addView(view__44);
view__43->addView(view__44); view__44->setVisible(false);
auto* const view__45 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12); auto* const view__45 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
controlsPanel_ = view__45;
view__44->addView(view__45); view__44->addView(view__45);
auto* const view__46 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); auto* const view__46 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 12);
subPanels_[kPanelSettings] = view__46; controlsPanel_ = view__46;
view__0->addView(view__46); view__45->addView(view__46);
auto* const view__47 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12); auto* const view__47 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14);
view__46->addView(view__47); subPanels_[kPanelSettings] = view__47;
auto* const view__48 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12); view__0->addView(view__47);
oversamplingSlider_ = view__48; auto* const view__48 = createTitleGroup(CRect(300, 26, 495, 126), -1, "Engine", kCenterText, 12);
view__47->addView(view__48); view__47->addView(view__48);
auto* const view__49 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12); auto* const view__49 = createValueMenu(CRect(25, 60, 85, 85), kTagSetOversampling, "", kCenterText, 12);
view__47->addView(view__49); oversamplingSlider_ = view__49;
auto* const view__50 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12); view__48->addView(view__49);
view__47->addView(view__50); auto* const view__50 = createValueLabel(CRect(15, 20, 95, 45), -1, "Oversampling", kCenterText, 12);
auto* const view__51 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12); view__48->addView(view__50);
preloadSizeSlider_ = view__51; auto* const view__51 = createValueLabel(CRect(100, 20, 180, 45), -1, "Preload size", kCenterText, 12);
view__47->addView(view__51); view__48->addView(view__51);
auto* const view__52 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12); auto* const view__52 = createValueMenu(CRect(110, 60, 170, 85), kTagSetPreloadSize, "", kCenterText, 12);
view__46->addView(view__52); preloadSizeSlider_ = view__52;
auto* const view__53 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12); view__48->addView(view__52);
view__52->addView(view__53); auto* const view__53 = createTitleGroup(CRect(170, 161, 585, 261), -1, "Tuning", kCenterText, 12);
auto* const view__54 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12); view__47->addView(view__53);
tuningFrequencySlider_ = view__54; auto* const view__54 = createValueLabel(CRect(155, 20, 235, 45), -1, "Root key", kCenterText, 12);
view__52->addView(view__54); view__53->addView(view__54);
auto* const view__55 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12); auto* const view__55 = createValueMenu(CRect(250, 60, 310, 85), kTagSetTuningFrequency, "", kCenterText, 12);
view__52->addView(view__55); tuningFrequencySlider_ = view__55;
auto* const view__56 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14); view__53->addView(view__55);
stretchedTuningSlider_ = view__56; auto* const view__56 = createValueLabel(CRect(240, 20, 320, 45), -1, "Frequency", kCenterText, 12);
view__52->addView(view__56); view__53->addView(view__56);
auto* const view__57 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12); auto* const view__57 = createStyledKnob(CRect(340, 45, 388, 93), kTagSetStretchedTuning, "", kCenterText, 14);
view__52->addView(view__57); stretchedTuningSlider_ = view__57;
auto* const view__58 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12); view__53->addView(view__57);
view__52->addView(view__58); auto* const view__58 = createValueLabel(CRect(325, 20, 405, 45), -1, "Stretch", kCenterText, 12);
auto* const view__59 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12); view__53->addView(view__58);
scalaFileButton_ = view__59; auto* const view__59 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
view__52->addView(view__59); view__53->addView(view__59);
auto* const view__60 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12); auto* const view__60 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
scalaRootKeySlider_ = view__60; scalaFileButton_ = view__60;
view__52->addView(view__60); view__53->addView(view__60);
auto* const view__61 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12); auto* const view__61 = createValueMenu(CRect(165, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
scalaRootOctaveSlider_ = view__61; scalaRootKeySlider_ = view__61;
view__52->addView(view__61); view__53->addView(view__61);
auto* const view__62 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12); auto* const view__62 = createValueMenu(CRect(200, 60, 230, 85), kTagSetScalaRootKey, "", kCenterText, 12);
scalaResetButton_ = view__62; scalaRootOctaveSlider_ = view__62;
view__52->addView(view__62); view__53->addView(view__62);
auto* const view__63 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12); auto* const view__63 = createResetSomethingButton(CRect(120, 60, 145, 85), kTagResetScalaFile, "", kCenterText, 12);
userFilesGroup_ = view__63; scalaResetButton_ = view__63;
view__46->addView(view__63); view__53->addView(view__63);
auto* const view__64 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12); auto* const view__64 = createTitleGroup(CRect(615, 161, 754, 261), -1, "Files", kCenterText, 12);
view__63->addView(view__64); userFilesGroup_ = view__64;
auto* const view__65 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12); view__47->addView(view__64);
userFilesDirButton_ = view__65; auto* const view__65 = createValueLabel(CRect(20, 20, 120, 45), -1, "User SFZ folder", kCenterText, 12);
view__63->addView(view__65); view__64->addView(view__65);
auto* const view__66 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12); auto* const view__66 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
view__46->addView(view__66); userFilesDirButton_ = view__66;
auto* const view__67 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12); view__64->addView(view__66);
sampleQualitySlider_ = view__67; auto* const view__67 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12);
view__66->addView(view__67); view__47->addView(view__67);
auto* const view__68 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12); auto* const view__68 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
view__66->addView(view__68); sampleQualitySlider_ = view__68;
auto* const view__69 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12); view__67->addView(view__68);
view__66->addView(view__69); auto* const view__69 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12); view__67->addView(view__69);
oscillatorQualitySlider_ = view__70; auto* const view__70 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
view__66->addView(view__70); view__67->addView(view__70);
auto* const view__71 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12); auto* const view__71 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
view__46->addView(view__71); oscillatorQualitySlider_ = view__71;
view__71->setVisible(false); view__67->addView(view__71);
auto* const view__72 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12); auto* const view__72 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12);
themeMenu_ = view__72; view__47->addView(view__72);
view__71->addView(view__72); view__72->setVisible(false);
auto* const view__73 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12); auto* const view__73 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
piano_ = view__73; themeMenu_ = view__73;
view__0->addView(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);

View file

@ -61,6 +61,12 @@ if(SFIZZ_LV2_UI)
endif() endif()
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 # 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.<ext> to
# <build_dir>/lv2/<plugin_name>.lv2/<plugin_name>.<ext> # <build_dir>/lv2/<plugin_name>.lv2/<plugin_name>.<ext>

View file

@ -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_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling); 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_log_status_uri = map->map(map->handle, SFIZZ__logStatus);
self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification); self->sfizz_check_modification_uri = map->map(map->handle, SFIZZ__checkModification);
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); 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_position_uri = map->map(map->handle, LV2_TIME__Position);
self->time_bar_uri = map->map(map->handle, LV2_TIME__bar); self->time_bar_uri = map->map(map->handle, LV2_TIME__bar);
self->time_bar_beat_uri = map->map(map->handle, LV2_TIME__barBeat); 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_samples_per_block(self->synth, self->max_block_size);
sfizz_set_sample_rate(self->synth, self->sample_rate); sfizz_set_sample_rate(self->synth, self->sample_rate);
self->must_update_midnam.store(1); self->must_update_midnam.store(1);
#if defined(SFIZZ_LV2_UI)
self->rms_follower.init(self->sample_rate);
#endif
} }
static void 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); 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 static void
sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, int delay, const LV2_Atom_Object *obj) 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); 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, &notify_frame); lv2_atom_forge_pop(&self->forge_notify, &notify_frame);
lv2_atom_forge_pop(&self->forge_automate, &automate_frame); lv2_atom_forge_pop(&self->forge_automate, &automate_frame);
} }

View file

@ -44,6 +44,8 @@
#define SFIZZ__checkModification SFIZZ_URI ":" "check_modification" #define SFIZZ__checkModification SFIZZ_URI ":" "check_modification"
// OSC atoms // OSC atoms
#define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob" #define SFIZZ__OSCBlob SFIZZ_URI ":" "OSCBlob"
// Level atoms
#define SFIZZ__AudioLevel SFIZZ_URI ":" "AudioLevel"
enum enum
{ {
@ -89,6 +91,10 @@ bool sfizz_lv2_fetch_description(
sfizz_plugin_t *self, const int *serial, sfizz_plugin_t *self, const int *serial,
uint8_t **descp, uint32_t *sizep, int *serialp); 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 // Mapping URID to CC and vice-versa
struct sfizz_lv2_ccmap; struct sfizz_lv2_ccmap;
sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map); sfizz_lv2_ccmap *sfizz_lv2_ccmap_create(LV2_URID_Map* map);

View file

@ -31,6 +31,13 @@ bool sfizz_lv2_fetch_description(
return true; 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 { struct sfizz_lv2_ccmap {
LV2_URID *cc_to_urid; LV2_URID *cc_to_urid;
int *urid_to_cc; int *urid_to_cc;

View file

@ -7,6 +7,9 @@
#pragma once #pragma once
#include "sfizz_lv2.h" #include "sfizz_lv2.h"
#if defined(SFIZZ_LV2_UI)
#include "plugin/RMSFollower.h"
#endif
#include <spin_mutex.h> #include <spin_mutex.h>
#include <sfizz.h> #include <sfizz.h>
#include <stdbool.h> #include <stdbool.h>
@ -89,6 +92,7 @@ struct sfizz_plugin_t
LV2_URID sfizz_check_modification_uri {}; LV2_URID sfizz_check_modification_uri {};
LV2_URID sfizz_active_voices_uri {}; LV2_URID sfizz_active_voices_uri {};
LV2_URID sfizz_osc_blob_uri {}; LV2_URID sfizz_osc_blob_uri {};
LV2_URID sfizz_audio_level_uri {};
LV2_URID time_position_uri {}; LV2_URID time_position_uri {};
LV2_URID time_bar_uri {}; LV2_URID time_bar_uri {};
LV2_URID time_bar_beat_uri {}; LV2_URID time_bar_beat_uri {};
@ -145,4 +149,10 @@ struct sfizz_plugin_t
// OSC // OSC
uint8_t osc_temp[OSC_TEMP_SIZE] {}; uint8_t osc_temp[OSC_TEMP_SIZE] {};
#if defined(SFIZZ_LV2_UI)
// UI
volatile bool ui_active = false;
RMSFollower rms_follower;
#endif
}; };

View file

@ -116,6 +116,7 @@ struct sfizz_ui_t : EditorController, VSTGUIEditorInterface {
LV2_URID sfizz_sfz_file_uri; LV2_URID sfizz_sfz_file_uri;
LV2_URID sfizz_scala_file_uri; LV2_URID sfizz_scala_file_uri;
LV2_URID sfizz_osc_blob_uri; LV2_URID sfizz_osc_blob_uri;
LV2_URID sfizz_audio_level_uri;
std::unique_ptr<sfizz_lv2_ccmap, sfizz_lv2_ccmap_delete> ccmap; std::unique_ptr<sfizz_lv2_ccmap, sfizz_lv2_ccmap_delete> ccmap;
uint8_t osc_temp[OSC_TEMP_SIZE]; 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_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile); self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__tuningfile);
self->sfizz_osc_blob_uri = map->map(map->handle, SFIZZ__OSCBlob); 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)); self->ccmap.reset(sfizz_lv2_ccmap_create(map));
// set up the resource path // set up the resource path
@ -287,6 +289,7 @@ static void
cleanup(LV2UI_Handle ui) cleanup(LV2UI_Handle ui)
{ {
sfizz_ui_t *self = (sfizz_ui_t *)ui; sfizz_ui_t *self = (sfizz_ui_t *)ui;
sfizz_lv2_set_ui_active(self->plugin, false);
delete self; 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) if (sfizz_extract_message(LV2_ATOM_BODY_CONST(atom), atom->size, buffer, sizeof(buffer), &path, &sig, &args) > 0)
self->uiReceiveMessage(path, sig, args); 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; (void)buffer_size;
@ -466,6 +490,8 @@ idle(LV2UI_Handle ui)
(void)self; (void)self;
#endif #endif
sfizz_lv2_set_ui_active(self->plugin, self->uiFrame->isVisible());
return 0; return 0;
} }

View file

@ -12,4 +12,4 @@
lv2:optionalFeature ui:touch ; lv2:optionalFeature ui:touch ;
lv2:requiredFeature urid:map ; lv2:requiredFeature urid:map ;
lv2:requiredFeature urid:unmap ; lv2:requiredFeature urid:unmap ;
lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> . lv2:requiredFeature <http://lv2plug.in/ns/ext/instance-access> .

View file

@ -92,6 +92,22 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
Vst::kRootUnitId, shortTitle)); 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 // Initial MIDI mapping
for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) { for (int32 i = 0; i < Vst::kCountCtrlNumber; ++i) {
Vst::ParamID id = Vst::kNoParamId; Vst::ParamID id = Vst::kNoParamId;

View file

@ -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::UserFilesDir, userFilesDir.value_or(fs::path()).u8string());
uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string()); uiReceiveValue(EditId::FallbackFilesDir, SfizzPaths::getSfzFallbackDefaultPath().u8string());
updateEditorIsOpenParameter();
return true; return true;
} }
@ -153,6 +155,16 @@ void PLUGIN_API SfizzVstEditor::close()
std::lock_guard<std::mutex> lock(noteEventQueueMutex_); std::lock_guard<std::mutex> lock(noteEventQueueMutex_);
noteEventQueue_.reset(); 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) { if (message == CVSTGUITimer::kMsgTimer) {
processOscQueue(); processOscQueue();
processNoteEventQueue(); processNoteEventQueue();
updateEditorIsOpenParameter(); // Note(jpc) for Reaper, it can fail at open time
} }
return result; return result;
@ -296,6 +309,12 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
case kPidOscillatorQuality: case kPidOscillatorQuality:
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value)); uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
break; break;
case kPidLeftLevel:
uiReceiveValue(EditId::LeftLevel, range.denormalize(value));
break;
case kPidRightLevel:
uiReceiveValue(EditId::RightLevel, range.denormalize(value));
break;
default: default:
if (id >= kPidCC0 && id <= kPidCCLast) { if (id >= kPidCC0 && id <= kPidCCLast) {
int cc = int(id - kPidCC0); int cc = int(id - kPidCC0);

View file

@ -37,15 +37,14 @@ public:
return static_cast<SfizzVstController*>(Vst::VSTGUIEditor::getController()); return static_cast<SfizzVstController*>(Vst::VSTGUIEditor::getController());
} }
void updateEditorIsOpenParameter();
// VSTGUIEditor // VSTGUIEditor
CMessageResult notify(CBaseObject* sender, const char* message) override; CMessageResult notify(CBaseObject* sender, const char* message) override;
// FObject // FObject
void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override; void PLUGIN_API update(FUnknown* changedUnknown, int32 message) override;
// //
void updateState(const SfizzVstState& state);
void updatePlayState(const SfizzPlayState& playState);
private: private:
void processOscQueue(); void processOscQueue();
void processNoteEventQueue(); void processNoteEventQueue();

View file

@ -26,6 +26,9 @@ enum {
kPidPitchBend, kPidPitchBend,
kPidCC0, kPidCC0,
kPidCCLast = kPidCC0 + sfz::config::numCCs - 1, kPidCCLast = kPidCC0 + sfz::config::numCCs - 1,
kPidLeftLevel,
kPidRightLevel,
kPidEditorOpen,
/* Reserved */ /* Reserved */
kNumParameters, kNumParameters,
}; };
@ -78,6 +81,12 @@ struct SfizzRange {
return {0.0, 0.0, 1.0}; return {0.0, 0.0, 1.0};
case kPidPitchBend: case kPidPitchBend:
return {0.0, -1.0, 1.0}; 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: default:
if (id >= kPidCC0 && id <= kPidCCLast) if (id >= kPidCC0 && id <= kPidCCLast)
return {0.0, 0.0, 1.0}; return {0.0, 0.0, 1.0};

View file

@ -37,7 +37,7 @@ static const char* kMsgIdSetPreloadSize = "SetPreloadSize";
static const char* kMsgIdReceiveMessage = "ReceiveMessage"; static const char* kMsgIdReceiveMessage = "ReceiveMessage";
static const char* kMsgIdNoteEvents = "NoteEvents"; static const char* kMsgIdNoteEvents = "NoteEvents";
static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 50 }; static constexpr std::chrono::milliseconds kBackgroundIdleInterval { 20 };
SfizzVstProcessor::SfizzVstProcessor() SfizzVstProcessor::SfizzVstProcessor()
: _oscTemp(new uint8_t[kOscTempSize]), : _oscTemp(new uint8_t[kOscTempSize]),
@ -106,6 +106,8 @@ tresult PLUGIN_API SfizzVstProcessor::initialize(FUnknown* context)
_noteEventsCurrentCycle.fill(-1.0f); _noteEventsCurrentCycle.fill(-1.0f);
_editorIsOpen = false;
return result; return result;
} }
@ -221,6 +223,7 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
if (state) { if (state) {
synth->setSampleRate(processSetup.sampleRate); synth->setSampleRate(processSetup.sampleRate);
synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock); synth->setSamplesPerBlock(processSetup.maxSamplesPerBlock);
_rmsFollower.init(processSetup.sampleRate);
initializeEventProcessor(processSetup, kNumParameters); initializeEventProcessor(processSetup, kNumParameters);
startBackgroundWork(); startBackgroundWork();
} else { } else {
@ -286,6 +289,24 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
synth.renderBlock(outputs, numFrames, numChannels); 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 // Request OSC updates
sfz::Client& client = *_client; sfz::Client& client = *_client;
synth.sendMessage(client, 0, "/sw/last/current", "", nullptr); synth.sendMessage(client, 0, "/sw/last/current", "", nullptr);
@ -388,6 +409,9 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id
case kPidPitchBend: case kPidPitchBend:
synth.hdPitchWheel(sampleOffset, range.denormalize(value)); synth.hdPitchWheel(sampleOffset, range.denormalize(value));
break; break;
case kPidEditorOpen:
_editorIsOpen = value != 0;
break;
default: default:
if (id >= kPidCC0 && id <= kPidCCLast) { if (id >= kPidCC0 && id <= kPidCCLast) {
int32 ccNumber = static_cast<int32>(id - kPidCC0); int32 ccNumber = static_cast<int32>(id - kPidCC0);
@ -713,7 +737,7 @@ void SfizzVstProcessor::doBackgroundIdle(size_t idleCounter)
sendMessage(notification); sendMessage(notification);
} }
if (idleCounter % 10 == 0) { if (idleCounter % 25 == 0) {
if (_synth->shouldReloadFile()) { if (_synth->shouldReloadFile()) {
fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n"); fprintf(stderr, "[Sfizz] sfz file has changed, reloading\n");
std::lock_guard<SpinMutex> lock(_processMutex); std::lock_guard<SpinMutex> lock(_processMutex);

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "SfizzVstState.h" #include "SfizzVstState.h"
#include "OrderedEventProcessor.h" #include "OrderedEventProcessor.h"
#include "plugin/RMSFollower.h"
#include "sfizz/RTSemaphore.h" #include "sfizz/RTSemaphore.h"
#include "ring_buffer/ring_buffer.h" #include "ring_buffer/ring_buffer.h"
#include "public.sdk/source/vst/vstaudioeffect.h" #include "public.sdk/source/vst/vstaudioeffect.h"
@ -63,6 +64,10 @@ private:
// whether allowed to perform events (owns the processing lock) // whether allowed to perform events (owns the processing lock)
bool _canPerformEventsAndParameters {}; bool _canPerformEventsAndParameters {};
// level meters
RMSFollower _rmsFollower;
bool _editorIsOpen = false;
// client // client
sfz::ClientPtr _client; sfz::ClientPtr _client;
std::unique_ptr<uint8_t[]> _oscTemp; std::unique_ptr<uint8_t[]> _oscTemp;

View file

@ -296,9 +296,6 @@ if(SFIZZ_USE_SNDFILE)
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1") target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1")
target_link_libraries(sfizz_internal PUBLIC st_audiofile) target_link_libraries(sfizz_internal PUBLIC st_audiofile)
endif() endif()
if(WIN32)
target_compile_definitions(sfizz_internal PRIVATE _USE_MATH_DEFINES)
endif()
if(SFIZZ_RELEASE_ASSERTS) if(SFIZZ_RELEASE_ASSERTS)
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1") target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
endif() endif()