Browse Source

VST3: Use UI idle to request changes from DSP

Signed-off-by: falkTX <falktx@falktx.com>
pull/330/head
falkTX 4 years ago
parent
commit
9b94fe398e
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
3 changed files with 226 additions and 105 deletions
  1. +5
    -0
      distrho/src/DistrhoPluginInternal.hpp
  2. +153
    -78
      distrho/src/DistrhoPluginVST3.cpp
  3. +68
    -27
      distrho/src/DistrhoUIVST3.cpp

+ 5
- 0
distrho/src/DistrhoPluginInternal.hpp View File

@@ -541,6 +541,11 @@ public:
return (getParameterHints(index) & kParameterIsOutput) != 0x0;
}

bool isParameterTrigger(const uint32_t index) const noexcept
{
return (getParameterHints(index) & kParameterIsTrigger) == kParameterIsTrigger;
}

bool isParameterOutputOrTrigger(const uint32_t index) const noexcept
{
const uint32_t hints = getParameterHints(index);


+ 153
- 78
distrho/src/DistrhoPluginVST3.cpp View File

@@ -49,7 +49,7 @@
* - hide program parameter?
* - save and restore state
* - save and restore current program
* - proper send of parameter, program and state changes to UI
* - deal with parameter triggers
* - send current state to UI on request
* - midi cc parameter mapping
* - full MIDI1 encode and decode
@@ -290,6 +290,11 @@ static constexpr const v3_tuid v3_attribute_list_utf8_iid =

v3_message** dpf_message_create(const char* id);

// --------------------------------------------------------------------------------------------------------------------
// dpf_plugin_view_create (implemented on UI side)

v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate);

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

