commit 4ef519266dbf3ab8d58db68367f9c0f0076a9d9c
parent 5df663bba62b16afb346e0559675d109ecbb0f56
Author: falkTX <falktx@falktx.com>
Date: Sun, 26 Sep 2021 02:42:22 +0100
VST3: Implement UI->DSP state messaging
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
4 files changed, 235 insertions(+), 27 deletions(-)
diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp
@@ -182,11 +182,11 @@ const char* tuid2str(const v3_tuid iid)
// --------------------------------------------------------------------------------------------------------------------
-static void strncpy(char* const dst, const char* const src, const size_t size)
+static void strncpy(char* const dst, const char* const src, const size_t length)
{
- DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
+ DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
- if (const size_t len = std::min(std::strlen(src), size-1U))
+ if (const size_t len = std::min(std::strlen(src), length-1U))
{
std::memcpy(dst, src, len);
dst[len] = '\0';
@@ -197,11 +197,11 @@ static void strncpy(char* const dst, const char* const src, const size_t size)
}
}
-static void strncpy_utf16(int16_t* const dst, const char* const src, const size_t size)
+void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length)
{
- DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
+ DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
- if (const size_t len = std::min(std::strlen(src), size-1U))
+ if (const size_t len = std::min(std::strlen(src), length-1U))
{
for (size_t i=0; i<len; ++i)
{
@@ -1322,6 +1322,34 @@ public:
return requestParameterValueChange(rindex, value) ? V3_OK : V3_INTERNAL_ERR;
}
+#if DISTRHO_PLUGIN_WANT_STATE
+ if (std::strcmp(msgid, "state-set") == 0)
+ {
+ int16_t* key16;
+ int16_t* value16;
+ uint32_t keySize, valueSize;
+ v3_result res;
+
+ res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize);
+ DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
+
+ res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize);
+ DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
+
+ // do cheap inline conversion
+ char* const key = (char*)key16;
+ char* const value = (char*)value16;
+
+ for (uint32_t i=0; i<keySize/sizeof(int16_t); ++i)
+ key[i] = key16[i];
+ for (uint32_t i=0; i<valueSize/sizeof(int16_t); ++i)
+ value[i] = value16[i];
+
+ fPlugin.setState(key, value);
+ return V3_OK;
+ }
+#endif
+
return V3_NOT_IMPLEMENTED;
}
diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp
@@ -73,6 +73,7 @@ static constexpr const setStateFunc setStateCallback = nullptr;
// Utility functions (defined on plugin side)
const char* tuid2str(const v3_tuid iid);
+void strncpy_utf16(int16_t* dst, const char* src, size_t length);
// --------------------------------------------------------------------------------------------------------------------
// create message object (needed by the UI class, implementation comes later)
@@ -80,6 +81,20 @@ const char* tuid2str(const v3_tuid iid);
static v3_message** dpf_message_create(const char* id);
// --------------------------------------------------------------------------------------------------------------------
+// custom attribute list struct, used for sending utf8 strings
+
+struct v3_attribute_list_utf8 {
+ V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string);
+ V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size);
+};
+
+static constexpr const v3_tuid v3_attribute_list_utf8_iid =
+ V3_ID(d_cconst('D','P','F',' '),
+ d_cconst('c','l','a','s'),
+ d_cconst('a','t','t','r'),
+ d_cconst('u','t','f','8'));
+
+// --------------------------------------------------------------------------------------------------------------------
/**
* VST3 UI class.
@@ -317,12 +332,28 @@ private:
}
#endif
-#if DISTRHO_PLUGIN_WANT_STATE
- void setState(const char* const /*key*/, const char* const /*value*/)
+ void setState(const char* const key, const char* const value)
{
- // fUiHelper->setStateFromUI(key, value);
+ DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
+
+ v3_message** const message = dpf_message_create("state-set");
+ DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
+
+ v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
+ DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,);
+
+ v3_attribute_list_utf8** utf8attrs = nullptr;
+ DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrs, v3_attribute_list_utf8_iid, &utf8attrs) == V3_OK,);
+ DISTRHO_SAFE_ASSERT_RETURN(utf8attrs != nullptr,);
+
+ v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "key", key);
+ v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "value", value);
+ v3_cpp_obj(fConnection)->notify(fConnection, message);
+
+ v3_cpp_obj_unref(message);
}
+#if DISTRHO_PLUGIN_WANT_STATE
static void setStateCallback(void* ptr, const char* key, const char* value)
{
((UIVst3*)ptr)->setState(key, value);
@@ -337,10 +368,9 @@ private:
*/
// --------------------------------------------------------------------------------------------------------------------
-// dpf_attribute_list
struct dpf_attribute_value {
- char type;
+ char type; // one of: i, f, s, b
union {
int64_t integer;
double v_float;
@@ -352,16 +382,113 @@ struct dpf_attribute_value {
};
};
+static void dpf_attribute_list_free(std::map<std::string, dpf_attribute_value>& attrs)
+{
+ for (auto& it : attrs)
+ {
+ dpf_attribute_value& v(it.second);
+
+ switch (v.type)
+ {
+ case 's':
+ case 'b':
+ std::free(v.binary.ptr);
+ break;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// dpf_attribute_list_dpf (the custom utf8 variant)
+
+struct v3_attribute_list_utf8_cpp : v3_funknown {
+ v3_attribute_list_utf8 attr;
+};
+
+struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp {
+ std::map<std::string, dpf_attribute_value>& attrs;
+
+ dpf_attribute_list_dpf(std::map<std::string, dpf_attribute_value>& a)
+ : attrs(a)
+ {
+ static const uint8_t* kSupportedInterfaces[] = {
+ v3_funknown_iid,
+ v3_attribute_list_utf8_iid
+ };
+
+ // ------------------------------------------------------------------------------------------------------------
+ // v3_funknown
+
+ query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result
+ {
+ d_stdout("dpf_attribute_list_dpf::query_interface => %p %s %p", self, tuid2str(iid), iface);
+ *iface = NULL;
+ DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);
+
+ for (const uint8_t* interface_iid : kSupportedInterfaces)
+ {
+ if (v3_tuid_match(interface_iid, iid))
+ {
+ *iface = self;
+ return V3_OK;
+ }
+ }
+
+ return V3_NO_INTERFACE;
+ };
+
+ // there is only a single instance of this, so we don't have to care here
+ ref = []V3_API(void*) -> uint32_t { return 1; };
+ unref = []V3_API(void*) -> uint32_t { return 0; };
+
+ // ------------------------------------------------------------------------------------------------------------
+ // v3_attribute_list_utf8
+
+ attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result
+ {
+ dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self;
+ DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
+
+ const uint32_t size = std::strlen(string) + 1;
+ int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size);
+ DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);
+
+ DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size);
+
+ dpf_attribute_value& attrval(attr->attrs[id]);
+ attrval.type = 's';
+ attrval.binary.ptr = copy;
+ attrval.binary.size = sizeof(int16_t) * size;
+ return V3_OK;
+ };
+
+ attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result
+ {
+ dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self;
+ DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
+
+ if (attr->attrs.find(id) == attr->attrs.end())
+ return V3_INVALID_ARG;
+
+ return V3_NOT_IMPLEMENTED;
+ };
+ }
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+// dpf_attribute_list
+
struct v3_attribute_list_cpp : v3_funknown {
v3_attribute_list attr;
};
struct dpf_attribute_list : v3_attribute_list_cpp {
+ ScopedPointer<dpf_attribute_list_dpf> attrdpf;
std::map<std::string, dpf_attribute_value> attrs;
dpf_attribute_list()
{
- static const uint8_t* kSupportedInterfaces[] = {
+ static const uint8_t* kSupportedInterfacesBase[] = {
v3_funknown_iid,
v3_attribute_list_iid
};
@@ -375,7 +502,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);
- for (const uint8_t* interface_iid : kSupportedInterfaces)
+ for (const uint8_t* interface_iid : kSupportedInterfacesBase)
{
if (v3_tuid_match(interface_iid, iid))
{
@@ -384,6 +511,17 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
}
}
+ dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
+ DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE);
+
+ if (v3_tuid_match(v3_attribute_list_utf8_iid, iid))
+ {
+ if (attr->attrdpf == nullptr)
+ attr->attrdpf = new dpf_attribute_list_dpf(attr->attrs);
+ *iface = &attr->attrdpf;
+ return V3_OK;
+ }
+
return V3_NO_INTERFACE;
};
@@ -400,6 +538,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
dpf_attribute_value& attrval(attr->attrs[id]);
+ attrval.type = 'i';
attrval.integer = value;
return V3_OK;
};
@@ -413,6 +552,8 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
return V3_INVALID_ARG;
const dpf_attribute_value& attrval(attr->attrs[id]);
+ DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG);
+
*value = attrval.integer;
return V3_OK;
};
@@ -423,6 +564,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
dpf_attribute_value& attrval(attr->attrs[id]);
+ attrval.type = 'f';
attrval.v_float = value;
return V3_OK;
};
@@ -436,15 +578,21 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
return V3_INVALID_ARG;
const dpf_attribute_value& attrval(attr->attrs[id]);
+ DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG);
+
*value = attrval.v_float;
return V3_OK;
};
- attr.set_string = []V3_API(void* self, const char* /*id*/, const int16_t* /*string*/) -> v3_result
+ attr.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
+ dpf_attribute_value& attrval(attr->attrs[id]);
+ attrval.type = 's';
+ attrval.binary.ptr = nullptr;
+ attrval.binary.size = 0;
return V3_NOT_IMPLEMENTED;
};
@@ -456,18 +604,30 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;
+ const dpf_attribute_value& attrval(attr->attrs[id]);
+ DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG);
+
return V3_NOT_IMPLEMENTED;
};
- attr.set_binary = []V3_API(void* self, const char* /*id*/, const void* /*data*/, uint32_t /*size*/) -> v3_result
+ attr.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
+ void* const copy = std::malloc(size);
+ DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);
+
+ std::memcpy(copy, data, size);
+
+ dpf_attribute_value& attrval(attr->attrs[id]);
+ attrval.type = 'b';
+ attrval.binary.ptr = copy;
+ attrval.binary.size = size;
return V3_NOT_IMPLEMENTED;
};
- attr.get_binary = []V3_API(void* self, const char* id, const void** /*data*/, uint32_t* /*size*/) -> v3_result
+ attr.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
@@ -475,7 +635,12 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;
- return V3_NOT_IMPLEMENTED;
+ const dpf_attribute_value& attrval(attr->attrs[id]);
+ DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG);
+
+ *data = attrval.binary.ptr;
+ *size = attrval.binary.size;
+ return V3_OK;
};
}
};
@@ -545,6 +710,9 @@ struct dpf_message : v3_message_cpp {
if (const int refcounter = --message->refcounter)
return refcounter;
+ if (message->attrlist != nullptr)
+ dpf_attribute_list_free(message->attrlist->attrs);
+
*message->self = nullptr;
delete messageptr;
return 0;
diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h
@@ -25,6 +25,7 @@
*/
#ifdef __cplusplus
+
/**
* cast object into its proper C++ type.
* this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields.
@@ -43,10 +44,13 @@ constexpr T* v3_cpp_obj(T** obj)
*/
return static_cast<T*>(static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(*obj)) + sizeof(void*)*3));
}
+
#else
+
# ifndef constexpr
# define constexpr
# endif
+
#endif
/**
@@ -179,12 +183,27 @@ static constexpr const v3_tuid v3_plugin_base_iid =
V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625);
#ifdef __cplusplus
+
/**
- * helper C++ function to manually call unref on an object.
+ * helper C++ functions to manually call v3_funknown methods on an object.
*/
+
+template<class T, class M> static inline
+v3_result v3_cpp_obj_query_interface(T** obj, const v3_tuid iid, M*** obj2)
+{
+ return static_cast<v3_funknown*>(static_cast<void*>(*obj))->query_interface(obj, iid, (void**)obj2);
+}
+
+template<class T> static inline
+uint32_t v3_cpp_obj_ref(T** obj)
+{
+ return static_cast<v3_funknown*>(static_cast<void*>(*obj))->ref(obj);
+}
+
template<class T> static inline
uint32_t v3_cpp_obj_unref(T** obj)
{
return static_cast<v3_funknown*>(static_cast<void*>(*obj))->unref(obj);
}
+
#endif
diff --git a/examples/States/Makefile b/examples/States/Makefile
@@ -26,17 +26,10 @@ include ../../Makefile.plugins.mk
# --------------------------------------------------------------
# Enable all possible plugin types
-ifeq ($(HAVE_OPENGL),true)
TARGETS += jack
-endif # HAVE_OPENGL
-
-ifeq ($(HAVE_OPENGL),true)
TARGETS += lv2_sep
-else
-TARGETS += lv2_dsp
-endif
-
-TARGETS += vst
+TARGETS += vst2
+TARGETS += vst3
all: $(TARGETS)