8 changes: 4 additions & 4 deletions dpf/cmake/DPF-plugin.cmake
Expand Up @@ -336,9 +336,9 @@ function(dpf__build_vst2 NAME DGL_LIBRARY)
OUTPUT_NAME "${NAME}"
SUFFIX "")
set(INFO_PLIST_PROJECT_NAME "${NAME}")
configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist"
configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist"
"${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents/Info.plist" @ONLY)
file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo"
file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo"
DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst/Contents")
endif()
endfunction()
Expand Down Expand Up @@ -425,9 +425,9 @@ function(dpf__build_vst3 NAME DGL_LIBRARY)
if(APPLE)
# Uses the same macOS bundle template as VST2
set(INFO_PLIST_PROJECT_NAME "${NAME}")
configure_file("${DPF_ROOT_DIR}/utils/plugin.vst/Contents/Info.plist"
configure_file("${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/Info.plist"
"${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents/Info.plist" @ONLY)
file(COPY "${DPF_ROOT_DIR}/utils/plugin.vst/Contents/PkgInfo"
file(COPY "${DPF_ROOT_DIR}/utils/plugin.bundle/Contents/PkgInfo"
DESTINATION "${PROJECT_BINARY_DIR}/bin/${NAME}.vst3/Contents")
endif()
endfunction()
Expand Down
2 changes: 1 addition & 1 deletion dpf/dgl/Color.hpp
Expand Up @@ -68,7 +68,7 @@ struct Color {
/**
Create a new color based on this one but with a different alpha value.
*/
Color withAlpha(float alpha) noexcept;
Color withAlpha(float alpha) const noexcept;

/**
Create a color specified by hue, saturation and lightness.
Expand Down
33 changes: 28 additions & 5 deletions dpf/dgl/EventHandlers.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -94,7 +94,8 @@ class KnobEventHandler
public:
enum Orientation {
Horizontal,
Vertical
Vertical,
Both
};

// NOTE hover not implemented yet
Expand All @@ -112,13 +113,17 @@ class KnobEventHandler
virtual void knobDragStarted(SubWidget* widget) = 0;
virtual void knobDragFinished(SubWidget* widget) = 0;
virtual void knobValueChanged(SubWidget* widget, float value) = 0;
virtual void knobDoubleClicked(SubWidget*) {};
};

explicit KnobEventHandler(SubWidget* self);
explicit KnobEventHandler(SubWidget* self, const KnobEventHandler& other);
KnobEventHandler& operator=(const KnobEventHandler& other);
virtual ~KnobEventHandler();

// if setStep(1) has been called before, this returns true
bool isInteger() const noexcept;

// returns raw value, is assumed to be scaled if using log
float getValue() const noexcept;

Expand All @@ -139,12 +144,15 @@ class KnobEventHandler
void setUsingLogScale(bool yesNo) noexcept;

Orientation getOrientation() const noexcept;
void setOrientation(const Orientation orientation) noexcept;
void setOrientation(Orientation orientation) noexcept;

void setCallback(Callback* callback) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev);
bool motionEvent(const Widget::MotionEvent& ev);
// default 200, higher means slower
void setMouseDeceleration(float accel) noexcept;

bool mouseEvent(const Widget::MouseEvent& ev, double scaleFactor = 1.0);
bool motionEvent(const Widget::MotionEvent& ev, double scaleFactor = 1.0);
bool scrollEvent(const Widget::ScrollEvent& ev);

protected:
Expand All @@ -168,6 +176,21 @@ class KnobEventHandler

// --------------------------------------------------------------------------------------------------------------------

class SliderEventHandler
{
public:
explicit SliderEventHandler(SubWidget* self);
virtual ~SliderEventHandler();

private:
struct PrivateData;
PrivateData* const pData;

DISTRHO_LEAK_DETECTOR(SliderEventHandler)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_EVENT_HANDLERS_HPP_INCLUDED
Expand Down
71 changes: 71 additions & 0 deletions dpf/dgl/Layout.hpp
@@ -0,0 +1,71 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef DGL_LAYOUT_HPP_INCLUDED
#define DGL_LAYOUT_HPP_INCLUDED

#include "Geometry.hpp"

#include <list>

START_NAMESPACE_DGL

class SubWidget;

// --------------------------------------------------------------------------------------------------------------------

// NOTE: under development, API to be finalized and documented soon

enum SizeHint {
Expanding,
Fixed
};

struct SubWidgetWithSizeHint {
SubWidget* widget;
SizeHint sizeHint;
};

template<bool horizontal>
struct Layout
{
std::list<SubWidgetWithSizeHint> widgets;
uint setAbsolutePos(int x, int y, uint padding);
void setSize(uint size, uint padding);
};

typedef Layout<true> HorizontalLayout;
typedef Layout<false> VerticalLayout;

struct HorizontallyStackedVerticalLayout
{
std::list<VerticalLayout*> items;
Size<uint> adjustSize(uint padding); // TODO
void setAbsolutePos(int x, int y, uint padding);
};

struct VerticallyStackedHorizontalLayout
{
std::list<HorizontalLayout*> items;
Size<uint> adjustSize(uint padding);
void setAbsolutePos(int x, int y, uint padding);
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL

#endif // DGL_LAYOUT_HPP_INCLUDED
1 change: 1 addition & 0 deletions dpf/dgl/Makefile
Expand Up @@ -34,6 +34,7 @@ OBJS_common = \
../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \
../build/dgl/Layout.cpp.o \
../build/dgl/Resources.cpp.o \
../build/dgl/SubWidget.cpp.o \
../build/dgl/SubWidgetPrivateData.cpp.o \
Expand Down
4 changes: 2 additions & 2 deletions dpf/dgl/NanoVG.hpp
Expand Up @@ -591,14 +591,14 @@ class NanoVG
/**
Creates image by loading it from the specified chunk of memory.
*/
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags);
NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags);

/**
Creates image by loading it from the specified chunk of memory.
Overloaded function for convenience.
@see ImageFlags
*/
NanoImage::Handle createImageFromMemory(uchar* data, uint dataSize, int imageFlags);
NanoImage::Handle createImageFromMemory(const uchar* data, uint dataSize, int imageFlags);

/**
Creates image from specified raw format image data.
Expand Down
6 changes: 6 additions & 0 deletions dpf/dgl/SubWidget.hpp
Expand Up @@ -137,6 +137,12 @@ class SubWidget : public Widget
*/
void repaint() noexcept override;

/**
Pushes this widget to the "bottom" of the parent widget.
Makes the widget behave as if it was the first to be registered on the parent widget, thus being "on bottom".
*/
virtual void toBottom();

/**
Bring this widget to the "front" of the parent widget.
Makes the widget behave as if it was the last to be registered on the parent widget, thus being "in front".
Expand Down
28 changes: 26 additions & 2 deletions dpf/dgl/Widget.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand All @@ -19,6 +19,8 @@

#include "Geometry.hpp"

#include <list>

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -62,7 +64,7 @@ class Widget
uint mod;
/** Event flags. @see EventFlag */
uint flags;
/** Event timestamp (if any). */
/** Event timestamp in milliseconds (if any). */
uint time;

/** Constructor for default/null values */
Expand Down Expand Up @@ -331,16 +333,33 @@ class Widget

/**
Get the Id associated with this widget.
Returns 0 by default.
@see setId
*/
uint getId() const noexcept;

/**
Get the name associated with this widget.
This is complately optional, mostly useful for debugging purposes.
Returns an empty string by default.
@see setName
*/
const char* getName() const noexcept;

/**
Set an Id to be associated with this widget.
@see getId
*/
void setId(uint id) noexcept;

/**
Set a name to be associated with this widget.
This is complately optional, only useful for debugging purposes.
@note name must not be null
@see getName
*/
void setName(const char* name) noexcept;

/**
Get the application associated with this widget's window.
This is the same as calling `getTopLevelWidget()->getApp()`.
Expand All @@ -367,6 +386,11 @@ class Widget
*/
TopLevelWidget* getTopLevelWidget() const noexcept;

/**
Get list of children (a subwidgets) that belong to this widget.
*/
std::list<SubWidget*> getChildren() const noexcept;

/**
Request repaint of this widget's area to the window this widget belongs to.
On the raw Widget class this function does nothing.
Expand Down
2 changes: 1 addition & 1 deletion dpf/dgl/src/Color.cpp
Expand Up @@ -114,7 +114,7 @@ Color::Color(const Color& color1, const Color& color2, const float u) noexcept
interpolate(color2, u);
}

Color Color::withAlpha(const float alpha2) noexcept
Color Color::withAlpha(const float alpha2) const noexcept
{
Color color(*this);
color.alpha = alpha2;
Expand Down
140 changes: 96 additions & 44 deletions dpf/dgl/src/EventHandlers.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -274,6 +274,7 @@ struct KnobEventHandler::PrivateData {
SubWidget* const widget;
KnobEventHandler::Callback* callback;

float accel;
float minimum;
float maximum;
float step;
Expand All @@ -287,13 +288,15 @@ struct KnobEventHandler::PrivateData {

double lastX;
double lastY;
uint lastClickTime;

PrivateData(KnobEventHandler* const s, SubWidget* const w)
: self(s),
widget(w),
callback(nullptr),
minimum(0.0f),
maximum(1.0f),
accel(200.f),
minimum(0.f),
maximum(1.f),
step(0.0f),
value(0.5f),
valueDef(value),
Expand All @@ -303,12 +306,14 @@ struct KnobEventHandler::PrivateData {
orientation(Vertical),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}
lastY(0.0),
lastClickTime(0) {}

PrivateData(KnobEventHandler* const s, SubWidget* const w, PrivateData* const other)
: self(s),
widget(w),
callback(other->callback),
accel(other->accel),
minimum(other->minimum),
maximum(other->maximum),
step(other->step),
Expand All @@ -320,11 +325,13 @@ struct KnobEventHandler::PrivateData {
orientation(other->orientation),
state(kKnobStateDefault),
lastX(0.0),
lastY(0.0) {}
lastY(0.0),
lastClickTime(0) {}

void assignFrom(PrivateData* const other)
{
callback = other->callback;
accel = other->accel;
minimum = other->minimum;
maximum = other->maximum;
step = other->step;
Expand All @@ -337,6 +344,7 @@ struct KnobEventHandler::PrivateData {
state = kKnobStateDefault;
lastX = 0.0;
lastY = 0.0;
lastClickTime = 0;
}

inline float logscale(const float v) const
Expand All @@ -353,7 +361,7 @@ struct KnobEventHandler::PrivateData {
return std::log(v/a)/b;
}

bool mouseEvent(const Widget::MouseEvent& ev)
bool mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor)
{
if (ev.button != 1)
return false;
Expand All @@ -370,9 +378,21 @@ struct KnobEventHandler::PrivateData {
return true;
}

lastX = ev.pos.getX() / scaleFactor;
lastY = ev.pos.getY() / scaleFactor;

if (lastClickTime > 0 && ev.time > lastClickTime && ev.time - lastClickTime <= 300)
{
lastClickTime = 0;

if (callback != nullptr)
callback->knobDoubleClicked(widget);

return true;
}

lastClickTime = ev.time;
state |= kKnobStateDragging;
lastX = ev.pos.getX();
lastY = ev.pos.getY();
widget->repaint();

if (callback != nullptr)
Expand All @@ -394,62 +414,87 @@ struct KnobEventHandler::PrivateData {
return false;
}

bool motionEvent(const Widget::MotionEvent& ev)
bool motionEvent(const Widget::MotionEvent& ev, const double scaleFactor)
{
if ((state & kKnobStateDragging) == 0x0)
return false;

bool doVal = false;
float d, value2 = 0.0f;
float movDiff;

if (orientation == Horizontal)
switch (orientation)
{
if (const double movX = ev.pos.getX() - lastX)
case Horizontal:
movDiff = ev.pos.getX() / scaleFactor - lastX;
break;
case Vertical:
movDiff = lastY - ev.pos.getY() / scaleFactor;
break;
case Both:
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movX));
doVal = true;
}
}
else if (orientation == Vertical)
{
if (const double movY = lastY - ev.pos.getY())
{
d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
value2 = (usingLog ? invlogscale(valueTmp) : valueTmp) + (float(maximum - minimum) / d * float(movY));
doVal = true;
const float movDiffX = ev.pos.getX() / scaleFactor - lastX;
const float movDiffY = lastY - ev.pos.getY() / scaleFactor;
movDiff = std::abs(movDiffX) > std::abs(movDiffY) ? movDiffX : movDiffY;
}
break;
default:
return false;
}

if (! doVal)
if (d_isZero(movDiff))
return false;

const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel;
valueTmp += (maximum - minimum) / divisor * movDiff;

if (usingLog)
value2 = logscale(value2);
valueTmp = logscale(valueTmp);

if (value2 < minimum)
float value2;
bool valueChanged = false;

if (valueTmp < minimum)
{
valueTmp = value2 = minimum;
valueChanged = true;
}
else if (value2 > maximum)
else if (valueTmp > maximum)
{
valueTmp = value2 = maximum;
valueChanged = true;
}
else
{
valueTmp = value2;

if (d_isNotZero(step))
{
const float rest = std::fmod(value2, step);
value2 -= rest + (rest > step/2.0f ? step : 0.0f);
if (std::abs(valueTmp - value) >= step)
{
const float rest = std::fmod(valueTmp, step);
valueChanged = true;
value2 = valueTmp - rest;

if (rest < 0 && rest < step * -0.5f)
value2 -= step;
else if (rest > 0 && rest > step * 0.5f)
value2 += step;

if (value2 < minimum)
value2 = minimum;
else if (value2 > maximum)
value2 = maximum;
}
}
else
{
value2 = valueTmp;
valueChanged = true;
}
}

setValue(value2, true);
if (valueChanged)
setValue(value2, true);

lastX = ev.pos.getX();
lastY = ev.pos.getY();
lastX = ev.pos.getX() / scaleFactor;
lastY = ev.pos.getY() / scaleFactor;

return true;
}
Expand All @@ -460,7 +505,7 @@ struct KnobEventHandler::PrivateData {
return false;

const float dir = (ev.delta.getY() > 0.f) ? 1.f : -1.f;
const float d = (ev.mod & kModifierControl) ? 2000.0f : 200.0f;
const float d = (ev.mod & kModifierControl) ? accel * 10.f : accel;
float value2 = (usingLog ? invlogscale(valueTmp) : valueTmp)
+ ((maximum - minimum) / d * 10.f * dir);

Expand Down Expand Up @@ -553,6 +598,11 @@ KnobEventHandler::~KnobEventHandler()
delete pData;
}

bool KnobEventHandler::isInteger() const noexcept
{
return d_isEqual(pData->step, 1.f);
}

float KnobEventHandler::getValue() const noexcept
{
return pData->value;
Expand Down Expand Up @@ -596,9 +646,6 @@ KnobEventHandler::Orientation KnobEventHandler::getOrientation() const noexcept

void KnobEventHandler::setOrientation(const Orientation orientation) noexcept
{
if (pData->orientation == orientation)
return;

pData->orientation = orientation;
}

Expand All @@ -607,14 +654,19 @@ void KnobEventHandler::setCallback(Callback* const callback) noexcept
pData->callback = callback;
}

bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev)
void KnobEventHandler::setMouseDeceleration(float accel) noexcept
{
return pData->mouseEvent(ev);
pData->accel = accel;
}

bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev)
bool KnobEventHandler::mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor)
{
return pData->motionEvent(ev);
return pData->mouseEvent(ev, scaleFactor);
}

bool KnobEventHandler::motionEvent(const Widget::MotionEvent& ev, const double scaleFactor)
{
return pData->motionEvent(ev, scaleFactor);
}

bool KnobEventHandler::scrollEvent(const Widget::ScrollEvent& ev)
Expand Down
4 changes: 2 additions & 2 deletions dpf/dgl/src/ImageBaseWidgets.cpp
Expand Up @@ -395,15 +395,15 @@ bool ImageBaseKnob<ImageType>::onMouse(const MouseEvent& ev)
{
if (SubWidget::onMouse(ev))
return true;
return KnobEventHandler::mouseEvent(ev);
return KnobEventHandler::mouseEvent(ev, getTopLevelWidget()->getScaleFactor());
}

template <class ImageType>
bool ImageBaseKnob<ImageType>::onMotion(const MotionEvent& ev)
{
if (SubWidget::onMotion(ev))
return true;
return KnobEventHandler::motionEvent(ev);
return KnobEventHandler::motionEvent(ev, getTopLevelWidget()->getScaleFactor());
}

template <class ImageType>
Expand Down
201 changes: 201 additions & 0 deletions dpf/dgl/src/Layout.cpp
@@ -0,0 +1,201 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "../Layout.hpp"
#include "../SubWidget.hpp"

START_NAMESPACE_DGL

typedef std::list<SubWidgetWithSizeHint>::iterator SubWidgetWithSizeHintIterator;
typedef std::list<HorizontalLayout*>::iterator HorizontalLayoutIterator;
typedef std::list<VerticalLayout*>::iterator VerticalLayoutIterator;

// --------------------------------------------------------------------------------------------------------------------

template<> // horizontal
uint Layout<true>::setAbsolutePos(int x, const int y, const uint padding)
{
uint maxHeight = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
maxHeight = std::max(maxHeight, s.widget->getHeight());
s.widget->setAbsolutePos(x, y);
x += s.widget->getWidth();
x += padding;
}

return maxHeight;
}

template<> // vertical
uint Layout<false>::setAbsolutePos(const int x, int y, const uint padding)
{
uint maxWidth = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
maxWidth = std::max(maxWidth, s.widget->getWidth());
s.widget->setAbsolutePos(x, y);
y += s.widget->getHeight();
y += padding;
}

return maxWidth;
}

template<> // horizontal
void Layout<true>::setSize(const uint width, const uint padding)
{
uint maxHeight = 0;
uint nonFixedWidth = width;
uint numDynamiclySizedWidgets = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
maxHeight = std::max(maxHeight, s.widget->getHeight());

if (s.sizeHint == Fixed)
nonFixedWidth -= s.widget->getWidth();
else
++numDynamiclySizedWidgets;
}

if (const size_t numWidgets = widgets.size())
nonFixedWidth -= padding * (numWidgets - 1);

const uint widthPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedWidth / numDynamiclySizedWidgets : 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
if (s.sizeHint != Fixed)
s.widget->setSize(widthPerWidget, maxHeight);
else
s.widget->setHeight(maxHeight);
}
}

template<> // vertical
void Layout<false>::setSize(const uint height, const uint padding)
{
uint biggestWidth = 0;
uint nonFixedHeight = height;
uint numDynamiclySizedWidgets = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
biggestWidth = std::max(biggestWidth, s.widget->getWidth());

if (s.sizeHint == Fixed)
nonFixedHeight -= s.widget->getHeight();
else
++numDynamiclySizedWidgets;
}

if (const size_t numWidgets = widgets.size())
nonFixedHeight -= padding * (numWidgets - 1);

const uint heightPerWidget = numDynamiclySizedWidgets != 0 ? nonFixedHeight / numDynamiclySizedWidgets : 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
if (s.sizeHint != Fixed)
s.widget->setSize(biggestWidth, heightPerWidget);
else
s.widget->setWidth(biggestWidth);
}
}

// --------------------------------------------------------------------------------------------------------------------

/* TODO
void HorizontallyStackedVerticalLayout::adjustSize(const uint padding)
{
}
*/

void HorizontallyStackedVerticalLayout::setAbsolutePos(int x, const int y, const uint padding)
{
for (VerticalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it)
{
VerticalLayout* l(*it);
x += l->setAbsolutePos(x, y, padding);
x += padding;
}
}

// --------------------------------------------------------------------------------------------------------------------

Size<uint> VerticallyStackedHorizontalLayout::adjustSize(const uint padding)
{
uint biggestWidth = 0;
uint totalHeight = 0;

// iterate all widgets to find which one is the biggest (horizontally)
for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it)
{
HorizontalLayout* const l(*it);
uint width = 0;
uint height = 0;

for (SubWidgetWithSizeHintIterator it=l->widgets.begin(), end=l->widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);

if (width != 0)
width += padding;

width += s.widget->getWidth();
height = std::max(height, s.widget->getHeight());
}

biggestWidth = std::max(biggestWidth, width);

if (totalHeight != 0)
totalHeight += padding;

totalHeight += height;
}

