commit 9b94fe398e6d1810d6d49ca34862d02fad5d14ec
parent 3c0d54c3de7bc15a9352723640860c2e73316248
Author: falkTX <falktx@falktx.com>
Date: Sun, 26 Sep 2021 20:24:18 +0100
VST3: Use UI idle to request changes from DSP
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
3 files changed, 226 insertions(+), 105 deletions(-)
diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp
@@ -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);
diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp
@@ -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
@@ -291,6 +291,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);
+
+// --------------------------------------------------------------------------------------------------------------------
/**
* VST3 DSP class.
@@ -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;
diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp
@@ -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,9 +344,29 @@ 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
void editParameter(const uint32_t rindex, const bool started) const
@@ -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