/**
@@ -321,10 +326,14 @@ public:
PluginVst3()
: fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
fComponentHandler(nullptr),
fConnectionToUI(nullptr),
fConnection(nullptr),
fConnectedToUI(false),
fParameterOffset(fPlugin.getParameterOffset()),
fRealParameterCount(fParameterOffset + fPlugin.getParameterCount()),
fParameterValues(nullptr)
#if DISTRHO_PLUGIN_HAS_UI
, fChangedParameterValues(nullptr)
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
, fHostEventOutputHandle(nullptr)
#endif
@@ -405,6 +414,14 @@ public:
for (uint32_t i=0; i < parameterCount; ++i)
fParameterValues[i] = fPlugin.getParameterDefault(i);
}

#if DISTRHO_PLUGIN_HAS_UI
if (fRealParameterCount != 0)
{
fChangedParameterValues = new bool[fRealParameterCount];
std::memset(fChangedParameterValues, 0, sizeof(bool)*fRealParameterCount);
}
#endif
}

~PluginVst3()
@@ -414,6 +431,14 @@ public:
delete[] fParameterValues;
fParameterValues = nullptr;
}

#if DISTRHO_PLUGIN_HAS_UI
if (fChangedParameterValues != nullptr)
{
delete[] fChangedParameterValues;
fChangedParameterValues = nullptr;
}
#endif
}

// ----------------------------------------------------------------------------------------------------------------
@@ -429,11 +454,6 @@ public:
return fPlugin.getSampleRate();
}

void setConnectionToUI(v3_connection_point** const point) noexcept
{
fConnectionToUI = point;
}

// ----------------------------------------------------------------------------------------------------------------
// v3_component interface calls

@@ -1270,36 +1290,39 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(rindex < fRealParameterCount, rindex, V3_INVALID_ARG);
DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0 && value <= 1.0, V3_INVALID_ARG);

// TESTING remove this
sendParameterChangeToUI(rindex, value);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (rindex == 0)
{
fCurrentProgram = std::round(value * fProgramCountMinusOne);
fPlugin.loadProgram(fCurrentProgram);
return V3_OK;
}
else
#endif
{
const uint32_t index = rindex - fParameterOffset;
const uint32_t hints = fPlugin.getParameterHints(index);
const ParameterRanges& ranges(fPlugin.getParameterRanges(index));

const uint32_t index = rindex - fParameterOffset;
const uint32_t hints = fPlugin.getParameterHints(index);
const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
float realValue = ranges.getUnnormalizedValue(value);

float realValue = ranges.getUnnormalizedValue(value);
if (hints & kParameterIsBoolean)
{
const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
realValue = realValue > midRange ? ranges.max : ranges.min;
}

if (hints & kParameterIsBoolean)
{
const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
realValue = realValue > midRange ? ranges.max : ranges.min;
}
if (hints & kParameterIsInteger)
{
realValue = std::round(realValue);
}

if (hints & kParameterIsInteger)
{
realValue = std::round(realValue);
fParameterValues[index] = realValue;
fPlugin.setParameterValue(index, realValue);
}

fPlugin.setParameterValue(index, realValue);
#if DISTRHO_PLUGIN_HAS_UI
fChangedParameterValues[rindex] = true;
#endif
return V3_OK;
}

@@ -1314,14 +1337,18 @@ public:

void connect(v3_connection_point** const other)
{
fConnectionToUI = other;
DISTRHO_SAFE_ASSERT(fConnectedToUI == false);

fConnection = other;
fConnectedToUI = false;

d_stdout("---------------------------------------------------------- will send plugin state now");
}

void disconnect()
{
fConnectionToUI = nullptr;
fConnection = nullptr;
fConnectedToUI = false;

d_stdout("---------------------------------------------------------- ui conn now null");
}
@@ -1351,9 +1378,63 @@ public:
}
#endif

if (std::strcmp(msgid, "init") == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR);
fConnectedToUI = true;

// report current state to UI
for (uint32_t i=0; i<fRealParameterCount; ++i)
{
#if DISTRHO_PLUGIN_HAS_UI
fChangedParameterValues[i] = false;
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (i == 0)
sendParameterChangeToUI(i, fCurrentProgram);
else
#endif
sendParameterChangeToUI(i, fPlugin.getParameterValue(i - fParameterOffset));
}

sendReadyToUI();
return V3_OK;
}

if (std::strcmp(msgid, "idle") == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR);
DISTRHO_SAFE_ASSERT_RETURN(fConnectedToUI, V3_INTERNAL_ERR);

#if DISTRHO_PLUGIN_HAS_UI
for (uint32_t i=0; i<fRealParameterCount; ++i)
{
fChangedParameterValues[i] = false;

#if DISTRHO_PLUGIN_WANT_PROGRAMS
if (i == 0)
sendParameterChangeToUI(i, fCurrentProgram);
else
#endif
sendParameterChangeToUI(i, fParameterValues[i - fParameterOffset]);
}
#endif

sendReadyToUI();
return V3_OK;
}

if (std::strcmp(msgid, "close") == 0)
{
fConnectedToUI = false;
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr, V3_INTERNAL_ERR);
return V3_OK;
}

if (std::strcmp(msgid, "parameter-edit") == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false);
DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR);

int64_t rindex;
int64_t started;
@@ -1429,12 +1510,16 @@ private:

// VST3 stuff
v3_component_handler** fComponentHandler;
v3_connection_point** fConnectionToUI;
v3_connection_point** fConnection;
bool fConnectedToUI;

// Temporary data
const uint32_t fParameterOffset;
const uint32_t fRealParameterCount; // regular parameters + current program
float* fParameterValues;
#if DISTRHO_PLUGIN_HAS_UI
bool* fChangedParameterValues;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
MidiEvent fMidiEvents[kMaxMidiEvents];
# if DISTRHO_PLUGIN_HAS_UI
@@ -1470,16 +1555,22 @@ private:
continue;

fParameterValues[i] = curValue;
#if DISTRHO_PLUGIN_HAS_UI
fChangedParameterValues[i] = true;
#endif
}
else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
else if (fPlugin.isParameterTrigger(i))
{
// NOTE: no trigger support in VST parameters, simulate it here
// NOTE: no trigger support in VST3 parameters, simulate it here
curValue = fPlugin.getParameterValue(i);

if (d_isEqual(curValue, fPlugin.getParameterDefault(i)))
continue;

fPlugin.setParameterValue(i, curValue);
#if DISTRHO_PLUGIN_HAS_UI
fChangedParameterValues[i] = true;
#endif
}
else
{
@@ -1495,9 +1586,6 @@ private:

void sendParameterChangeToUI(const v3_param_id rindex, const double value)
{
d_stdout("will send message now");
DISTRHO_SAFE_ASSERT_RETURN(fConnectionToUI != nullptr,);

v3_message** const message = dpf_message_create("parameter-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

@@ -1507,7 +1595,21 @@ private:
v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
v3_cpp_obj(attrlist)->set_float(attrlist, "value", value);
v3_cpp_obj(fConnectionToUI)->notify(fConnectionToUI, message);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);
}

void sendReadyToUI()
{
v3_message** const message = dpf_message_create("ready");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);

v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);
}
@@ -1603,13 +1705,6 @@ private:
* VST3 low-level pointer thingies follow, proceed with care.
*/