// now make all horizontal lines the same width
for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it)
{
HorizontalLayout* const l(*it);
l->setSize(biggestWidth, padding);
}

return Size<uint>(biggestWidth, totalHeight);
}

void VerticallyStackedHorizontalLayout::setAbsolutePos(const int x, int y, const uint padding)
{
for (HorizontalLayoutIterator it=items.begin(), end=items.end(); it != end; ++it)
{
HorizontalLayout* l(*it);
y += l->setAbsolutePos(x, y, padding);
y += padding;
}
}

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL
6 changes: 3 additions & 3 deletions dpf/dgl/src/NanoVG.cpp
Expand Up @@ -664,18 +664,18 @@ NanoImage::Handle NanoVG::createImageFromFile(const char* filename, int imageFla
return NanoImage::Handle(fContext, nvgCreateImage(fContext, filename, imageFlags));
}

NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, ImageFlags imageFlags)
NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, ImageFlags imageFlags)
{
return createImageFromMemory(data, dataSize, static_cast<int>(imageFlags));
}

NanoImage::Handle NanoVG::createImageFromMemory(uchar* data, uint dataSize, int imageFlags)
NanoImage::Handle NanoVG::createImageFromMemory(const uchar* data, uint dataSize, int imageFlags)
{
if (fContext == nullptr) return NanoImage::Handle();
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, NanoImage::Handle());
DISTRHO_SAFE_ASSERT_RETURN(dataSize > 0, NanoImage::Handle());

