diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp index 08a25a1f..81479aeb 100644 --- a/distrho/src/DistrhoPluginVST3.cpp +++ b/distrho/src/DistrhoPluginVST3.cpp @@ -113,6 +113,8 @@ const char* tuid2str(const v3_tuid iid) if (v3_tuid_match(iid, v3_audio_processor_iid)) return "{v3_audio_processor}"; + if (v3_tuid_match(iid, v3_attribute_list_iid)) + return "{v3_attribute_list_iid}"; if (v3_tuid_match(iid, v3_bstream_iid)) return "{v3_bstream}"; if (v3_tuid_match(iid, v3_component_iid)) @@ -1275,10 +1277,52 @@ public: v3_result notify(v3_message** const message) { - d_stdout("notify received %p -> %s", message, v3_cpp_obj(message)->get_message_id(message)); - // TESTING, ensure host gets the message - requestParameterValueChange(2, 1.0f); - return V3_OK; + const char* const msgid = v3_cpp_obj(message)->get_message_id(message); + DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG); + + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG); + + if (std::strcmp(msgid, "parameter-edit") == 0) + { + DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, false); + + int64_t rindex; + int64_t started; + v3_result res; + + res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_int(attrs, "started", &started); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + rindex -= fParameterOffset; + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + return started != 0 ? v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex) + : v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex); + } + + if (std::strcmp(msgid, "parameter-set") == 0) + { + int64_t rindex; + double value; + v3_result res; + + res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value); + DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + + rindex -= fParameterOffset; + DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR); + + return requestParameterValueChange(rindex, value) ? V3_OK : V3_INTERNAL_ERR; + } + + return V3_NOT_IMPLEMENTED; } // ---------------------------------------------------------------------------------------------------------------- diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp index 677501c7..a6142142 100644 --- a/distrho/src/DistrhoUIVST3.cpp +++ b/distrho/src/DistrhoUIVST3.cpp @@ -42,6 +42,10 @@ #include "travesty/message.h" #include "travesty/view.h" +#include +#include +#include + /* TODO items: * - disable UI if non-embed UI build * - parameter change listener @@ -71,19 +75,9 @@ static constexpr const setStateFunc setStateCallback = nullptr; const char* tuid2str(const v3_tuid iid); // -------------------------------------------------------------------------------------------------------------------- -// dpf_message (needed by the UI class, implementation comes later) - -struct v3_message_cpp : v3_funknown { - v3_message msg; -}; +// create message object (needed by the UI class, implementation comes later) -// NOTE value type must be POD -template -struct dpf_message : v3_message_cpp { - dpf_message(ScopedPointer* self, const char* id, T value); - struct PrivateData; - PrivateData* const pData; -}; +static v3_message** dpf_message_create(const char* id); // -------------------------------------------------------------------------------------------------------------------- @@ -238,14 +232,21 @@ private: // ---------------------------------------------------------------------------------------------------------------- // DPF callbacks - void editParameter(const uint32_t /*rindex*/, const bool /*started*/) const + void editParameter(const uint32_t rindex, const bool started) const { -// DISTRHO_SAFE_ASSERT_RETURN(fHandler != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); + + v3_message** const message = dpf_message_create("parameter-edit"); + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); -// if (started) -// v3_cpp_obj(fHandler)->begin_edit(fHandler, rindex); -// else -// v3_cpp_obj(fHandler)->end_edit(fHandler, rindex); + v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message); + DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,); + + v3_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); + v3_cpp_obj(attrs)->set_int(attrs, "started", started ? 1 : 0); + v3_cpp_obj(fConnection)->notify(fConnection, message); + + v3_cpp_obj_unref(message); } static void editParameterCallback(void* ptr, uint32_t rindex, bool started) @@ -256,16 +257,18 @@ private: void setParameterValue(const uint32_t rindex, const float realValue) { DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,); - d_stdout("setParameterValue %p %u %f", this, rindex, realValue); - struct IndexAndValue { - uint32_t index; - float value; - }; - ScopedPointer>* const messageptr = new ScopedPointer>; - *messageptr = new dpf_message(messageptr, "parameter-value-set", { rindex, realValue }); + v3_message** const message = dpf_message_create("parameter-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_cpp_obj(attrs)->set_int(attrs, "rindex", rindex); + v3_cpp_obj(attrs)->set_float(attrs, "value", realValue); + v3_cpp_obj(fConnection)->notify(fConnection, message); - v3_cpp_obj(fConnection)->notify(fConnection, (v3_message**)messageptr); + v3_cpp_obj_unref(message); } static void setParameterCallback(void* ptr, uint32_t rindex, float value) @@ -334,117 +337,258 @@ private: */ // -------------------------------------------------------------------------------------------------------------------- -// dpf_message (implementation only, declaration already done as needed by UI class) +// dpf_attribute_list + +struct dpf_attribute_value { + char type; + union { + int64_t integer; + double v_float; + int16_t* string; + struct { + void* ptr; + uint32_t size; + } binary; + }; +}; + +struct v3_attribute_list_cpp : v3_funknown { + v3_attribute_list attr; +}; + +struct dpf_attribute_list : v3_attribute_list_cpp { + std::map attrs; + + dpf_attribute_list() + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_attribute_list_iid + }; + + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown + + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_attribute_list::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 + + attr.set_int = []V3_API(void* self, const char* id, int64_t value) -> 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.integer = value; + return V3_OK; + }; + + attr.get_int = []V3_API(void* self, const char* id, int64_t* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + *value = attrval.integer; + return V3_OK; + }; + + attr.set_float = []V3_API(void* self, const char* id, double value) -> 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.v_float = value; + return V3_OK; + }; + + attr.get_float = []V3_API(void* self, const char* id, double* value) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)self; + DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + const dpf_attribute_value& attrval(attr->attrs[id]); + *value = attrval.v_float; + return V3_OK; + }; + + 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); + + return V3_NOT_IMPLEMENTED; + }; + + attr.get_string = []V3_API(void* self, const char* id, int16_t* /*string*/, uint32_t /*size*/) -> v3_result + { + dpf_attribute_list* const attr = *(dpf_attribute_list**)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; + }; + + 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); + + return V3_NOT_IMPLEMENTED; + }; + + 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); + + if (attr->attrs.find(id) == attr->attrs.end()) + return V3_INVALID_ARG; + + return V3_NOT_IMPLEMENTED; + }; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// dpf_message -template -struct dpf_message::PrivateData { +struct v3_message_cpp : v3_funknown { + v3_message msg; +}; + +struct dpf_message : v3_message_cpp { std::atomic refcounter; ScopedPointer* self; + ScopedPointer attrlist; String id; - const T value; - PrivateData(ScopedPointer* const s, const char* const id2, T value2) - : refcounter(1), - self(s), - id(id2), - value(value2) {} -}; + dpf_message(ScopedPointer* const s, const char* const id2) + : self(s), + id(id2) + { + static const uint8_t* kSupportedInterfaces[] = { + v3_funknown_iid, + v3_message_iid + }; -static V3_API v3_result dpf_message__query_interface(void* self, const v3_tuid iid, void** iface) -{ - d_stdout("dpf_message::query_interface => %p %s %p", self, tuid2str(iid), iface); - *iface = NULL; - DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE); + // ------------------------------------------------------------------------------------------------------------ + // v3_funknown - static const uint8_t* kSupportedInterfaces[] = { - v3_funknown_iid, - v3_message_iid - }; + query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result + { + d_stdout("dpf_plugin_view::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)) + for (const uint8_t* interface_iid : kSupportedInterfaces) + { + if (v3_tuid_match(interface_iid, iid)) + { + *iface = self; + return V3_OK; + } + } + + return V3_NO_INTERFACE; + }; + + ref = []V3_API(void* const self) -> uint32_t { - *iface = self; - return V3_OK; - } - } + d_stdout("dpf_message::ref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - return V3_NO_INTERFACE; -} + return ++message->refcounter; + }; -template -static V3_API uint32_t dpf_message__ref(void* const self) -{ - d_stdout("dpf_message::ref => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); + unref = []V3_API(void* const self) -> uint32_t + { + d_stdout("dpf_message::unref => %p", self); + dpf_message** const messageptr = static_cast(self); + DISTRHO_SAFE_ASSERT_RETURN(messageptr != nullptr, 0); + dpf_message* const message = *messageptr; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - return ++message->pData->refcounter; -} + if (const int refcounter = --message->refcounter) + return refcounter; -template -static V3_API uint32_t dpf_message__unref(void* const self) -{ - d_stdout("dpf_message::unref => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, 0); - - if (const int refcounter = --message->pData->refcounter) - return refcounter; - - dpf_message* const messageptr = message->pData->self->release(); - delete message->pData; - delete messageptr; - delete (dpf_message**)self; - return 0; -} + *message->self = nullptr; + delete messageptr; + return 0; + }; -template -static V3_API const char* dpf_message__get_message_id(void* const self) -{ - d_stdout("dpf_message::get_message_id => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + 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); - return message->pData->id; -} + return message->id; + }; -template -static V3_API void dpf_message__set_message_id(void* const self, const char* const id) -{ - d_stdout("dpf_message::set_message_id => %p %s", self, id); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); + msg.set_message_id = []V3_API(void* const self, const char* const id) -> void + { + d_stdout("dpf_message::set_message_id => %p %s", self, id); + dpf_message* const message = *(dpf_message**)self; + DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,); - message->pData->id = id; -} + message->id = id; + }; -template -static V3_API v3_attribute_list* dpf_message__get_attributes(void* const self) -{ - d_stdout("dpf_message::get_attributes => %p", self); - dpf_message* const message = *(dpf_message**)self; - DISTRHO_SAFE_ASSERT_RETURN(message != nullptr, nullptr); + 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); - // TODO - return nullptr; -} + if (message->attrlist == nullptr) + message->attrlist = new dpf_attribute_list(); -template -dpf_message::dpf_message(ScopedPointer* const self, const char* const id, const T value) - : pData(new PrivateData(self, id, value)) + return (v3_attribute_list**)&message->attrlist; + }; + } +}; + +static v3_message** dpf_message_create(const char* const id) { - query_interface = dpf_message__query_interface; - ref = dpf_message__ref; - unref = dpf_message__unref; - msg.get_message_id = dpf_message__get_message_id; - msg.set_message_id = dpf_message__set_message_id; - msg.get_attributes = dpf_message__get_attributes; + ScopedPointer* const messageptr = new ScopedPointer; + *messageptr = new dpf_message(messageptr, id); + return static_cast(static_cast(messageptr)); } -template struct dpf_message; - // -------------------------------------------------------------------------------------------------------------------- // dpf_plugin_view_content_scale diff --git a/distrho/src/travesty/base.h b/distrho/src/travesty/base.h index 9c54f215..5b015318 100644 --- a/distrho/src/travesty/base.h +++ b/distrho/src/travesty/base.h @@ -25,6 +25,15 @@ */ #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. + * we can use this as a little helper for keeping both C and C++ compatiblity. + * specialized templated calls are defined where required + * (that is, object inherits from something other than `v3_funknown`) + * + * example usage: `v3_cpp_obj(obj)->method(obj, args...);` + */ template static inline constexpr T* v3_cpp_obj(T** obj) { @@ -168,3 +177,14 @@ struct v3_plugin_base { 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. + */ +template static inline +uint32_t v3_cpp_obj_unref(T** obj) +{ + return static_cast(static_cast(*obj))->unref(obj); +} +#endif diff --git a/distrho/src/travesty/message.h b/distrho/src/travesty/message.h index 70e42b18..2d022bbd 100644 --- a/distrho/src/travesty/message.h +++ b/distrho/src/travesty/message.h @@ -21,17 +21,35 @@ #include "align_push.h" /** - * message + * attribute list */ -struct v3_attribute_list; +struct v3_attribute_list { + struct v3_funknown; + + V3_API v3_result (*set_int)(void* self, const char* id, int64_t value); + V3_API v3_result (*get_int)(void* self, const char* id, int64_t* value); + V3_API v3_result (*set_float)(void* self, const char* id, double value); + V3_API v3_result (*get_float)(void* self, const char* id, double* value); + V3_API v3_result (*set_string)(void* self, const char* id, const int16_t* string); + V3_API v3_result (*get_string)(void* self, const char* id, int16_t* string, uint32_t size); + V3_API v3_result (*set_binary)(void* self, const char* id, const void* data, uint32_t size); + V3_API v3_result (*get_binary)(void* self, const char* id, const void** data, uint32_t* size); +}; + +static constexpr const v3_tuid v3_attribute_list_iid = + V3_ID(0x1E5F0AEB, 0xCC7F4533, 0xA2544011, 0x38AD5EE4); + +/** + * message + */ struct v3_message { struct v3_funknown; V3_API const char* (*get_message_id)(void* self); V3_API void (*set_message_id)(void* self, const char* id); - V3_API v3_attribute_list* (*get_attributes)(void* self); + V3_API v3_attribute_list** (*get_attributes)(void* self); }; static constexpr const v3_tuid v3_message_iid =