#if DISTRHO_PLUGIN_HAS_UI
// --------------------------------------------------------------------------------------------------------------------
// dpf_plugin_view_create (called from DSP side)

v3_plugin_view** dpf_plugin_view_create(void* instancePointer, double sampleRate);
#endif

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

struct dpf_attribute_value {
@@ -1936,7 +2031,6 @@ struct dpf_message : v3_message_cpp {

unref = []V3_API(void* const self) -> uint32_t
{
d_stdout("dpf_message::unref => %p", self);
dpf_message** const messageptr = static_cast<dpf_message**>(self);
DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0);
dpf_message* const message = *messageptr;
@@ -1955,7 +2049,6 @@ struct dpf_message : v3_message_cpp {

msg.get_message_id = []V3_API(void* const self) -> const char*
{
d_stdout("dpf_message::get_message_id => %p", self);
dpf_message* const message = *(dpf_message**)self;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr);

@@ -1973,7 +2066,6 @@ struct dpf_message : v3_message_cpp {

msg.get_attributes = []V3_API(void* const self) -> v3_attribute_list**
{
d_stdout("dpf_message::get_attributes => %p", self);
dpf_message* const message = *(dpf_message**)self;
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr);

@@ -2086,7 +2178,6 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp {

point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result
{
d_stdout("dpf_dsp_connection_point::notify => %p %p", self, message);
dpf_dsp_connection_point* const point = *(dpf_dsp_connection_point**)self;
DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED);

@@ -2102,68 +2193,52 @@ struct dpf_dsp_connection_point : v3_connection_point_cpp {
int64_t target = 0;
const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target);
DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res);
DISTRHO_SAFE_ASSERT_RETURN(target != 0, V3_INTERNAL_ERR);
DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR);

switch (point->type)
{
// pass message from component (aka plugin) to controller
// message belongs to component (aka plugin)
case kConnectionPointComponent:
{
d_stdout("dpf_dsp_connection_point::notify kConnectionPointComponent");

switch (target)
if (target == 1)
{
case 1:
// message is from view to controller to component
// view -> edit controller -> component
return vst3->notify(message);
case 2:
}
else
{
// message is from component to controller to view
return v3_cpp_obj(other)->notify(other, message);
}
break;
}

// pass message from controller to component (aka plugin)
// message belongs to edit controller
case kConnectionControllerToComponent:
{
d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToComponent");

switch (target)
if (target == 1)
{
case 1:
// message is from view to controller to component
// view -> edit controller -> component
return v3_cpp_obj(other)->notify(other, message);
case 2:
}
else
{
// message is from component to controller to view
v3_connection_point** const bridge = point->bridge;
DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED);
return v3_cpp_obj(bridge)->notify(bridge, message);
}
}
break;
}

// pass message from from view (aka ui) to controller
// message belongs to view (aka ui)
case kConnectionControllerToView:
{
d_stdout("dpf_dsp_connection_point::notify kConnectionControllerToView");

switch (target)
{
case 1:
if (target == 1)
{
// message is from view to controller to component
// view -> edit controller -> component
v3_connection_point** const bridge = point->bridge;
DISTRHO_SAFE_ASSERT_RETURN(bridge != nullptr, V3_NOT_INITIALISED);
return v3_cpp_obj(bridge)->notify(bridge, message);
}
case 2:
else
{
// message is from component to controller to view
return v3_cpp_obj(other)->notify(other, message);
}
break;
}
}

return V3_INTERNAL_ERR;


+ 68
- 27
distrho/src/DistrhoUIVST3.cpp View File

@@ -14,25 +14,6 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoPluginChecks.h"

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
# undef DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# undef DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

// #undef DISTRHO_PLUGIN_HAS_UI
// #define DISTRHO_PLUGIN_HAS_UI 1

#if DISTRHO_PLUGIN_HAS_UI

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

#include "DistrhoUIInternal.hpp"

#include <atomic>
@@ -45,10 +26,9 @@
#include "travesty/view.h"

/* TODO items:
* - disable UI if non-embed UI build
* - sample rate change listener
* - proper send of parameter, program and state changes to UI
* - request current state of UI when created
* - ui mousewheel event
* - ui key down/up events
*/

START_NAMESPACE_DISTRHO
@@ -119,6 +99,7 @@ public:
fView(view),
fConnection(nullptr),
fFrame(nullptr),
fReadyForPluginData(false),
fScaleFactor(scaleFactor)
{
// TESTING awful idea dont reuse
@@ -128,6 +109,9 @@ public:
~UIVst3() override
{
stopThread(5000);

if (fConnection != nullptr)
disconnect();
}

// TESTING awful idea dont reuse
@@ -135,6 +119,12 @@ public:
{
while (! shouldThreadExit())
{
if (fReadyForPluginData)
{
fReadyForPluginData = false;
requestMorePluginData();
}

fUI.plugin_idle();
d_msleep(50);
}
@@ -227,11 +217,40 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,);

fConnection = point;

d_stdout("requesting current plugin state");

v3_message** const message = dpf_message_create("init");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);

v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);
}