return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data,static_cast<int>(dataSize)));
return NanoImage::Handle(fContext, nvgCreateImageMem(fContext, imageFlags, data, static_cast<int>(dataSize)));
}

NanoImage::Handle NanoVG::createImageFromRawMemory(uint w, uint h, const uchar* data,
Expand Down
11 changes: 10 additions & 1 deletion dpf/dgl/src/SubWidget.cpp
Expand Up @@ -139,12 +139,21 @@ void SubWidget::repaint() noexcept
if (TopLevelWidget* const topw = getTopLevelWidget())
{
if (pData->needsFullViewportForDrawing)
topw->repaint();
// repaint is virtual and we want precisely the top-level specific implementation, not any higher level
topw->TopLevelWidget::repaint();
else
topw->repaint(getConstrainedAbsoluteArea());
}
}

void SubWidget::toBottom()
{
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);

subwidgets.remove(this);
subwidgets.insert(subwidgets.begin(), this);
}

void SubWidget::toFront()
{
std::list<SubWidget*>& subwidgets(pData->parentWidget->pData->subWidgets);
Expand Down
18 changes: 17 additions & 1 deletion dpf/dgl/src/Widget.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -148,6 +148,11 @@ TopLevelWidget* Widget::getTopLevelWidget() const noexcept
return pData->topLevelWidget;
}

std::list<SubWidget*> Widget::getChildren() const noexcept
{
return pData->subWidgets;
}

void Widget::repaint() noexcept
{
}
Expand All @@ -157,11 +162,22 @@ uint Widget::getId() const noexcept
return pData->id;
}

const char* Widget::getName() const noexcept
{
return pData->name != nullptr ? pData->name : "";
}

void Widget::setId(uint id) noexcept
{
pData->id = id;
}

void Widget::setName(const char* const name) noexcept
{
std::free(pData->name);
pData->name = strdup(name);
}

bool Widget::onKeyboard(const KeyboardEvent& ev)
{
return pData->giveKeyboardEventForSubWidgets(ev);
Expand Down
5 changes: 4 additions & 1 deletion dpf/dgl/src/WidgetPrivateData.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -33,6 +33,7 @@ Widget::PrivateData::PrivateData(Widget* const s, TopLevelWidget* const tlw)
topLevelWidget(tlw),
parentWidget(nullptr),
id(0),
name(nullptr),
needsScaling(false),
visible(true),
size(0, 0),
Expand All @@ -43,6 +44,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
topLevelWidget(findTopLevelWidget(pw)),
parentWidget(pw),
id(0),
name(nullptr),
needsScaling(false),
visible(true),
size(0, 0),
Expand All @@ -51,6 +53,7 @@ Widget::PrivateData::PrivateData(Widget* const s, Widget* const pw)
Widget::PrivateData::~PrivateData()
{
subWidgets.clear();
std::free(name);
}

void Widget::PrivateData::displaySubWidgets(const uint width, const uint height, const double autoScaleFactor)
Expand Down
3 changes: 2 additions & 1 deletion dpf/dgl/src/WidgetPrivateData.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -30,6 +30,7 @@ struct Widget::PrivateData {
TopLevelWidget* const topLevelWidget;
Widget* const parentWidget;
uint id;
char* name;
bool needsScaling;
bool visible;
Size<uint> size;
Expand Down
7 changes: 6 additions & 1 deletion dpf/dgl/src/nanovg/nanovg.c
Expand Up @@ -23,6 +23,11 @@

#include "nanovg.h"
#define FONTSTASH_IMPLEMENTATION
#define stbtt_fontinfo dpf_nvg_stbtt_fontinfo
#define stbrp_context dpf_nvg_stbrp_context
#define stbrp_rect dpf_nvg_stbrp_rect
#define stbrp_node dpf_nvg_stbrp_node
#define stbrp_coord dpf_nvg_stbrp_coord
#include "fontstash.h"

#ifndef NVG_NO_STB
Expand Down Expand Up @@ -869,7 +874,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags)
return image;
}

int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata)
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata)
{
int w, h, n, image;
unsigned char* img = stbi_load_from_memory(data, ndata, &w, &h, &n, 4);
Expand Down
2 changes: 1 addition & 1 deletion dpf/dgl/src/nanovg/nanovg.h
Expand Up @@ -385,7 +385,7 @@ int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);

// Creates image by loading it from the specified chunk of memory.
// Returns handle to the image.
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, const unsigned char* data, int ndata);

// Creates image from specified image data and texture format.
// Returns handle to the image.
Expand Down
4 changes: 0 additions & 4 deletions dpf/dgl/src/pugl-upstream/.clang-tidy

This file was deleted.

1 change: 0 additions & 1 deletion dpf/dgl/src/pugl-upstream/src/.clang-tidy
Expand Up @@ -8,7 +8,6 @@ Checks: >
-hicpp-signed-bitwise,
-llvm-header-guard,
-llvmlibc-*,
-performance-no-int-to-ptr,
-readability-function-cognitive-complexity,
FormatStyle: file
HeaderFilterRegex: 'pugl/.*'
2 changes: 1 addition & 1 deletion dpf/dgl/src/pugl-upstream/src/mac.m
Expand Up @@ -1378,7 +1378,7 @@ - (void)windowDidResignKey:(NSNotification*)notification
double
puglGetTime(const PuglWorld* world)
{
return (mach_absolute_time() / 1e9) - world->startTime;
return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9) - world->startTime;
}

PuglStatus
Expand Down
107 changes: 95 additions & 12 deletions dpf/distrho/DistrhoInfo.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -54,6 +54,9 @@ START_NAMESPACE_DISTRHO
Let's begin with some examples.@n
Here is one of a stereo audio plugin that simply mutes the host output:
@code
/* DPF plugin include */
#include "DistrhoPlugin.hpp"

/* Make DPF related classes available for us to use without any extra namespace references */
USE_NAMESPACE_DISTRHO;

Expand Down Expand Up @@ -115,7 +118,7 @@ START_NAMESPACE_DISTRHO

/**
Get the plugin unique Id.
This value is used by LADSPA, DSSI and VST plugin formats.
This value is used by LADSPA, DSSI, VST2 and VST3 plugin formats.
*/
int64_t getUniqueId() const override
{
Expand Down Expand Up @@ -157,7 +160,7 @@ START_NAMESPACE_DISTRHO
A plugin is nothing without parameters.@n
In DPF parameters can be inputs or outputs.@n
They have hints to describe how they behave plus a name and a symbol identifying them.@n
Parameters also have 'ranges' – a minimum, maximum and default value.
Parameters also have 'ranges' - a minimum, maximum and default value.

Input parameters are by default "read-only": the plugin can read them but not change them.
(there are exceptions and possibly a request to the host to change values, more on that below)@n
Expand Down Expand Up @@ -349,10 +352,10 @@ START_NAMESPACE_DISTRHO
}
}

/**
Set the name of the program @a index.
This function will be called once, shortly after the plugin is created.
*/
/**
Set the name of the program @a index.
This function will be called once, shortly after the plugin is created.
*/
void initProgramName(uint32_t index, String& programName)
{
// we only have one program so we can skip checking the index
Expand All @@ -374,6 +377,8 @@ START_NAMESPACE_DISTRHO
return fGainL;
case 1;
return fGainR;
default:
return 0.f;
}
}

Expand Down Expand Up @@ -618,6 +623,24 @@ START_NAMESPACE_DISTRHO
*/
#define DISTRHO_UI_CUSTOM_WIDGET_TYPE

