From 7d55cfe473f08599b8078b36f3923af65e198ea8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 23 Aug 2018 06:51:06 +0200 Subject: [PATCH] Start code for carla-plugin without lv2 instance-access --- data/valgrind.sh | 9 +- data/valgrind.supp | 20 +- source/backend/CarlaStandalone.cpp | 6 +- source/backend/plugin/CarlaPluginInternal.cpp | 4 +- source/backend/plugin/CarlaPluginLV2.cpp | 4 +- source/plugin/Makefile | 16 + source/plugin/carla-lv2-ui.cpp | 515 ++++++++++++++++++ source/plugin/carla-lv2.cpp | 111 +++- source/utils/CarlaLv2Utils.hpp | 18 +- 9 files changed, 683 insertions(+), 20 deletions(-) create mode 100644 source/plugin/carla-lv2-ui.cpp diff --git a/data/valgrind.sh b/data/valgrind.sh index 4a70d42ec..9f9d6a607 100755 --- a/data/valgrind.sh +++ b/data/valgrind.sh @@ -7,7 +7,14 @@ export WINEDEBUG=-all # export PYTHONMALLOC=malloc # export SKIP_STRIPPING=true -valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=./data/valgrind.supp -- ./bin/carla-bridge-native internal "" carlapatchbay & +valgrind \ + --tool=memcheck \ + --leak-check=full \ + --show-leak-kinds=all \ + --track-origins=yes \ + --gen-suppressions=all \ + --suppressions=./data/valgrind.supp \ + -- ./bin/carla-bridge-native internal "" carlapatchbay & PID=$! while true; do diff --git a/data/valgrind.supp b/data/valgrind.supp index cb1e9e9cc..a80bc6f1c 100644 --- a/data/valgrind.supp +++ b/data/valgrind.supp @@ -18,7 +18,16 @@ obj:/lib/x86_64-linux-gnu/ld-2.27.so } { - dlclose + _dl_init-3 + Memcheck:Leak + match-leak-kinds: definite + ... + fun:call_init + fun:_dl_init + obj:/lib/x86_64-linux-gnu/ld-2.27.so +} +{ + dlclose-1 Memcheck:Leak match-leak-kinds: reachable fun:calloc @@ -26,6 +35,15 @@ fun:dlopen@@GLIBC_2.2.5 ... } +{ + dlclose-2 + Memcheck:Leak + match-leak-kinds: reachable + ... + fun:_dl_open + ... +} + # XInitThreads { diff --git a/source/backend/CarlaStandalone.cpp b/source/backend/CarlaStandalone.cpp index a0aa20673..e8b69ad68 100644 --- a/source/backend/CarlaStandalone.cpp +++ b/source/backend/CarlaStandalone.cpp @@ -385,9 +385,6 @@ bool carla_engine_init(const char* driverName, const char* clientName) #ifdef CARLA_OS_WIN carla_setenv("WINEASIO_CLIENT_NAME", clientName); #endif -#ifdef CARLA_OS_UNIX - sThreadSafeFFTW.init(); -#endif ScopedPointer engine(CarlaEngine::newDriverByName(driverName)); @@ -411,6 +408,9 @@ bool carla_engine_init(const char* driverName, const char* clientName) #ifndef BUILD_BRIDGE if (gStandalone.logThreadEnabled && std::getenv("CARLA_LOGS_DISABLED") == nullptr) gStandalone.logThread.init(); +#endif +#ifdef CARLA_OS_UNIX + sThreadSafeFFTW.init(); #endif gStandalone.lastError = "No error"; gStandalone.engine = engine.release(); diff --git a/source/backend/plugin/CarlaPluginInternal.cpp b/source/backend/plugin/CarlaPluginInternal.cpp index 305b9700d..14e8d455c 100644 --- a/source/backend/plugin/CarlaPluginInternal.cpp +++ b/source/backend/plugin/CarlaPluginInternal.cpp @@ -714,10 +714,10 @@ CarlaPlugin::ProtectedData::~ProtectedData() noexcept masterMutex.unlock(); singleMutex.unlock(); + CARLA_SAFE_ASSERT(uiLib == nullptr); + if (lib != nullptr) libClose(); - - CARLA_SAFE_ASSERT(uiLib == nullptr); } // ----------------------------------------------------------------------- diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index 0f3afe0b2..1dcf785ed 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -16,7 +16,7 @@ */ // testing macros -#define LV2_UIS_ONLY_BRIDGES +// #define LV2_UIS_ONLY_BRIDGES // #define LV2_UIS_ONLY_INPROCESS #include "CarlaPluginInternal.hpp" @@ -6276,7 +6276,7 @@ private: static void carla_lv2_inline_display_queue_draw(LV2_Inline_Display_Handle handle) { CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); - carla_debug("carla_lv2_inline_display_queue_draw(%p)", handle); + // carla_debug("carla_lv2_inline_display_queue_draw(%p)", handle); ((CarlaPluginLV2*)handle)->handleInlineDisplayQueueRedraw(); } diff --git a/source/plugin/Makefile b/source/plugin/Makefile index 4060ff2e4..7cf4d6175 100644 --- a/source/plugin/Makefile +++ b/source/plugin/Makefile @@ -88,10 +88,16 @@ SHARED += -Wl,-exported_symbol,_lv2_descriptor SHARED += -Wl,-exported_symbol,_lv2ui_descriptor endif +# ---------------------------------------------------------------------------------------------------------------------------- +# ... + +LIBS_ui = $(MODULEDIR)/water.a + # ---------------------------------------------------------------------------------------------------------------------------- TARGETS = \ $(BINDIR)/carla.lv2/carla$(LIB_EXT) \ + $(BINDIR)/carla.lv2/carla_ui$(LIB_EXT) \ $(BINDIR)/carla.lv2/manifest.ttl ifeq ($(LINUX),true) @@ -129,6 +135,11 @@ $(BINDIR)/carla.lv2/carla$(LIB_EXT): $(OBJDIR)/carla-lv2.cpp.o $(LIBS) @echo "Linking carla.lv2/carla$(LIB_EXT)" @$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@ +$(BINDIR)/carla.lv2/carla_ui$(LIB_EXT): $(OBJDIR)/carla-lv2-ui.cpp.o $(LIBS_ui) + -@mkdir -p $(BINDIR)/carla.lv2 + @echo "Linking carla.lv2/carla-ui$(LIB_EXT)" + @$(CXX) $< $(LIBS_START) $(LIBS_ui) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@ + $(BINDIR)/CarlaRack$(LIB_EXT): $(OBJDIR)/carla-vst.cpp.rack-syn.o $(LIBS) -@mkdir -p $(BINDIR) @echo "Linking CarlaRack$(LIB_EXT)" @@ -166,6 +177,11 @@ $(OBJDIR)/carla-lv2.cpp.o: carla-lv2.cpp @echo "Compiling $<" @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ +$(OBJDIR)/carla-lv2-ui.cpp.o: carla-lv2-ui.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling $<" + @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + $(OBJDIR)/carla-vst.cpp.rack-fx.o: carla-vst.cpp -@mkdir -p $(OBJDIR) @echo "Compiling $< (RackFX)" diff --git a/source/plugin/carla-lv2-ui.cpp b/source/plugin/carla-lv2-ui.cpp new file mode 100644 index 000000000..0f13078de --- /dev/null +++ b/source/plugin/carla-lv2-ui.cpp @@ -0,0 +1,515 @@ +/* + * Carla Native Plugins + * Copyright (C) 2013-2018 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#include "CarlaLv2Utils.hpp" +#include "CarlaPipeUtils.hpp" + +// -------------------------------------------------------------------------------------------------------------------- + +class NativePluginUI : public LV2_External_UI_Widget_Compat +{ +public: + NativePluginUI(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, + LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed) + : fUridMap(nullptr), + fUridUnmap(nullptr), + fUridTranser(0), + fUridTranser2(0), + fUI() + { + run = extui_run; + show = extui_show; + hide = extui_hide; + + fUI.writeFunction = writeFunction; + fUI.controller = controller; + fUI.isEmbed = isEmbed; + + const LV2_URID_Map* uridMap = nullptr; + const LV2_URID_Unmap* uridUnmap = nullptr; + + for (int i=0; features[i] != nullptr; ++i) + { + /**/ if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) + uridMap = (const LV2_URID_Map*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0) + uridUnmap = (const LV2_URID_Unmap*)features[i]->data; + } + + if (uridMap == nullptr || uridUnmap == nullptr) + { + carla_stderr("Host doesn't provide urid-map feature"); + return; + } + + fUridMap = uridMap; + fUridUnmap = uridUnmap; + fUridTranser = uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer); + fUridTranser2 = uridMap->map(uridMap->handle, "urn:carla:transmitEv"); + +#ifdef CARLA_OS_LINUX + // --------------------------------------------------------------- + // show embed UI if needed + + if (isEmbed) + { + intptr_t parentId = 0; + const LV2UI_Resize* uiResize = nullptr; + + for (int i=0; features[i] != nullptr; ++i) + { + if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) + { + parentId = (intptr_t)features[i]->data; + } + else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) + { + uiResize = (const LV2UI_Resize*)features[i]->data; + } + } + + // ----------------------------------------------------------- + // see if the host can really embed the UI + + if (parentId != 0) + { + if (uiResize && uiResize->ui_resize != nullptr) + uiResize->ui_resize(uiResize->handle, 740, 512); + + fUI.name = carla_strdup("Carla"); + fUI.isVisible = true; + + char strBuf[0xff+1]; + strBuf[0xff] = '\0'; + std::snprintf(strBuf, 0xff, P_INTPTR, parentId); + + carla_setenv("CARLA_PLUGIN_EMBED_WINID", strBuf); + writeAtomMessage("show"); + + // FIXME + *widget = nullptr; + return; + } + } +#endif + + // --------------------------------------------------------------- + // see if the host supports external-ui + + for (int i=0; features[i] != nullptr; ++i) + { + if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 || + std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0) + { + fUI.host = (const LV2_External_UI_Host*)features[i]->data; + break; + } + } + + if (fUI.host != nullptr) + { + fUI.name = carla_strdup(fUI.host->plugin_human_id); + *widget = (LV2_External_UI_Widget_Compat*)this; + return; + } + + // --------------------------------------------------------------- + // no external-ui support, use showInterface + + for (int i=0; features[i] != nullptr; ++i) + { + if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) != 0) + continue; + + const LV2_Options_Option* const options((const LV2_Options_Option*)features[i]->data); + CARLA_SAFE_ASSERT_BREAK(options != nullptr); + + for (int j=0; options[j].key != 0; ++j) + { + if (options[j].key != uridMap->map(uridMap->handle, LV2_UI__windowTitle)) + continue; + + const char* const title((const char*)options[j].value); + CARLA_SAFE_ASSERT_BREAK(title != nullptr && title[0] != '\0'); + + fUI.name = carla_strdup(title); + break; + } + break; + } + + if (fUI.name == nullptr) + fUI.name = carla_strdup("Carla"); + + *widget = nullptr; + } + + ~NativePluginUI() + { + if (fUI.isVisible) + writeAtomMessage("quit"); + + fUI.host = nullptr; + fUI.writeFunction = nullptr; + fUI.controller = nullptr; + } + + // ---------------------------------------------------------------------------------------------------------------- + + void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) + { + CARLA_SAFE_ASSERT_RETURN(buffer != nullptr,); + + if (format == 0) + { + char msg[128]; + const float* const valuePtr = (const float*)buffer; + + { + const ScopedLocale csl; + std::snprintf(msg, 127, "control %u %f", portIndex, *valuePtr); + } + + msg[127] = '\0'; + + writeAtomMessage(msg); + return; + } + + if (format == fUridTranser) + { + CARLA_SAFE_ASSERT_RETURN(bufferSize > sizeof(LV2_Atom),); + + const LV2_Atom* const atom = (const LV2_Atom*)buffer; + + if (atom->type == fUridTranser2) + { + const char* const msg = (const char*)(atom + 1); + + if (std::strcmp(msg, "quit") == 0) + { + handleUiClosed(); + } + + return; + } + } + + carla_stdout("lv2ui_port_event %u %u %u:%s %p", portIndex, bufferSize, format, fUridUnmap->unmap(fUridUnmap->handle, format), buffer); + } + + // ---------------------------------------------------------------------------------------------------------------- + + void lv2ui_select_program(uint32_t bank, uint32_t program) const + { + char msg[128]; + std::snprintf(msg, 127, "program %u %u", bank, program); + msg[127] = '\0'; + + writeAtomMessage(msg); + } + + // ---------------------------------------------------------------------------------------------------------------- + + int lv2ui_idle() const + { + if (! fUI.isVisible) + return 1; + + handleUiRun(); + return 0; + } + + int lv2ui_show() + { + handleUiShow(); + return 0; + } + + int lv2ui_hide() + { + handleUiHide(); + return 0; + } + + // ---------------------------------------------------------------------------------------------------------------- + +protected: + void handleUiShow() + { + writeAtomMessage("show"); + fUI.isVisible = true; + } + + void handleUiHide() + { + if (fUI.isVisible) + { + fUI.isVisible = false; + writeAtomMessage("hide"); + } + } + + void handleUiRun() const + { + if (fUI.isVisible) + writeAtomMessage("idle"); + } + + void handleUiClosed() + { + fUI.isVisible = false; + + if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr) + fUI.host->ui_closed(fUI.controller); + + fUI.host = nullptr; + fUI.writeFunction = nullptr; + fUI.controller = nullptr; + } + + bool writeAtomMessage(const char* const msg) const + { + CARLA_SAFE_ASSERT_RETURN(fUI.writeFunction != nullptr, false); + CARLA_SAFE_ASSERT_RETURN(fUridTranser2 != 0, false); + carla_debug("writeAtomMessage(%s)", msg); + + const size_t msgSize = std::strlen(msg)+1; + const size_t atomSize = sizeof(LV2_Atom) + msgSize; + + if (atomSize <= 128) + { + char atomBuf[atomSize]; + carla_zeroChars(atomBuf, atomSize); + + LV2_Atom* const atom = (LV2_Atom*)atomBuf; + atom->size = msgSize; + atom->type = fUridTranser2; + std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize); + + fUI.writeFunction(fUI.controller, 0, atomSize, fUridTranser, atomBuf); + } + else + { + char* const atomBuf = new char[atomSize]; + carla_zeroChars(atomBuf, atomSize); + + LV2_Atom* const atom = (LV2_Atom*)atomBuf; + atom->size = msgSize; + atom->type = fUridTranser2; + std::memcpy(atomBuf+sizeof(LV2_Atom), msg, msgSize); + + fUI.writeFunction(fUI.controller, 0, atomSize, fUridTranser, atomBuf); + + delete[] atomBuf; + } + + return true; + } + + // ---------------------------------------------------------------------------------------------------------------- + +private: + const LV2_URID_Map* fUridMap; + const LV2_URID_Unmap* fUridUnmap; + LV2_URID fUridTranser, fUridTranser2; + + struct UI { + const LV2_External_UI_Host* host; + LV2UI_Write_Function writeFunction; + LV2UI_Controller controller; + const char* name; + bool isEmbed; + bool isVisible; + + UI() + : host(nullptr), + writeFunction(nullptr), + controller(nullptr), + name(nullptr), + isEmbed(false), + isVisible(false) {} + + ~UI() + { + if (name != nullptr) + { + delete[] name; + name = nullptr; + } + } + } fUI; + + // ---------------------------------------------------------------------------------------------------------------- + + #define handlePtr ((NativePluginUI*)handle) + + static void extui_run(LV2_External_UI_Widget_Compat* handle) + { + handlePtr->handleUiRun(); + } + + static void extui_show(LV2_External_UI_Widget_Compat* handle) + { + carla_debug("extui_show(%p)", handle); + handlePtr->handleUiShow(); + } + + static void extui_hide(LV2_External_UI_Widget_Compat* handle) + { + carla_debug("extui_hide(%p)", handle); + handlePtr->handleUiHide(); + } + + #undef handlePtr + + // ------------------------------------------------------------------- + + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePluginUI) +}; + +// ----------------------------------------------------------------------- +// LV2 UI descriptor functions + +static LV2UI_Handle lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, + LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed) +{ + carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features); +#ifndef CARLA_OS_LINUX + CARLA_SAFE_ASSERT_RETURN(! isEmbed, nullptr); +#endif + carla_debug("writeAtomMessage=========================================================================="); + + NativePluginUI* const ui = new NativePluginUI(writeFunction, controller, widget, features, isEmbed); + + // TODO: check ok + + return (LV2UI_Handle)ui; +} + +#ifdef CARLA_OS_LINUX +static LV2UI_Handle lv2ui_instantiate_embed(const LV2UI_Descriptor*, const char*, const char*, + LV2UI_Write_Function writeFunction, LV2UI_Controller controller, + LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return lv2ui_instantiate(writeFunction, controller, widget, features, true); +} +#endif + +static LV2UI_Handle lv2ui_instantiate_external(const LV2UI_Descriptor*, const char*, const char*, + LV2UI_Write_Function writeFunction, LV2UI_Controller controller, + LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return lv2ui_instantiate(writeFunction, controller, widget, features, false); +} + +#define uiPtr ((NativePluginUI*)ui) + +static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) +{ + carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer); + uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); +} + +static void lv2ui_cleanup(LV2UI_Handle ui) +{ + carla_debug("lv2ui_cleanup(%p)", ui); + delete uiPtr; +} + +static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) +{ + carla_debug("lv2ui_select_program(%p, %i, %i)", ui, bank, program); + uiPtr->lv2ui_select_program(bank, program); +} + +static int lv2ui_idle(LV2UI_Handle ui) +{ + return uiPtr->lv2ui_idle(); +} + +static int lv2ui_show(LV2UI_Handle ui) +{ + carla_debug("lv2ui_show(%p)", ui); + return uiPtr->lv2ui_show(); +} + +static int lv2ui_hide(LV2UI_Handle ui) +{ + carla_debug("lv2ui_hide(%p)", ui); + return uiPtr->lv2ui_hide(); +} + +static const void* lv2ui_extension_data(const char* uri) +{ + carla_stdout("lv2ui_extension_data(\"%s\")", uri); + + static const LV2UI_Idle_Interface uiidle = { lv2ui_idle }; + static const LV2UI_Show_Interface uishow = { lv2ui_show, lv2ui_hide }; + static const LV2_Programs_UI_Interface uiprograms = { lv2ui_select_program }; + + if (std::strcmp(uri, LV2_UI__idleInterface) == 0) + return &uiidle; + if (std::strcmp(uri, LV2_UI__showInterface) == 0) + return &uishow; + if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0) + return &uiprograms; + + return nullptr; +} + +#undef uiPtr + +// ----------------------------------------------------------------------- +// Startup code + +CARLA_EXPORT +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) +{ + carla_debug("lv2ui_descriptor(%i)", index); + +#ifdef CARLA_OS_LINUX + static const LV2UI_Descriptor lv2UiEmbedDesc = { + /* URI */ "http://kxstudio.sf.net/carla/ui-bridge-embed", + /* instantiate */ lv2ui_instantiate_embed, + /* cleanup */ lv2ui_cleanup, + /* port_event */ lv2ui_port_event, + /* extension_data */ lv2ui_extension_data + }; + + if (index == 0) + return &lv2UiEmbedDesc; + else + --index; +#endif + + static const LV2UI_Descriptor lv2UiExtDesc = { + /* URI */ "http://kxstudio.sf.net/carla/ui-bridge-ext", + /* instantiate */ lv2ui_instantiate_external, + /* cleanup */ lv2ui_cleanup, + /* port_event */ lv2ui_port_event, + /* extension_data */ lv2ui_extension_data + }; + + return (index == 0) ? &lv2UiExtDesc : nullptr; +} + +// ----------------------------------------------------------------------- + +#include "CarlaPipeUtils.cpp" + +// ----------------------------------------------------------------------- diff --git a/source/plugin/carla-lv2.cpp b/source/plugin/carla-lv2.cpp index a9c2f904a..f85571310 100644 --- a/source/plugin/carla-lv2.cpp +++ b/source/plugin/carla-lv2.cpp @@ -41,7 +41,8 @@ public: #ifdef CARLA_PROPER_CPP11_SUPPORT fProgramDesc({0, 0, nullptr}), #endif - fMidiEventCount(0) + fMidiEventCount(0), + fWorkerUISignal(0) { carla_zeroStruct(fHost); @@ -107,8 +108,8 @@ public: fPorts.usesTime = fDescriptor->hints & NATIVE_PLUGIN_USES_TIME; fPorts.numAudioIns = fDescriptor->audioIns; fPorts.numAudioOuts = fDescriptor->audioOuts; - fPorts.numMidiIns = fDescriptor->midiIns; - fPorts.numMidiOuts = fDescriptor->midiOuts; + fPorts.numMidiIns = std::max(fDescriptor->midiIns, 1U); + fPorts.numMidiOuts = std::max(fDescriptor->midiOuts, 1U); if (fDescriptor->get_parameter_count != nullptr && fDescriptor->get_parameter_info != nullptr && @@ -198,6 +199,17 @@ public: { if (event == nullptr) continue; + + if (event->body.type == fURIs.uiEvents && fWorkerUISignal != -1) + { + fWorkerUISignal = 1; + const char* const msg((const char*)(event + 1)); + const size_t msgSize = std::strlen(msg); + //std::puts(msg); + fWorker->schedule_work(fWorker->handle, msgSize+1, msg); + continue; + } + if (event->body.type != fURIs.midiEvent) continue; if (event->body.size > 4) @@ -230,6 +242,31 @@ public: const_cast(fPorts.audioIns), fPorts.audioOuts, frames, fMidiEvents, fMidiEventCount); + if (fWorkerUISignal == -1 && fPorts.numMidiOuts > 0) + { + const char* const msg = "quit"; + const size_t msgSize = 5; + + LV2_Atom_Sequence* const seq(fPorts.midiOuts[0]); + Ports::MidiOutData& mData(fPorts.midiOutData[0]); + + if (sizeof(LV2_Atom_Event) + msgSize <= mData.capacity - mData.offset) + { + LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq) + mData.offset); + + aev->time.frames = 0; + aev->body.size = msgSize; + aev->body.type = fURIs.uiEvents; + std::memcpy(LV2_ATOM_BODY(&aev->body), msg, msgSize); + + const uint32_t size = lv2_atom_pad_size(static_cast(sizeof(LV2_Atom_Event) + msgSize)); + mData.offset += size; + seq->atom.size += size; + + fWorkerUISignal = 0; + } + } + lv2_post_run(frames); updateParameterOutputs(); } @@ -321,6 +358,42 @@ public: // ---------------------------------------------------------------------------------------------------------------- + LV2_Worker_Status lv2_work(LV2_Worker_Respond_Function, LV2_Worker_Respond_Handle, uint32_t, const void* data) + { + const char* const msg = (const char*)data; + + /**/ if (std::strcmp(msg, "show") == 0) + { + handleUiShow(); + } + else if (std::strcmp(msg, "hide") == 0) + { + handleUiHide(); + } + else if (std::strcmp(msg, "idle") == 0) + { + handleUiRun(); + } + else if (std::strcmp(msg, "quit") == 0) + { + handleUiRun(); + } + else + { + carla_stdout("lv2_work unknown msg '%s'", msg); + return LV2_WORKER_ERR_UNKNOWN; + } + + return LV2_WORKER_SUCCESS; + } + + LV2_Worker_Status lv2_work_resp(uint32_t /*size*/, const void* /*body*/) + { + return LV2_WORKER_SUCCESS; + } + + // ---------------------------------------------------------------------------------------------------------------- + void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed) { @@ -547,8 +620,11 @@ protected: void handleUiParameterChanged(const uint32_t index, const float value) const { - if (fUI.writeFunction != nullptr && fUI.controller != nullptr) - fUI.writeFunction(fUI.controller, index+fPorts.indexOffset, sizeof(float), 0, &value); + if (fWorkerUISignal) + { + } + else if (fUI.writeFunction != nullptr && fUI.controller != nullptr) + fUI.writeFunction(fUI.controller, index+fPorts.indexOffset, sizeof(float), 0, &value); } void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const @@ -558,13 +634,17 @@ protected: void handleUiClosed() { + fUI.isVisible = false; + + if (fWorkerUISignal) + fWorkerUISignal = -1; + if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr) fUI.host->ui_closed(fUI.controller); fUI.host = nullptr; fUI.writeFunction = nullptr; fUI.controller = nullptr; - fUI.isVisible = false; } const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const @@ -639,6 +719,8 @@ private: uint32_t fMidiEventCount; NativeMidiEvent fMidiEvents[kMaxMidiEvents]; + int fWorkerUISignal; + // ------------------------------------------------------------------- #define handlePtr ((NativePlugin*)handle) @@ -823,6 +905,18 @@ static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Func return instancePtr->lv2_restore(retrieve, handle, flags, features); } +static LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle, uint32_t size, const void* data) +{ + carla_debug("work(%p, %p, %p, %u, %p)", instance, respond, handle, size, data); + return instancePtr->lv2_work(respond, handle, size, data); +} + +LV2_Worker_Status lv2_work_resp(LV2_Handle instance, uint32_t size, const void* body) +{ + carla_debug("work_resp(%p, %u, %p)", instance, size, body); + return instancePtr->lv2_work_resp(size, body); +} + static const void* lv2_extension_data(const char* uri) { carla_debug("lv2_extension_data(\"%s\")", uri); @@ -830,6 +924,7 @@ static const void* lv2_extension_data(const char* uri) static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; static const LV2_State_Interface state = { lv2_save, lv2_restore }; + static const LV2_Worker_Interface worker = { lv2_work, lv2_work_resp, nullptr }; if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) return &options; @@ -837,6 +932,8 @@ static const void* lv2_extension_data(const char* uri) return &programs; if (std::strcmp(uri, LV2_STATE__interface) == 0) return &state; + if (std::strcmp(uri, LV2_WORKER__interface) == 0) + return &worker; return nullptr; } @@ -896,7 +993,7 @@ static LV2UI_Handle lv2ui_instantiate_external(const LV2UI_Descriptor*, const ch static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) { - carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer); + carla_debug("lv2ui_port_eventxx(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer); uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); } diff --git a/source/utils/CarlaLv2Utils.hpp b/source/utils/CarlaLv2Utils.hpp index 64cc1069e..b6417d0b3 100644 --- a/source/utils/CarlaLv2Utils.hpp +++ b/source/utils/CarlaLv2Utils.hpp @@ -554,6 +554,7 @@ public: fBufferSize(0), fSampleRate(sampleRate), fUridMap(nullptr), + fWorker(nullptr), fTimeInfo(), fLastPositionData(), fURIs(), @@ -572,20 +573,23 @@ public: const LV2_Options_Option* options = nullptr; const LV2_URID_Map* uridMap = nullptr; const LV2_URID_Unmap* uridUnmap = nullptr; + const LV2_Worker_Schedule* worker = nullptr; for (int i=0; features[i] != nullptr; ++i) { - if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) + /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) options = (const LV2_Options_Option*)features[i]->data; else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) uridMap = (const LV2_URID_Map*)features[i]->data; else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0) uridUnmap = (const LV2_URID_Unmap*)features[i]->data; + else if (std::strcmp(features[i]->URI, LV2_WORKER__schedule) == 0) + worker = (const LV2_Worker_Schedule*)features[i]->data; } - if (options == nullptr || uridMap == nullptr) + if (options == nullptr || uridMap == nullptr || worker == nullptr) { - carla_stderr("Host doesn't provide option or urid-map features"); + carla_stderr("Host doesn't provide option, urid-map and worker features"); return; } @@ -640,6 +644,8 @@ public: fUridMap = uridMap; fURIs.map(uridMap); + fWorker = worker; + carla_zeroStruct(fTimeInfo); carla_zeroStruct(fLastPositionData); } @@ -1140,6 +1146,7 @@ protected: // LV2 host features const LV2_URID_Map* fUridMap; + const LV2_Worker_Schedule* fWorker; // Time info stuff TimeInfoStruct fTimeInfo; @@ -1433,6 +1440,7 @@ protected: LV2_URID timeFrame; LV2_URID timeSpeed; LV2_URID timeTicksPerBeat; + LV2_URID uiEvents; URIDs() : atomBlank(0), @@ -1452,7 +1460,8 @@ protected: timeBeatUnit(0), timeFrame(0), timeSpeed(0), - timeTicksPerBeat(0) {} + timeTicksPerBeat(0), + uiEvents(0) {} void map(const LV2_URID_Map* const uridMap) { @@ -1474,6 +1483,7 @@ protected: timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar); timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute); timeTicksPerBeat = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat); + uiEvents = uridMap->map(uridMap->handle, "urn:carla:transmitEv"); } } fURIs;