void disconnect() noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

d_stdout("reporting UI closed");

v3_message** const message = dpf_message_create("close");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);

v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);

fConnection = nullptr;
fReadyForPluginData = false;
}

v3_result notify(v3_message** const message)
@@ -301,6 +320,13 @@ public:
}
#endif

if (std::strcmp(msgid, "ready") == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR);
fReadyForPluginData = true;
return V3_OK;
}

d_stdout("UIVst3 received unknown msg '%s'", msgid);

return V3_NOT_IMPLEMENTED;
@@ -318,8 +344,28 @@ private:
v3_plugin_frame** fFrame;

// Temporary data
bool fReadyForPluginData;
float fScaleFactor;

// ----------------------------------------------------------------------------------------------------------------
// helper functions called during message passing

void requestMorePluginData() const
{
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("idle");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);

v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);
}

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

@@ -600,7 +646,6 @@ struct dpf_ui_connection_point : v3_connection_point_cpp {

point.notify = []V3_API(void* self, struct v3_message** message) -> v3_result
{
d_stdout("dpf_ui_connection_point::notify => %p %p", self, message);
dpf_ui_connection_point* const point = *(dpf_ui_connection_point**)self;
DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALISED);

@@ -906,7 +951,3 @@ v3_plugin_view** dpf_plugin_view_create(void* const instancePointer, const doubl
// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

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

#endif // DISTRHO_PLUGIN_HAS_UI

Loading…
Cancel
Save