/**
Default UI width to use when creating initial and temporary windows.@n
Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts.
(which would normally be done for knowing the UI size before host creates a window for it)
When this macro is defined, the companion DISTRHO_UI_DEFAULT_HEIGHT macro must be defined as well.
*/
#define DISTRHO_UI_DEFAULT_WIDTH 300

/**
Default UI height to use when creating initial and temporary windows.@n
Setting this macro allows to skip a temporary UI from being created in certain VST2 and VST3 hosts.
(which would normally be done for knowing the UI size before host creates a window for it)
When this macro is defined, the companion DISTRHO_UI_DEFAULT_WIDTH macro must be defined as well.
*/
#define DISTRHO_UI_DEFAULT_HEIGHT 300

/**
Whether the %UI uses NanoVG for drawing instead of the default raw OpenGL calls.@n
When enabled your %UI instance will subclass @ref NanoWidget instead of @ref Widget.
Expand All @@ -640,9 +663,8 @@ START_NAMESPACE_DISTRHO

/**
Custom LV2 category for the plugin.@n
This can be one of the following values:
This is a single string, and can be one of the following values:
- lv2:Plugin
- lv2:AllpassPlugin
- lv2:AmplifierPlugin
- lv2:AnalyserPlugin
Expand Down Expand Up @@ -688,7 +710,7 @@ START_NAMESPACE_DISTRHO

/**
Custom VST3 categories for the plugin.@n
This is a list of categories, separated by a @c |.
This is a single concatenated string of categories, separated by a @c |.
Each effect category can be one of the following values:
Expand Down Expand Up @@ -723,9 +745,70 @@ START_NAMESPACE_DISTRHO
- Instrument|Synth
- Instrument|Synth|Sampler
@note DPF will automatically set Mono and Stereo categories when appropriate.
And extra categories possible for any plugin type:
- Mono
- Stereo
*/
#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx"
#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Stereo"

/**
Custom CLAP features for the plugin.@n
This is a list of features defined as a string array body, without the terminating @c , or nullptr.
A top-level category can be set as feature and be one of the following values:
- instrument
- audio-effect
- note-effect
- analyzer
The following sub-categories can also be set:
- synthesizer
- sampler
- drum
- drum-machine
- filter
- phaser
- equalizer
- de-esser
- phase-vocoder
- granular
- frequency-shifter
- pitch-shifter
- distortion
- transient-shaper
- compressor
- limiter
- flanger
- chorus
- delay
- reverb
- tremolo
- glitch
- utility
- pitch-correction
- restoration
- multi-effects
- mixing
- mastering
And finally the following audio capabilities can be set:
- mono
- stereo
- surround
- ambisonic
*/
#define DISTRHO_PLUGIN_CLAP_FEATURES "audio-effect", "stereo"

/** @} */

