diff --git a/source/backend/plugin/CarlaPluginVST3.cpp b/source/backend/plugin/CarlaPluginVST3.cpp index e0617e02b..14a691f97 100644 --- a/source/backend/plugin/CarlaPluginVST3.cpp +++ b/source/backend/plugin/CarlaPluginVST3.cpp @@ -51,6 +51,7 @@ #endif #include +#include CARLA_BACKEND_START_NAMESPACE @@ -99,11 +100,273 @@ static uint32_t V3_API v3_unref_static(void*) { return 0; } // -------------------------------------------------------------------------------------------------------------------- +struct v3_var { + char type; + uint32_t size; + union { + int64_t i; + double f; + int16_t* s; + void* b; + } value; +}; + +static void v3_var_cleanup(v3_var& var) +{ + switch (var.type) + { + case 's': + std::free(var.value.s); + break; + case 'b': + std::free(var.value.b); + break; + } + + carla_zeroStruct(var); +} + +struct carla_v3_attribute_list : v3_attribute_list_cpp { + // std::atomic refcounter; + std::unordered_map vars; + + carla_v3_attribute_list() + // : refcounter(1) + { + query_interface = carla_query_interface; + ref = v3_ref_static; + unref = v3_unref_static; + attrlist.set_int = carla_set_int; + attrlist.get_int = carla_get_int; + attrlist.set_float = carla_set_float; + attrlist.get_float = carla_get_float; + attrlist.set_string = carla_set_string; + attrlist.get_string = carla_get_string; + attrlist.set_binary = carla_set_binary; + attrlist.get_binary = carla_get_binary; + } + + ~carla_v3_attribute_list() + { + for (std::unordered_map::iterator it = vars.begin(); it != vars.end(); ++it) + v3_var_cleanup(it->second); + } + + v3_result add(const char* const id, const v3_var& var) + { + const std::string sid(id); + + for (std::unordered_map::iterator it = vars.begin(); it != vars.end(); ++it) + { + if (it->first == sid) + { + v3_var_cleanup(it->second); + break; + } + } + + vars[sid] = var; + return V3_OK; + } + + bool get(const char* const id, v3_var& var) + { + const std::string sid(id); + + for (std::unordered_map::iterator it = vars.begin(); it != vars.end(); ++it) + { + if (it->first == sid) + { + var = it->second; + return true; + } + } + + return false; + } + + static v3_result V3_API carla_query_interface(void* const self, const v3_tuid iid, void** const iface) + { + carla_debug("%s %s %p", __PRETTY_FUNCTION__, tuid2str(iid), iface); + // carla_v3_attribute_list* const attrlist = *static_cast(self); + + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_attribute_list_iid)) + { + // ++attrlist->refcounter; + *iface = self; + return V3_OK; + } + + *iface = nullptr; + carla_stdout("TODO carla_v3_attribute_list::query_interface %s", tuid2str(iid)); + return V3_NO_INTERFACE; + } + + /* + static uint32_t V3_API carla_ref(void* const self) + { + carla_v3_attribute_list* const attrlist = *static_cast(self); + const int refcount = ++msg->refcounter; + carla_debug("carla_v3_attribute_list::ref => %p | refcount %i", self, refcount); + return refcount; + } + + static uint32_t V3_API carla_unref(void* const self) + { + carla_v3_attribute_list** const attrlistptr = static_cast(self); + carla_v3_attribute_list* const attrlist = *attrlistptr; + + if (const int refcount = --attrlist->refcounter) + { + carla_debug("carla_v3_attribute_list::unref => %p | refcount %i", self, refcount); + return refcount; + } + + carla_debug("carla_v3_attribute_list::unref => %p | refcount is zero, deleting factory", self); + + delete attrlist; + delete attrlistptr; + return 0; + } + */ + + static v3_result carla_set_int(void* const self, const char* const id, const int64_t value) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + var.type = 'i'; + var.value.i = value; + return attrlist->add(id, var); + } + + static v3_result carla_get_int(void* const self, const char* const id, int64_t* const value) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + if (attrlist->get(id, var)) + { + *value = var.value.i; + return V3_OK; + } + + return V3_INVALID_ARG; + } + + static v3_result carla_set_float(void* const self, const char* const id, const double value) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + var.type = 'f'; + var.value.f = value; + return attrlist->add(id, var); + } + + static v3_result carla_get_float(void* const self, const char* const id, double* const value) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + if (attrlist->get(id, var)) + { + *value = var.value.f; + return V3_OK; + } + + return V3_INVALID_ARG; + } + + static v3_result carla_set_string(void* const self, const char* const id, const int16_t* const string) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(string != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + const size_t size = sizeof(int16_t) * (strlen_utf16(string) + 1); + int16_t* const s = static_cast(std::malloc(size)); + CARLA_SAFE_ASSERT_RETURN(s != nullptr, V3_NOMEM); + std::memcpy(s, string, size); + + v3_var var = {}; + var.type = 's'; + var.size = size; + var.value.s = s; + return attrlist->add(id, var); + } + + static v3_result carla_get_string(void* const self, const char* const id, + int16_t* const string, const uint32_t size) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(string != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(size != 0, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + if (attrlist->get(id, var)) + { + CARLA_SAFE_ASSERT_UINT2_RETURN(var.size >= size, var.size, size, V3_INVALID_ARG); + std::memcpy(string, var.value.s, size); + return V3_OK; + } + + return V3_INVALID_ARG; + } + + static v3_result carla_set_binary(void* const self, const char* const id, + const void* const data, const uint32_t size) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(data != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(size != 0, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + void* const b = std::malloc(size); + CARLA_SAFE_ASSERT_RETURN(b != nullptr, V3_NOMEM); + std::memcpy(b, data, size); + + v3_var var = {}; + var.type = 'b'; + var.size = size; + var.value.b = b; + return attrlist->add(id, var); + } + + static v3_result carla_get_binary(void* const self, const char* const id, + const void** const data, uint32_t* const size) + { + CARLA_SAFE_ASSERT_RETURN(id != nullptr, V3_INVALID_ARG); + carla_v3_attribute_list* const attrlist = *static_cast(self); + + v3_var var = {}; + if (attrlist->get(id, var)) + { + *data = var.value.b; + *size = var.size; + return V3_OK; + } + + return V3_INVALID_ARG; + } +}; + struct carla_v3_message : v3_message_cpp { std::atomic refcounter; + carla_v3_attribute_list attrlist; + carla_v3_attribute_list* attrlistptr; + const char* msgId; carla_v3_message() - : refcounter(1) + : refcounter(1), + attrlistptr(&attrlist), + msgId(nullptr) { query_interface = carla_query_interface; ref = carla_ref; @@ -113,6 +376,11 @@ struct carla_v3_message : v3_message_cpp { msg.get_attributes = carla_get_attributes; } + ~carla_v3_message() + { + delete[] msgId; + } + static v3_result V3_API carla_query_interface(void* const self, const v3_tuid iid, void** const iface) { carla_debug("%s %s %p", __PRETTY_FUNCTION__, tuid2str(iid), iface); @@ -157,21 +425,23 @@ struct carla_v3_message : v3_message_cpp { return 0; } - static const char* V3_API carla_get_message_id(void*) + static const char* V3_API carla_get_message_id(void* const self) { - carla_debug("TODO %s", __PRETTY_FUNCTION__); - return nullptr; + carla_v3_message* const msg = *static_cast(self); + return msg->msgId; } - static void V3_API carla_set_message_id(void*, const char*) + static void V3_API carla_set_message_id(void* const self, const char* const id) { - carla_debug("TODO %s", __PRETTY_FUNCTION__); + carla_v3_message* const msg = *static_cast(self); + delete[] msg->msgId; + msg->msgId = id != nullptr ? carla_strdup(id) : nullptr; } - static v3_attribute_list** V3_API carla_get_attributes(void*) + static v3_attribute_list** V3_API carla_get_attributes(void* const self) { - carla_debug("TODO %s", __PRETTY_FUNCTION__); - return nullptr; + carla_v3_message* const msg = *static_cast(self); + return (v3_attribute_list**)&msg->attrlistptr; } CARLA_DECLARE_NON_COPYABLE(carla_v3_message) @@ -179,6 +449,114 @@ struct carla_v3_message : v3_message_cpp { // -------------------------------------------------------------------------------------------------------------------- +struct carla_v3_bstream : v3_bstream_cpp { + void* buffer; + int64_t size; + int64_t pos; + + carla_v3_bstream() + : buffer(nullptr), + size(0), + pos(0) + { + query_interface = carla_query_interface; + ref = v3_ref_static; + unref = v3_unref_static; + stream.read = carla_read; + stream.write = carla_write; + stream.seek = carla_seek; + stream.tell = carla_tell; + } + + static v3_result V3_API carla_query_interface(void* const self, const v3_tuid iid, void** const iface) + { + carla_debug("%s %s %p", __PRETTY_FUNCTION__, tuid2str(iid), iface); + + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_bstream_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = nullptr; + carla_stdout("TODO carla_v3_bstream::query_interface %s", tuid2str(iid)); + return V3_NO_INTERFACE; + } + + static v3_result carla_read(void* const self, void* const buffer, int32_t num_bytes, int32_t* const bytes_read) + { + CARLA_SAFE_ASSERT_RETURN(buffer != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(num_bytes > 0, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(bytes_read != nullptr, V3_INVALID_ARG); + carla_v3_bstream* const stream = *static_cast(self); + + if (stream->pos + num_bytes > stream->size) + num_bytes = stream->size - stream->pos; + + std::memcpy(buffer, static_cast(stream->buffer) + stream->pos, num_bytes); + stream->pos += num_bytes; + *bytes_read = num_bytes; + + return V3_OK; + } + + static v3_result carla_write(void* const self, + void* const buffer, const int32_t num_bytes, int32_t* const bytes_read) + { + CARLA_SAFE_ASSERT_RETURN(buffer != nullptr, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(num_bytes > 0, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(bytes_read != nullptr, V3_INVALID_ARG); + carla_v3_bstream* const stream = *static_cast(self); + + const int64_t size = stream->size + num_bytes; + void* const oldbuffer = stream->buffer; + stream->buffer = std::realloc(oldbuffer, size); // FIXME detect alloc failure + CARLA_SAFE_ASSERT_RETURN(stream->buffer != oldbuffer, V3_NOMEM); + + stream->size += num_bytes; + *bytes_read = num_bytes; + return V3_OK; + } + + static v3_result carla_seek(void* const self, const int64_t pos, const int32_t seek_mode, int64_t* const result) + { + CARLA_SAFE_ASSERT_RETURN(result != nullptr, V3_INVALID_ARG); + carla_v3_bstream* const stream = *static_cast(self); + + switch (seek_mode) + { + case V3_SEEK_SET: + CARLA_SAFE_ASSERT_INT2_RETURN(pos <= stream->size, pos, stream->size, V3_INVALID_ARG); + *result = stream->pos = pos; + return V3_OK; + case V3_SEEK_CUR: + CARLA_SAFE_ASSERT_INT2_RETURN(stream->pos + pos <= stream->size, pos, stream->size, V3_INVALID_ARG); + *result = stream->pos = stream->pos + pos; + return V3_OK; + case V3_SEEK_END: + CARLA_SAFE_ASSERT_INT2_RETURN(pos <= stream->size, pos, stream->size, V3_INVALID_ARG); + *result = stream->pos = stream->size - pos; + return V3_OK; + } + + return V3_INVALID_ARG; + } + + static v3_result carla_tell(void* const self, int64_t* const pos) + { + CARLA_SAFE_ASSERT_RETURN(pos != nullptr, V3_INVALID_ARG); + carla_v3_bstream* const stream = *static_cast(self); + + *pos = stream->pos; + return V3_OK; + } + + CARLA_DECLARE_NON_COPYABLE(carla_v3_bstream) +}; + +// -------------------------------------------------------------------------------------------------------------------- + struct carla_v3_host_application : v3_host_application_cpp { carla_v3_host_application() { @@ -779,13 +1157,13 @@ private: // -------------------------------------------------------------------------------------------------------------------- struct carla_v3_plugin_frame : v3_plugin_frame_cpp { + CarlaPluginUI::Callback* const callback; carla_v3_run_loop loop; carla_v3_run_loop* loopPtr; - CarlaPluginUI::Callback* uiCallback; - carla_v3_plugin_frame() - : loopPtr(&loop), - uiCallback(nullptr) + carla_v3_plugin_frame(CarlaPluginUI::Callback* const cb) + : callback(cb), + loopPtr(&loop) { query_interface = carla_query_interface; ref = v3_ref_static; @@ -823,8 +1201,7 @@ private: { carla_debug("TODO %s", __PRETTY_FUNCTION__); const carla_v3_plugin_frame* const me = *static_cast(self); - CARLA_SAFE_ASSERT_RETURN(me->uiCallback != nullptr, V3_INVALID_ARG); - me->uiCallback->handlePluginUIResized(rect->right - rect->left, rect->bottom - rect->top); + me->callback->handlePluginUIResized(rect->right - rect->left, rect->bottom - rect->top); return V3_OK; } @@ -848,6 +1225,8 @@ public: fV3ApplicationPtr(&fV3Application), fComponentHandler(), fComponentHandlerPtr(&fComponentHandler), + fPluginFrame(this), + fPluginFramePtr(&fPluginFrame), fV3ClassInfo(), fV3(), fEvents(), @@ -871,7 +1250,6 @@ public: if (fUI.isAttached) { fUI.isAttached = false; - fUI.frame.uiCallback = nullptr; v3_cpp_obj(fV3.view)->set_frame(fV3.view, nullptr); v3_cpp_obj(fV3.view)->removed(fV3.view); } @@ -1216,9 +1594,7 @@ public: // TODO inform plugin of what UI scale we use #endif - fUI.frame.uiCallback = this; - - v3_cpp_obj(fV3.view)->set_frame(fV3.view, (v3_plugin_frame**)&fUI.framePtr); + v3_cpp_obj(fV3.view)->set_frame(fV3.view, (v3_plugin_frame**)&fPluginFramePtr); if (v3_cpp_obj(fV3.view)->attached(fV3.view, fUI.window->getPtr(), V3_VIEW_PLATFORM_TYPE_NATIVE) == V3_OK) @@ -1243,7 +1619,6 @@ public: } else { - fUI.frame.uiCallback = nullptr; v3_cpp_obj(fV3.view)->set_frame(fV3.view, nullptr); delete fUI.window; @@ -1276,9 +1651,7 @@ public: CARLA_SAFE_ASSERT_RETURN(fUI.window == nullptr, nullptr); CARLA_SAFE_ASSERT_RETURN(fV3.view != nullptr, nullptr); - fUI.frame.uiCallback = this; - - v3_cpp_obj(fV3.view)->set_frame(fV3.view, (v3_plugin_frame**)&fUI.framePtr); + v3_cpp_obj(fV3.view)->set_frame(fV3.view, (v3_plugin_frame**)&fPluginFramePtr); #ifndef CARLA_OS_MAC const EngineOptions& opts(pData->engine->getOptions()); @@ -1317,7 +1690,6 @@ public: else { fUI.isVisible = false; - fUI.frame.uiCallback = nullptr; v3_cpp_obj(fV3.view)->set_frame(fV3.view, nullptr); carla_stderr2("Plugin refused to open its own UI"); @@ -1345,7 +1717,7 @@ public: void uiIdle() override { #ifdef _POSIX_VERSION - LinkedList& posixfds(fUI.frame.loop.posixfds); + LinkedList& posixfds(fPluginFrame.loop.posixfds); if (posixfds.isNotEmpty()) { @@ -1388,7 +1760,7 @@ public: } #endif - LinkedList& timers(fUI.frame.loop.timers); + LinkedList& timers(fPluginFrame.loop.timers); if (timers.isNotEmpty()) { @@ -2019,7 +2391,7 @@ public: #endif bool isSampleAccurate = (pData->options & PLUGIN_OPTION_FIXED_BUFFERS) == 0; - uint32_t startTime = 0; + uint32_t startTime = 0; uint32_t timeOffset = 0; #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH @@ -2838,10 +3210,13 @@ private: v3_process_context fV3TimeContext; carla_v3_host_application fV3Application; - carla_v3_host_application* fV3ApplicationPtr; + carla_v3_host_application* const fV3ApplicationPtr; carla_v3_component_handler fComponentHandler; - carla_v3_component_handler* fComponentHandlerPtr; + carla_v3_component_handler* const fComponentHandlerPtr; + + carla_v3_plugin_frame fPluginFrame; + carla_v3_plugin_frame* const fPluginFramePtr; // v3_class_info_2 is ABI compatible with v3_class_info union ClassInfo { @@ -3143,15 +3518,12 @@ private: bool isAttached; bool isEmbed; bool isVisible; - carla_v3_plugin_frame frame; - carla_v3_plugin_frame* framePtr; CarlaPluginUI* window; UI() noexcept : isAttached(false), isEmbed(false), isVisible(false), - framePtr(&frame), window(nullptr) {} ~UI() diff --git a/source/includes/travesty/bstream.h b/source/includes/travesty/bstream.h index 92bbc3b4e..8c625e4e8 100644 --- a/source/includes/travesty/bstream.h +++ b/source/includes/travesty/bstream.h @@ -36,3 +36,15 @@ struct v3_bstream { static constexpr const v3_tuid v3_bstream_iid = V3_ID(0xC3BF6EA2, 0x30994752, 0x9B6BF990, 0x1EE33E9B); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_bstream_cpp : v3_funknown { + v3_bstream stream; +}; + +#endif