From 4ef519266dbf3ab8d58db68367f9c0f0076a9d9c Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 26 Sep 2021 02:42:49 +0100 Subject: [PATCH] VST3: Implement UI->DSP state messaging Signed-off-by: falkTX --- distrho/src/DistrhoPluginVST3.cpp | 40 ++++++- distrho/src/DistrhoUIVST3.cpp | 190 ++++++++++++++++++++++++++++-- distrho/src/travesty/base.h | 21 +++- examples/States/Makefile | 11 +- 4 files changed, 235 insertions(+), 27 deletions(-) diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 81479aeb..b97b8a72 100644 --- 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; iget_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; isetStateFromUI(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& 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& attrs; + + dpf_attribute_list_dpf(std::map& 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 attrdpf; std::map 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 index 5b015318..a6b9dea0 100644 --- 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(static_cast(static_cast(static_cast(*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 static inline +v3_result v3_cpp_obj_query_interface(T** obj, const v3_tuid iid, M*** obj2) +{ + return static_cast(static_cast(*obj))->query_interface(obj, iid, (void**)obj2); +} + +template static inline +uint32_t v3_cpp_obj_ref(T** obj) +{ + return static_cast(static_cast(*obj))->ref(obj); +} + template static inline uint32_t v3_cpp_obj_unref(T** obj) { return static_cast(static_cast(*obj))->unref(obj); } + #endif diff --git a/examples/States/Makefile b/examples/States/Makefile index 0a0bfb77..b4190c6e 100644 --- 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)