Expand Down
74 changes: 72 additions & 2 deletions dpf/distrho/DistrhoPlugin.hpp
Expand Up @@ -362,7 +362,7 @@ struct ParameterRanges {
*/
float getNormalizedValue(const float& value) const noexcept
{
const float normValue((value - min) / (max - min));
const float normValue = (value - min) / (max - min);

if (normValue <= 0.0f)
return 0.0f;
Expand All @@ -371,6 +371,21 @@ struct ParameterRanges {
return normValue;
}

/**
Get a value normalized to 0.0<->1.0.
Overloaded function using double precision values.
*/
double getNormalizedValue(const double& value) const noexcept
{
const double normValue = (value - min) / (max - min);

if (normValue <= 0.0)
return 0.0;
if (normValue >= 1.0)
return 1.0;
return normValue;
}

/**
Get a value normalized to 0.0<->1.0, fixed within range.
*/
Expand All @@ -381,7 +396,7 @@ struct ParameterRanges {
if (value >= max)
return 1.0f;

const float normValue((value - min) / (max - min));
const float normValue = (value - min) / (max - min);

if (normValue <= 0.0f)
return 0.0f;
Expand All @@ -391,6 +406,27 @@ struct ParameterRanges {
return normValue;
}

/**
Get a value normalized to 0.0<->1.0, fixed within range.
Overloaded function using double precision values.
*/
double getFixedAndNormalizedValue(const double& value) const noexcept
{
if (value <= min)
return 0.0;
if (value >= max)
return 1.0;

const double normValue = (value - min) / (max - min);

if (normValue <= 0.0)
return 0.0;
if (normValue >= 1.0)
return 1.0;

return normValue;
}

/**
Get a proper value previously normalized to 0.0<->1.0.
*/
Expand All @@ -403,6 +439,20 @@ struct ParameterRanges {

return value * (max - min) + min;
}

/**
Get a proper value previously normalized to 0.0<->1.0.
Overloaded function using double precision values.
*/
double getUnnormalizedValue(const double& value) const noexcept
{
if (value <= 0.0)
return min;
if (value >= 1.0)
return max;

return value * (max - min) + min;
}
};

/**
Expand Down Expand Up @@ -706,6 +756,16 @@ struct State {
@note This value is optional and only used for LV2.
*/
String description;

/**
Default constructor for a null state.
*/
State() noexcept
: hints(0x0),
key(),
defaultValue(),
label(),
description() {}
};

/**
Expand Down Expand Up @@ -951,6 +1011,16 @@ class Plugin
*/
bool isDummyInstance() const noexcept;

/**
Check if this plugin instance is a "selftest" one used for automated plugin tests.@n
To enable this mode build with `DPF_RUNTIME_TESTING` macro defined (i.e. set as compiler build flag),
and run the JACK/Standalone executable with "selftest" as its only and single argument.
A few basic DSP and UI tests will run in self-test mode, with once instance having this function returning true.@n
You can use this chance to do a few tests of your own as well.
*/
bool isSelfTestInstance() const noexcept;

#if DISTRHO_PLUGIN_WANT_TIMEPOS
/**
Get the current host transport time position.@n
Expand Down
4 changes: 3 additions & 1 deletion dpf/distrho/DistrhoPluginMain.cpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand All @@ -18,6 +18,8 @@

#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
# include "src/DistrhoPluginCarla.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
# include "src/DistrhoPluginCLAP.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
# include "src/DistrhoPluginJACK.cpp"
#elif (defined(DISTRHO_PLUGIN_TARGET_LADSPA) || defined(DISTRHO_PLUGIN_TARGET_DSSI))
Expand Down
15 changes: 10 additions & 5 deletions dpf/distrho/DistrhoUIMain.cpp
Expand Up @@ -17,24 +17,29 @@
#include "src/DistrhoUI.cpp"

#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_JACK)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0
# include "src/DistrhoUIDSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# include "src/DistrhoUILV2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
# include "src/DistrhoUIVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED) || defined(DISTRHO_PLUGIN_TARGET_STATIC)
// nothing
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#else
# error unsupported format
#endif

#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && !defined(DISTRHO_PLUGIN_TARGET_CARLA) && !defined(DISTRHO_PLUGIN_TARGET_JACK) && !defined(DISTRHO_PLUGIN_TARGET_VST2) && !defined(DISTRHO_PLUGIN_TARGET_VST3)
#if !DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
# ifdef DISTRHO_PLUGIN_TARGET_DSSI
# define DISTRHO_IS_STANDALONE 1
# else
Expand Down
2 changes: 1 addition & 1 deletion dpf/distrho/src/DistrhoDefines.h
Expand Up @@ -211,7 +211,7 @@ private: \
#endif

/* Useful macros */
#define ARRAY_SIZE(ARRAY) sizeof(ARRAY)/sizeof(ARRAY[0])
#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))

/* Useful typedefs */
typedef unsigned char uchar;
Expand Down
38 changes: 22 additions & 16 deletions dpf/distrho/src/DistrhoPlugin.cpp
Expand Up @@ -25,6 +25,7 @@ uint32_t d_nextBufferSize = 0;
double d_nextSampleRate = 0.0;
const char* d_nextBundlePath = nullptr;
bool d_nextPluginIsDummy = false;
bool d_nextPluginIsSelfTest = false;
bool d_nextCanRequestParameterValueChanges = false;

/* ------------------------------------------------------------------------------------------------------------
Expand All @@ -42,45 +43,45 @@ const PortGroupWithId PluginExporter::sFallbackPortGroup;
Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount)
: pData(new PrivateData())
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS];
#endif
#endif

#ifdef DPF_ABORT_ON_ERROR
# define DPF_ABORT abort();
#else
# define DPF_ABORT
#endif
#if defined(DPF_ABORT_ON_ERROR) || defined(DPF_RUNTIME_TESTING)
#define DPF_ABORT abort();
#else
#define DPF_ABORT
#endif

if (parameterCount > 0)
{
pData->parameterCount = parameterCount;
pData->parameters = new Parameter[parameterCount];
pData->parameters = new Parameter[parameterCount];
}

if (programCount > 0)
{
#if DISTRHO_PLUGIN_WANT_PROGRAMS
#if DISTRHO_PLUGIN_WANT_PROGRAMS
pData->programCount = programCount;
pData->programNames = new String[programCount];
#else
#else
d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1");
DPF_ABORT
#endif
#endif
}

if (stateCount > 0)
{
#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
pData->stateCount = stateCount;
pData->states = new State[stateCount];
#else
pData->states = new State[stateCount];
#else
d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1");
DPF_ABORT
#endif
#endif
}

#undef DPF_ABORT
#undef DPF_ABORT
}

Plugin::~Plugin()
Expand Down Expand Up @@ -111,6 +112,11 @@ bool Plugin::isDummyInstance() const noexcept
return pData->isDummy;
}

bool Plugin::isSelfTestInstance() const noexcept
{
return pData->isSelfTest;
}

#if DISTRHO_PLUGIN_WANT_TIMEPOS
const TimePosition& Plugin::getTimePosition() const noexcept
{
Expand Down
1,471 changes: 1,471 additions & 0 deletions dpf/distrho/src/DistrhoPluginCLAP.cpp

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions dpf/distrho/src/DistrhoPluginChecks.h
Expand Up @@ -183,6 +183,17 @@
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

// -----------------------------------------------------------------------
// Make sure both default width and height are provided

#if defined(DISTRHO_UI_DEFAULT_WIDTH) && !defined(DISTRHO_UI_DEFAULT_HEIGHT)
# error DISTRHO_UI_DEFAULT_WIDTH is defined but DISTRHO_UI_DEFAULT_HEIGHT is not
#endif

#if defined(DISTRHO_UI_DEFAULT_HEIGHT) && !defined(DISTRHO_UI_DEFAULT_WIDTH)
# error DISTRHO_UI_DEFAULT_HEIGHT is defined but DISTRHO_UI_DEFAULT_WIDTH is not
#endif

// -----------------------------------------------------------------------
// Prevent users from messing about with DPF internals

Expand Down
63 changes: 61 additions & 2 deletions dpf/distrho/src/DistrhoPluginInternal.hpp
Expand Up @@ -39,6 +39,7 @@ extern uint32_t d_nextBufferSize;
extern double d_nextSampleRate;
extern const char* d_nextBundlePath;
extern bool d_nextPluginIsDummy;
extern bool d_nextPluginIsSelfTest;
extern bool d_nextCanRequestParameterValueChanges;

// -----------------------------------------------------------------------
Expand Down Expand Up @@ -67,7 +68,8 @@ struct PortGroupWithId : PortGroup {
groupId(kPortGroupNone) {}
};

static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
static inline
void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& portGroup)
{
switch (groupId)
{
Expand All @@ -86,12 +88,62 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por
}
}

static inline
void strncpy(char* const dst, const char* const src, const size_t length)
{
DISTRHO_SAFE_ASSERT_RETURN(length > 0,);

if (const size_t len = std::min(std::strlen(src), length-1U))
{
std::memcpy(dst, src, len);
dst[len] = '\0';
}
else
{
dst[0] = '\0';
}
}

template<typename T>
static inline
void snprintf_t(char* const dst, const T value, const char* const format, const size_t size)
{
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
std::snprintf(dst, size-1, format, value);
dst[size-1] = '\0';
}

static inline
void snprintf_f32(char* const dst, const float value, const size_t size)
{
return snprintf_t<float>(dst, value, "%f", size);
}

static inline
void snprintf_f32(char* const dst, const double value, const size_t size)
{
return snprintf_t<double>(dst, value, "%f", size);
}

static inline
void snprintf_i32(char* const dst, const int32_t value, const size_t size)
{
return snprintf_t<int32_t>(dst, value, "%d", size);
}

static inline
void snprintf_u32(char* const dst, const uint32_t value, const size_t size)
{
return snprintf_t<uint32_t>(dst, value, "%u", size);
}

// -----------------------------------------------------------------------
// Plugin private data

struct Plugin::PrivateData {
const bool canRequestParameterValueChanges;
const bool isDummy;
const bool isSelfTest;
bool isProcessing;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
Expand Down Expand Up @@ -136,6 +188,7 @@ struct Plugin::PrivateData {
PrivateData() noexcept
: canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges),
isDummy(d_nextPluginIsDummy),
isSelfTest(d_nextPluginIsSelfTest),
isProcessing(false),
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioPorts(nullptr),
Expand Down Expand Up @@ -324,7 +377,8 @@ class PluginExporter
# if DISTRHO_PLUGIN_WANT_STATE
if (fData->stateCount != 0)
{
if ((void*)(fPlugin->*(&Plugin::initState)) == (void*)&Plugin::initState)
if ((void*)(fPlugin->*(static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))) ==
(void*)static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))
{
d_stderr2("DPF warning: Plugins with state must implement `initState`");
abort();
Expand Down Expand Up @@ -593,6 +647,11 @@ class PluginExporter
return (getParameterHints(index) & kParameterIsOutput) != 0x0;
}

bool isParameterInteger(const uint32_t index) const noexcept
{
return (getParameterHints(index) & kParameterIsInteger) != 0x0;
}

bool isParameterTrigger(const uint32_t index) const noexcept
{
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger;
Expand Down
146 changes: 89 additions & 57 deletions dpf/distrho/src/DistrhoPluginJACK.cpp
Expand Up @@ -38,6 +38,12 @@
#include "jackbridge/JackBridge.cpp"
#include "lv2/lv2.h"

#ifdef DISTRHO_OS_MAC
# define Point CocoaPoint
# include <CoreFoundation/CoreFoundation.h>
# undef Point
#endif

#ifndef DISTRHO_OS_WINDOWS
# include <signal.h>
# include <unistd.h>
Expand Down Expand Up @@ -789,11 +795,11 @@ class PluginProcessTestingThread : public Thread

while (! shouldThreadExit())
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
plugin.run(inputs, outputs, 128, nullptr, 0);
#else
#else
plugin.run(inputs, outputs, 128);
#endif
#endif
d_msleep(100);
}

Expand All @@ -807,7 +813,7 @@ bool runSelfTests()
{
d_nextBufferSize = 512;
d_nextSampleRate = 44100.0;
PluginExporter plugin(nullptr, nullptr, nullptr);
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
d_nextBufferSize = 0;
d_nextSampleRate = 0.0;
}
Expand All @@ -818,7 +824,17 @@ bool runSelfTests()

// simple processing
{
PluginExporter plugin(nullptr, nullptr, nullptr);
d_nextPluginIsSelfTest = true;
PluginExporter plugin(nullptr, nullptr, nullptr, nullptr);
d_nextPluginIsSelfTest = false;

#if DISTRHO_PLUGIN_HAS_UI
UIExporter ui(nullptr, 0, plugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
plugin.getInstancePointer(), 0.0);
ui.showAndFocus();
#endif

plugin.activate();
plugin.deactivate();
plugin.setBufferSize(128);
Expand All @@ -833,20 +849,26 @@ bool runSelfTests()
for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
outputs[i] = buffer;

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
plugin.run(inputs, outputs, 128, nullptr, 0);
#else
#else
plugin.run(inputs, outputs, 128);
#endif
#endif

plugin.deactivate();

#if DISTRHO_PLUGIN_HAS_UI
ui.plugin_idle();
#endif
}

return true;

// multi-threaded processing with UI
{
PluginExporter pluginA(nullptr, nullptr, nullptr);
PluginExporter pluginB(nullptr, nullptr, nullptr);
PluginExporter pluginC(nullptr, nullptr, nullptr);
PluginExporter pluginA(nullptr, nullptr, nullptr, nullptr);
PluginExporter pluginB(nullptr, nullptr, nullptr, nullptr);
PluginExporter pluginC(nullptr, nullptr, nullptr, nullptr);
PluginProcessTestingThread procTestA(pluginA);
PluginProcessTestingThread procTestB(pluginB);
PluginProcessTestingThread procTestC(pluginC);
Expand All @@ -860,7 +882,7 @@ bool runSelfTests()
// stop the 2nd instance now
procTestB.stopThread(5000);

#if DISTRHO_PLUGIN_HAS_UI
#if DISTRHO_PLUGIN_HAS_UI
// start UI in the middle of this
{
UIExporter uiA(nullptr, 0, pluginA.getSampleRate(),
Expand All @@ -887,7 +909,7 @@ bool runSelfTests()
d_msleep(100);
}
}
#endif
#endif

procTestA.stopThread(5000);
procTestC.stopThread(5000);
Expand All @@ -905,12 +927,47 @@ int main(int argc, char* argv[])
{
USE_NAMESPACE_DISTRHO;

#ifdef DPF_RUNTIME_TESTING
initSignalHandler();

#if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
// find plugin bundle
static String bundlePath;
if (bundlePath.isEmpty())
{
String tmpPath(getBinaryFilename());
tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
#ifdef DISTRHO_OS_MAC
if (tmpPath.endsWith("/MacOS"))
{
tmpPath.truncate(tmpPath.rfind('/'));
if (tmpPath.endsWith("/Contents"))
{
tmpPath.truncate(tmpPath.rfind('/'));
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
}
#else
if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0)
{
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
#endif
}
#endif

if (argc == 2 && std::strcmp(argv[1], "selftest") == 0)
{
#ifdef DPF_RUNTIME_TESTING
return runSelfTests() ? 0 : 1;
#endif
#else
d_stderr2("Code was built without DPF_RUNTIME_TESTING macro enabled, selftest option is not available");
return 1;
#endif
}

#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
/* the code below is based on
* https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/
*/
Expand Down Expand Up @@ -938,7 +995,7 @@ int main(int argc, char* argv[])

hasConsole = true;
}
#endif
#endif

jack_status_t status = jack_status_t(0x0);
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);
Expand Down Expand Up @@ -982,7 +1039,17 @@ int main(int argc, char* argv[])
else
d_stderr("Failed to create the JACK client, cannot continue!");

#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
#if defined(DISTRHO_OS_MAC)
CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr,
DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8);
CFStringRef errorStringRef = CFStringCreateWithCString(nullptr,
String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);

CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
nullptr, nullptr, nullptr,
errorTitleRef, errorStringRef,
nullptr, nullptr, nullptr, nullptr);
#elif defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
// make sure message box is high-dpi aware
if (const HMODULE user32 = LoadLibrary("user32.dll"))
{
Expand All @@ -1007,49 +1074,19 @@ int main(int argc, char* argv[])
return 1;
}

initSignalHandler();

d_nextBufferSize = jackbridge_get_buffer_size(client);
d_nextSampleRate = jackbridge_get_sample_rate(client);
d_nextCanRequestParameterValueChanges = true;

#if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD)
// find plugin bundle
static String bundlePath;
if (bundlePath.isEmpty())
{
String tmpPath(getBinaryFilename());
tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
#ifdef DISTRHO_OS_MAC
if (tmpPath.endsWith("/MacOS"))
{
tmpPath.truncate(tmpPath.rfind('/'));
if (tmpPath.endsWith("/Contents"))
{
tmpPath.truncate(tmpPath.rfind('/'));
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
}
#else
if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0)
{
bundlePath = tmpPath;
d_nextBundlePath = bundlePath.buffer();
}
#endif
}
#endif

uintptr_t winId = 0;
#if DISTRHO_PLUGIN_HAS_UI
#if DISTRHO_PLUGIN_HAS_UI
if (argc == 3 && std::strcmp(argv[1], "embed") == 0)
winId = static_cast<uintptr_t>(std::atoll(argv[2]));
#endif
#endif

const PluginJack p(client, winId);

#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI
/* the code below is based on
* https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/
*/
Expand All @@ -1075,14 +1112,9 @@ int main(int argc, char* argv[])
ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
SendInput(1, &ip, sizeof(INPUT));
}
#endif
#endif

return 0;

#ifndef DPF_RUNTIME_TESTING
// unused
(void)argc; (void)argv;
#endif
}

// -----------------------------------------------------------------------
110 changes: 55 additions & 55 deletions dpf/distrho/src/DistrhoPluginLV2.cpp
Expand Up @@ -620,22 +620,22 @@ class PluginLv2
// Run plugin
if (sampleCount != 0)
{
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
fRunCount = mod_license_run_begin(fRunCount, sampleCount);
#endif
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount);
#else
#else
fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount);
#endif
#endif

#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
mod_license_run_silence(fRunCount, fPortAudioOuts[i], sampleCount, i);
#endif
#endif

#if DISTRHO_PLUGIN_WANT_TIMEPOS
#if DISTRHO_PLUGIN_WANT_TIMEPOS
// update timePos for next callback
if (d_isNotZero(fLastPositionData.speed))
{
Expand Down Expand Up @@ -691,12 +691,12 @@ class PluginLv2

fPlugin.setTimePosition(fTimePosition);
}
#endif
#endif
}

updateParameterOutputsAndTriggers();

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
fEventsOutData.initIfNeeded(fURIDs.atomSequence);

LV2_Atom_Event* aev;
Expand Down Expand Up @@ -797,11 +797,11 @@ class PluginLv2
break;
}
}
#endif
#endif

#if DISTRHO_LV2_USE_EVENTS_OUT
#if DISTRHO_LV2_USE_EVENTS_OUT
fEventsOutData.endRun();
#endif
#endif
}

// -------------------------------------------------------------------
Expand Down Expand Up @@ -860,7 +860,7 @@ class PluginLv2

// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_WANT_PROGRAMS
#if DISTRHO_PLUGIN_WANT_PROGRAMS
const LV2_Program_Descriptor* lv2_get_program(const uint32_t index)
{
if (index >= fPlugin.getProgramCount())
Expand Down Expand Up @@ -895,30 +895,30 @@ class PluginLv2
setPortControlValue(i, fLastControlValues[i]);
}

# if DISTRHO_PLUGIN_WANT_FULL_STATE
#if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update state
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
#endif
}
#endif
#endif

// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle)
{
# if DISTRHO_PLUGIN_WANT_FULL_STATE
#if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update current state
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
{
const String& key = cit->first;
fStateMap[key] = fPlugin.getStateValue(key);
}
# endif
#endif

String lv2key;
LV2_URID urid;
Expand Down Expand Up @@ -1021,11 +1021,11 @@ class PluginLv2

setState(key, value);

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
// signal msg needed for UI
if ((hints & kStateIsOnlyForDSP) == 0x0)
fNeededUiSends[i] = true;
#endif
#endif
}

return LV2_STATE_SUCCESS;
Expand Down Expand Up @@ -1090,53 +1090,53 @@ class PluginLv2
{
return LV2_WORKER_SUCCESS;
}
#endif
#endif

// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
void* lv2_get_instance_pointer()
{
return fPlugin.getInstancePointer();
}
#endif
#endif

// -------------------------------------------------------------------

private:
PluginExporter fPlugin;
const bool fUsingNominal; // if false use maxBlockLength

#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
uint32_t fRunCount;
#endif
#endif

// LV2 ports
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS];
#else
#else
const float** fPortAudioIns;
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];
#else
#else
float** fPortAudioOuts;
#endif
#endif
float** fPortControls;
#if DISTRHO_LV2_USE_EVENTS_IN
#if DISTRHO_LV2_USE_EVENTS_IN
LV2_Atom_Sequence* fPortEventsIn;
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
float* fPortLatency;
#endif
#endif

// Temporary data
float* fLastControlValues;
double fSampleRate;
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
MidiEvent fMidiEvents[kMaxMidiEvents];
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
TimePosition fTimePosition;

struct Lv2PositionData {
Expand All @@ -1160,9 +1160,9 @@ class PluginLv2
ticksPerBeat(-1.0) {}

} fLastPositionData;
#endif
#endif

#if DISTRHO_LV2_USE_EVENTS_OUT
#if DISTRHO_LV2_USE_EVENTS_OUT
struct Lv2EventsOutData {
uint32_t capacity, offset;
LV2_Atom_Sequence* port;
Expand Down Expand Up @@ -1198,7 +1198,7 @@ class PluginLv2
}

} fEventsOutData;
#endif
#endif

// LV2 URIDs
struct URIDs {
Expand Down Expand Up @@ -1262,13 +1262,13 @@ class PluginLv2
} fURIDs;

// LV2 features
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
const LV2_ControlInputPort_Change_Request* const fCtrlInPortChangeReq;
#endif
#endif
const LV2_URID_Map* const fUridMap;
const LV2_Worker_Schedule* const fWorker;

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
LV2_Atom_Forge fAtomForge;
StringToStringMap fStateMap;
UridToStringMap fUridStateMap;
Expand Down Expand Up @@ -1321,7 +1321,7 @@ class PluginLv2
d_stderr("Failed to find plugin state with key \"%s\"", key);
return false;
}
#endif
#endif

void updateParameterOutputsAndTriggers()
{
Expand All @@ -1341,13 +1341,13 @@ class PluginLv2
}
}

#if DISTRHO_PLUGIN_WANT_LATENCY
#if DISTRHO_PLUGIN_WANT_LATENCY
if (fPortLatency != nullptr)
*fPortLatency = fPlugin.getLatency();
#endif
#endif
}

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
bool requestParameterValueChange(const uint32_t index, const float value)
{
if (fCtrlInPortChangeReq == nullptr)
Expand All @@ -1359,16 +1359,16 @@ class PluginLv2
{
return (((PluginLv2*)ptr)->requestParameterValueChange(index, value) == 0);
}
#endif
#endif

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
{
return ((PluginLv2*)ptr)->updateState(key, value);
}
#endif
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
bool writeMidi(const MidiEvent& midiEvent)
{
DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false);
Expand Down Expand Up @@ -1398,7 +1398,7 @@ class PluginLv2
{
return ((PluginLv2*)ptr)->writeMidi(midiEvent);
}
#endif
#endif
};

// -----------------------------------------------------------------------
Expand Down
97 changes: 97 additions & 0 deletions dpf/distrho/src/DistrhoPluginStub.cpp
@@ -0,0 +1,97 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
* permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoPluginInternal.hpp"

START_NAMESPACE_DISTRHO

// --------------------------------------------------------------------------------------------------------------------

#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static constexpr const writeMidiFunc writeMidiCallback = nullptr;
#endif
#if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
#endif
#if ! DISTRHO_PLUGIN_WANT_STATE
static const updateStateValueFunc updateStateValueCallback = nullptr;
#endif

// --------------------------------------------------------------------------------------------------------------------

/**
* Stub plugin class, does nothing but serving as example code for other implementations.
*/
class PluginStub
{
public:
PluginStub()
: fPlugin(this,
writeMidiCallback,
requestParameterValueChangeCallback,
updateStateValueCallback)
{
}

// ----------------------------------------------------------------------------------------------------------------

private:
// Plugin
PluginExporter fPlugin;

// ----------------------------------------------------------------------------------------------------------------
// DPF callbacks

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
bool writeMidi(const MidiEvent&)
{
return true;
}

static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent)
{
return ((PluginStub*)ptr)->writeMidi(midiEvent);
}
#endif

#if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
bool requestParameterValueChange(uint32_t, float)
{
return true;
}

static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
{
return ((PluginStub*)ptr)->requestParameterValueChange(index, value);
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
bool updateState(const char*, const char*)
{
return true;
}

static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value)
{
return ((PluginStub*)ptr)->updateState(key, value);
}
#endif
};

END_NAMESPACE_DISTRHO

// --------------------------------------------------------------------------------------------------------------------
65 changes: 15 additions & 50 deletions dpf/distrho/src/DistrhoPluginVST.hpp
Expand Up @@ -67,24 +67,24 @@ START_NAMESPACE_DISTRHO
// --------------------------------------------------------------------------------------------------------------------

enum Vst3InternalParameters {
#if DPF_VST3_USES_SEPARATE_CONTROLLER
#if DPF_VST3_USES_SEPARATE_CONTROLLER
kVst3InternalParameterBufferSize,
kVst3InternalParameterSampleRate,
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
kVst3InternalParameterLatency,
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
kVst3InternalParameterProgram,
#endif
#endif
kVst3InternalParameterBaseCount,
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount,
kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16,
kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end
#else
#else
kVst3InternalParameterCount = kVst3InternalParameterBaseCount
#endif
#endif
};

#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT
Expand Down Expand Up @@ -136,22 +136,6 @@ size_t strlen_utf16(const int16_t* const str)

// --------------------------------------------------------------------------------------------------------------------

static inline
void strncpy(char* const dst, const char* const src, const size_t length)
{
DISTRHO_SAFE_ASSERT_RETURN(length > 0,);

if (const size_t len = std::min(std::strlen(src), length-1U))
{
std::memcpy(dst, src, len);
dst[len] = '\0';
}
else
{
dst[0] = '\0';
}
}

static inline
void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length)
{
Expand Down Expand Up @@ -201,15 +185,8 @@ void strncpy_utf16(int16_t* const dst, const char* const src, const size_t lengt
// --------------------------------------------------------------------------------------------------------------------

template<typename T>
static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size)
{
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
std::snprintf(dst, size-1, format, value);
dst[size-1] = '\0';
}

template<typename T>
static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size)
static inline
void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size)
{
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);

Expand All @@ -224,27 +201,15 @@ static void snprintf_utf16_t(int16_t* const dst, const T value, const char* cons
}

static inline
void snprintf_f32(char* const dst, const float value, const size_t size)
{
return snprintf_t<float>(dst, value, "%f", size);
}

static inline
void snprintf_i32(char* const dst, const int32_t value, const size_t size)
{
return snprintf_t<int32_t>(dst, value, "%d", size);
}

static inline
void snprintf_u32(char* const dst, const uint32_t value, const size_t size)
void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size)
{
return snprintf_t<uint32_t>(dst, value, "%u", size);
return snprintf_utf16_t<float>(dst, value, "%f", size);
}

static inline
void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size)
void snprintf_f32_utf16(int16_t* const dst, const double value, const size_t size)
{
return snprintf_utf16_t<float>(dst, value, "%f", size);
return snprintf_utf16_t<double>(dst, value, "%f", size);
}

static inline
Expand Down
36 changes: 22 additions & 14 deletions dpf/distrho/src/DistrhoPluginVST2.cpp
Expand Up @@ -65,7 +65,7 @@ START_NAMESPACE_DISTRHO

typedef std::map<const String, String> StringMap;

static const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));
static constexpr const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));

#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static const writeMidiFunc writeMidiCallback = nullptr;
Expand Down Expand Up @@ -117,7 +117,7 @@ struct ParameterAndNotesHelper
}

#if DISTRHO_PLUGIN_WANT_STATE
virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0;
virtual void setStateFromUI(const char* key, const char* value) = 0;
#endif
};

Expand Down Expand Up @@ -575,17 +575,25 @@ class PluginVst : public ParameterAndNotesHelper
}
else
{
double scaleFactor = fLastScaleFactor;
#if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT)
fVstRect.right = DISTRHO_UI_DEFAULT_WIDTH;
fVstRect.bottom = DISTRHO_UI_DEFAULT_HEIGHT;
if (d_isZero(scaleFactor))
scaleFactor = 1.0;
#else
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
fPlugin.getInstancePointer(), fLastScaleFactor);
fVstRect.right = tmpUI.getWidth();
fPlugin.getInstancePointer(), scaleFactor);
fVstRect.right = tmpUI.getWidth();
fVstRect.bottom = tmpUI.getHeight();
# ifdef DISTRHO_OS_MAC
const double scaleFactor = tmpUI.getScaleFactor();
scaleFactor = tmpUI.getScaleFactor();
tmpUI.quit();
#endif
#ifdef DISTRHO_OS_MAC
fVstRect.right /= scaleFactor;
fVstRect.bottom /= scaleFactor;
# endif
tmpUI.quit();
#endif
}
*(ERect**)ptr = &fVstRect;
return 1;
Expand Down Expand Up @@ -995,7 +1003,7 @@ class PluginVst : public ParameterAndNotesHelper
else
fTimePosition.bbt.beatsPerMinute = 120.0;

if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid))
if ((vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid)) == (kVstPpqPosValid|kVstTimeSigValid))
{
const double ppqPos = std::abs(vstTimeInfo->ppqPos);
const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
Expand Down Expand Up @@ -1131,7 +1139,7 @@ class PluginVst : public ParameterAndNotesHelper
{
if (fPlugin.isParameterOutput(i))
{
// NOTE: no output parameter support in VST, simulate it here
// NOTE: no output parameter support in VST2, simulate it here
curValue = fPlugin.getParameterValue(i);

if (d_isEqual(curValue, parameterValues[i]))
Expand Down Expand Up @@ -1230,12 +1238,12 @@ class PluginVst : public ParameterAndNotesHelper
// functions called from the UI side, may block

# if DISTRHO_PLUGIN_HAS_UI
void setStateFromUI(const char* const key, const char* const newValue) override
void setStateFromUI(const char* const key, const char* const value) override
# else
void setStateFromUI(const char* const key, const char* const newValue)
void setStateFromUI(const char* const key, const char* const value)
# endif
{
fPlugin.setState(key, newValue);
fPlugin.setState(key, value);

// check if we want to save this key
if (! fPlugin.wantStateKey(key))
Expand All @@ -1248,7 +1256,7 @@ class PluginVst : public ParameterAndNotesHelper

if (dkey == key)
{
it->second = newValue;
it->second = value;
return;
}
}
Expand Down
637 changes: 352 additions & 285 deletions dpf/distrho/src/DistrhoPluginVST3.cpp

Large diffs are not rendered by default.

22 changes: 16 additions & 6 deletions dpf/distrho/src/DistrhoUI.cpp
Expand Up @@ -202,13 +202,23 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei
* UI */

UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize)
: UIWidget(UI::PrivateData::createNextWindow(this, width, height)),
: UIWidget(UI::PrivateData::createNextWindow(this,
#ifdef DISTRHO_UI_DEFAULT_WIDTH
width == 0 ? DISTRHO_UI_DEFAULT_WIDTH :
#endif
width,
#ifdef DISTRHO_UI_DEFAULT_WIDTH
height == 0 ? DISTRHO_UI_DEFAULT_WIDTH :
#endif
height)),
uiData(UI::PrivateData::s_nextPrivateData)
{
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (width != 0 && height != 0)
{
#ifndef DISTRHO_UI_DEFAULT_WIDTH
Widget::setSize(width, height);
#endif

if (automaticallyScaleAndSetAsMinimumSize)
setGeometryConstraints(width, height, true, true, true);
Expand Down Expand Up @@ -405,19 +415,19 @@ void UI::onResize(const ResizeEvent& ev)
#endif
}

// NOTE: only used for VST3
// NOTE: only used for VST3 and CLAP
void UI::requestSizeChange(const uint width, const uint height)
{
# ifdef DISTRHO_PLUGIN_TARGET_VST3
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP)
if (uiData->initializing)
uiData->window->setSizeForVST3(width, height);
uiData->window->setSizeFromHost(width, height);
else
uiData->setSizeCallback(width, height);
# else
#else
// unused
(void)width;
(void)height;
# endif
#endif
}
#endif

Expand Down
30 changes: 15 additions & 15 deletions dpf/distrho/src/DistrhoUIInternal.hpp
@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
Expand Down Expand Up @@ -262,7 +262,7 @@ class UIExporter

// -------------------------------------------------------------------

#if defined(DISTRHO_PLUGIN_TARGET_VST3) && (defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
void idleForVST3()
{
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
Expand All @@ -271,7 +271,7 @@ class UIExporter
ui->uiIdle();
}

# if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void addIdleCallbackForVST3(IdleCallback* const cb, const uint timerFrequencyInMs)
{
uiData->window->addIdleCallback(cb, timerFrequencyInMs);
Expand All @@ -281,31 +281,31 @@ class UIExporter
{
uiData->window->removeIdleCallback(cb);
}
# endif
#endif
#endif
#endif

// -------------------------------------------------------------------

void setWindowOffset(const int x, const int y)
{
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// TODO
(void)x; (void)y;
#else
#else
uiData->window->setOffset(x, y);
#endif
#endif
}

#ifdef DISTRHO_PLUGIN_TARGET_VST3
void setWindowSizeForVST3(const uint width, const uint height)
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP)
void setWindowSizeFromHost(const uint width, const uint height)
{
# if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
ui->setSize(width, height);
# else
uiData->window->setSizeForVST3(width, height);
# endif
#else
uiData->window->setSizeFromHost(width, height);
#endif
}
#endif
#endif

void setWindowTitle(const char* const uiTitle)
{
Expand Down
252 changes: 127 additions & 125 deletions dpf/distrho/src/DistrhoUILV2.cpp
Expand Up @@ -42,8 +42,11 @@ typedef struct _LV2_Atom_MidiEvent {
uint8_t data[3]; /**< MIDI data (body). */
} LV2_Atom_MidiEvent;

#if ! DISTRHO_PLUGIN_WANT_STATE
static constexpr const setStateFunc setStateCallback = nullptr;
#endif
#if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
static const sendNoteFunc sendNoteCallback = nullptr;
static constexpr const sendNoteFunc sendNoteCallback = nullptr;
#endif

// -----------------------------------------------------------------------
Expand Down Expand Up @@ -76,19 +79,8 @@ class UiLv2
const float scaleFactor,
const uint32_t bgColor,
const uint32_t fgColor)
: fUI(this, winId, sampleRate,
editParameterCallback,
setParameterCallback,
setStateCallback,
sendNoteCallback,
nullptr, // resize is very messy, hosts can do it without extensions
fileRequestCallback,
bundlePath,
dspPtr,
scaleFactor,
bgColor,
fgColor),
fUridMap(uridMap),
: fUridMap(uridMap),
fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)),
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
Expand All @@ -97,15 +89,23 @@ class UiLv2
fURIDs(uridMap),
fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
: LV2UI_INVALID_PORT_INDEX),
fWinIdWasNull(winId == 0)
fWinIdWasNull(winId == 0),
fUI(this, winId, sampleRate,
editParameterCallback,
setParameterCallback,
setStateCallback,
sendNoteCallback,
nullptr, // resize is very messy, hosts can do it without extensions
fileRequestCallback,
bundlePath, dspPtr, scaleFactor, bgColor, fgColor)
{
if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
// tell the DSP we're ready to receive msgs
setState("__dpf_ui_data__", "");
#endif
#endif

if (winId != 0)
return;
Expand Down Expand Up @@ -169,7 +169,7 @@ class UiLv2

fUI.parameterChanged(rindex-parameterOffset, value);
}
#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE
else if (format == fURIDs.atomEventTransfer)
{
const LV2_Atom* const atom = (const LV2_Atom*)buffer;
Expand All @@ -181,39 +181,44 @@ class UiLv2

fUI.stateChanged(key, value);
}
else if (atom->type == fURIDs.atomObject)
else if (atom->type == fURIDs.atomObject && fUridUnmap != nullptr)
{
/* TODO
const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)LV2_ATOM_BODY_CONST(atom);
const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)atom;

const LV2_Atom* property = nullptr;
const LV2_Atom* avalue = nullptr;
lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &avalue, 0);
const LV2_Atom* atomvalue = nullptr;
lv2_atom_object_get(obj, fURIDs.patchProperty, &property, fURIDs.patchValue, &atomvalue, 0);

DISTRHO_SAFE_ASSERT_RETURN(property != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(avalue != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(atomvalue != nullptr,);

DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID,);
DISTRHO_SAFE_ASSERT_RETURN(avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString,);
DISTRHO_SAFE_ASSERT_RETURN(atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString,);

if (property != nullptr && property->type == fURIDs.atomURID &&
avalue != nullptr && (avalue->type == fURIDs.atomPath || avalue->type == fURIDs.atomString))
atomvalue != nullptr && (atomvalue->type == fURIDs.atomPath || atomvalue->type == fURIDs.atomString))
{
const char* const key = (const char*)LV2_ATOM_BODY_CONST(property);
const char* const value = (const char*)LV2_ATOM_BODY_CONST(avalue);
const LV2_URID dpf_lv2_urid = ((const LV2_Atom_URID*)property)->body;
DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_urid != 0,);

const char* const dpf_lv2_key = fUridUnmap->unmap(fUridUnmap->handle, dpf_lv2_urid);
DISTRHO_SAFE_ASSERT_RETURN(dpf_lv2_key != nullptr,);

/*constexpr*/ const size_t reqLen = std::strlen(DISTRHO_PLUGIN_URI "#");
DISTRHO_SAFE_ASSERT_RETURN(std::strlen(dpf_lv2_key) > reqLen,);

d_stdout("received atom object '%s' '%s'", key, value);
const char* const key = dpf_lv2_key + reqLen;
const char* const value = (const char*)LV2_ATOM_BODY_CONST(atomvalue);

fUI.stateChanged(key, value);
}
*/
}
else
{
d_stdout("received atom not dpfKeyValue");
d_stdout("DPF :: received atom not handled");
}
}
#endif
#endif
}

// -------------------------------------------------------------------
Expand Down Expand Up @@ -269,24 +274,91 @@ class UiLv2

// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_WANT_PROGRAMS
#if DISTRHO_PLUGIN_WANT_PROGRAMS
void lv2ui_select_program(const uint32_t bank, const uint32_t program)
{
const uint32_t realProgram = bank * 128 + program;

fUI.programLoaded(realProgram);
}
#endif
#endif

// -------------------------------------------------------------------

protected:
private:
// LV2 features
const LV2_URID_Map* const fUridMap;
const LV2_URID_Unmap* const fUridUnmap;
const LV2UI_Port_Map* const fUiPortMap;
const LV2UI_Request_Value* const fUiRequestValue;
const LV2UI_Touch* const fUiTouch;

// LV2 UI stuff
const LV2UI_Controller fController;
const LV2UI_Write_Function fWriteFunction;

// LV2 URIDs
const struct URIDs {
const LV2_URID_Map* _uridMap;
const LV2_URID dpfKeyValue;
const LV2_URID atomEventTransfer;
const LV2_URID atomFloat;
const LV2_URID atomLong;
const LV2_URID atomObject;
const LV2_URID atomPath;
const LV2_URID atomString;
const LV2_URID atomURID;
const LV2_URID midiEvent;
const LV2_URID paramSampleRate;
const LV2_URID patchProperty;
const LV2_URID patchSet;
const LV2_URID patchValue;

URIDs(const LV2_URID_Map* const uridMap)
: _uridMap(uridMap),
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
atomEventTransfer(map(LV2_ATOM__eventTransfer)),
atomFloat(map(LV2_ATOM__Float)),
atomLong(map(LV2_ATOM__Long)),
atomObject(map(LV2_ATOM__Object)),
atomPath(map(LV2_ATOM__Path)),
atomString(map(LV2_ATOM__String)),
atomURID(map(LV2_ATOM__URID)),
midiEvent(map(LV2_MIDI__MidiEvent)),
paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
patchProperty(map(LV2_PATCH__property)),
patchSet(map(LV2_PATCH__Set)),
patchValue(map(LV2_PATCH__value)) {}

inline LV2_URID map(const char* const uri) const
{
return _uridMap->map(_uridMap->handle, uri);
}
} fURIDs;

// index of bypass parameter, if present
const uint32_t fBypassParameterIndex;

// using ui:showInterface if true
const bool fWinIdWasNull;

// Plugin UI (after LV2 stuff so the UI can call into us during its constructor)
UIExporter fUI;

// ----------------------------------------------------------------------------------------------------------------
// DPF callbacks

void editParameterValue(const uint32_t rindex, const bool started)
{
if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
fUiTouch->touch(fUiTouch->handle, rindex, started);
}

static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
{
static_cast<UiLv2*>(ptr)->editParameterValue(rindex, started);
}

void setParameterValue(const uint32_t rindex, float value)
{
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
Expand All @@ -297,6 +369,12 @@ class UiLv2
fWriteFunction(fController, rindex, sizeof(float), 0, &value);
}

static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
{
static_cast<UiLv2*>(ptr)->setParameterValue(rindex, value);
}

#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const key, const char* const value)
{
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
Expand Down Expand Up @@ -336,7 +414,13 @@ class UiLv2
free(atomBuf);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static void setStateCallback(void* const ptr, const char* const key, const char* const value)
{
static_cast<UiLv2*>(ptr)->setState(key, value);
}
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
Expand All @@ -358,7 +442,12 @@ class UiLv2
fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
fURIDs.atomEventTransfer, &atomMidiEvent);
}
#endif

static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
{
static_cast<UiLv2*>(ptr)->sendNote(channel, note, velocity);
}
#endif

bool fileRequest(const char* const key)
{
Expand All @@ -379,97 +468,10 @@ class UiLv2
return r == LV2UI_REQUEST_VALUE_SUCCESS;
}

private:
UIExporter fUI;

// LV2 features
const LV2_URID_Map* const fUridMap;
const LV2UI_Port_Map* const fUiPortMap;
const LV2UI_Request_Value* const fUiRequestValue;
const LV2UI_Touch* const fUiTouch;

// LV2 UI stuff
const LV2UI_Controller fController;
const LV2UI_Write_Function fWriteFunction;

// LV2 URIDs
const struct URIDs {
const LV2_URID_Map* _uridMap;
const LV2_URID dpfKeyValue;
const LV2_URID atomEventTransfer;
const LV2_URID atomFloat;
const LV2_URID atomLong;
const LV2_URID atomObject;
const LV2_URID atomPath;
const LV2_URID atomString;
const LV2_URID atomURID;
const LV2_URID midiEvent;
const LV2_URID paramSampleRate;
const LV2_URID patchProperty;
const LV2_URID patchSet;
const LV2_URID patchValue;

URIDs(const LV2_URID_Map* const uridMap)
: _uridMap(uridMap),
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
atomEventTransfer(map(LV2_ATOM__eventTransfer)),
atomFloat(map(LV2_ATOM__Float)),
atomLong(map(LV2_ATOM__Long)),
atomObject(map(LV2_ATOM__Object)),
atomPath(map(LV2_ATOM__Path)),
atomString(map(LV2_ATOM__String)),
atomURID(map(LV2_ATOM__URID)),
midiEvent(map(LV2_MIDI__MidiEvent)),
paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
patchProperty(map(LV2_PATCH__property)),
patchSet(map(LV2_PATCH__Set)),
patchValue(map(LV2_PATCH__value)) {}

inline LV2_URID map(const char* const uri) const
{
return _uridMap->map(_uridMap->handle, uri);
}
} fURIDs;

// index of bypass parameter, if present
const uint32_t fBypassParameterIndex;

// using ui:showInterface if true
const bool fWinIdWasNull;

// -------------------------------------------------------------------
// Callbacks

#define uiPtr ((UiLv2*)ptr)

static void editParameterCallback(void* ptr, uint32_t rindex, bool started)
{
uiPtr->editParameterValue(rindex, started);
}

static void setParameterCallback(void* ptr, uint32_t rindex, float value)
{
uiPtr->setParameterValue(rindex, value);
}

static void setStateCallback(void* ptr, const char* key, const char* value)
{
uiPtr->setState(key, value);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
{
uiPtr->sendNote(channel, note, velocity);
}
#endif

static bool fileRequestCallback(void* ptr, const char* key)
{
return uiPtr->fileRequest(key);
return static_cast<UiLv2*>(ptr)->fileRequest(key);
}

#undef uiPtr
};

// -----------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions dpf/distrho/src/DistrhoUIPrivateData.hpp
Expand Up @@ -215,7 +215,7 @@ class PluginWindow : public DGL_NAMESPACE::Window
puglBackendLeave(pData->view);
}

// used for temporary windows (VST2/3 get size without active/visible view)
// used for temporary windows (VST/CLAP get size without active/visible view)
void setIgnoreIdleCallbacks(const bool ignore = true)
{
pData->ignoreIdleCallbacks = ignore;
Expand All @@ -228,8 +228,8 @@ class PluginWindow : public DGL_NAMESPACE::Window
puglBackendEnter(pData->view);
}

#ifdef DISTRHO_PLUGIN_TARGET_VST3
void setSizeForVST3(const uint width, const uint height)
#if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP)
void setSizeFromHost(const uint width, const uint height)
{
puglSetSizeAndDefault(pData->view, width, height);
}
Expand Down