@@ -7,7 +7,14 @@ export WINEDEBUG=-all | |||||
# export PYTHONMALLOC=malloc | # export PYTHONMALLOC=malloc | ||||
# export SKIP_STRIPPING=true | # 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=$! | PID=$! | ||||
while true; do | while true; do | ||||
@@ -18,7 +18,16 @@ | |||||
obj:/lib/x86_64-linux-gnu/ld-2.27.so | 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 | Memcheck:Leak | ||||
match-leak-kinds: reachable | match-leak-kinds: reachable | ||||
fun:calloc | fun:calloc | ||||
@@ -26,6 +35,15 @@ | |||||
fun:dlopen@@GLIBC_2.2.5 | fun:dlopen@@GLIBC_2.2.5 | ||||
... | ... | ||||
} | } | ||||
{ | |||||
dlclose-2 | |||||
Memcheck:Leak | |||||
match-leak-kinds: reachable | |||||
... | |||||
fun:_dl_open | |||||
... | |||||
} | |||||
# XInitThreads | # XInitThreads | ||||
{ | { | ||||
@@ -385,9 +385,6 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||||
#ifdef CARLA_OS_WIN | #ifdef CARLA_OS_WIN | ||||
carla_setenv("WINEASIO_CLIENT_NAME", clientName); | carla_setenv("WINEASIO_CLIENT_NAME", clientName); | ||||
#endif | #endif | ||||
#ifdef CARLA_OS_UNIX | |||||
sThreadSafeFFTW.init(); | |||||
#endif | |||||
ScopedPointer<CarlaEngine> engine(CarlaEngine::newDriverByName(driverName)); | ScopedPointer<CarlaEngine> engine(CarlaEngine::newDriverByName(driverName)); | ||||
@@ -411,6 +408,9 @@ bool carla_engine_init(const char* driverName, const char* clientName) | |||||
#ifndef BUILD_BRIDGE | #ifndef BUILD_BRIDGE | ||||
if (gStandalone.logThreadEnabled && std::getenv("CARLA_LOGS_DISABLED") == nullptr) | if (gStandalone.logThreadEnabled && std::getenv("CARLA_LOGS_DISABLED") == nullptr) | ||||
gStandalone.logThread.init(); | gStandalone.logThread.init(); | ||||
#endif | |||||
#ifdef CARLA_OS_UNIX | |||||
sThreadSafeFFTW.init(); | |||||
#endif | #endif | ||||
gStandalone.lastError = "No error"; | gStandalone.lastError = "No error"; | ||||
gStandalone.engine = engine.release(); | gStandalone.engine = engine.release(); | ||||
@@ -714,10 +714,10 @@ CarlaPlugin::ProtectedData::~ProtectedData() noexcept | |||||
masterMutex.unlock(); | masterMutex.unlock(); | ||||
singleMutex.unlock(); | singleMutex.unlock(); | ||||
CARLA_SAFE_ASSERT(uiLib == nullptr); | |||||
if (lib != nullptr) | if (lib != nullptr) | ||||
libClose(); | libClose(); | ||||
CARLA_SAFE_ASSERT(uiLib == nullptr); | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -16,7 +16,7 @@ | |||||
*/ | */ | ||||
// testing macros | // testing macros | ||||
#define LV2_UIS_ONLY_BRIDGES | |||||
// #define LV2_UIS_ONLY_BRIDGES | |||||
// #define LV2_UIS_ONLY_INPROCESS | // #define LV2_UIS_ONLY_INPROCESS | ||||
#include "CarlaPluginInternal.hpp" | #include "CarlaPluginInternal.hpp" | ||||
@@ -6276,7 +6276,7 @@ private: | |||||
static void carla_lv2_inline_display_queue_draw(LV2_Inline_Display_Handle handle) | static void carla_lv2_inline_display_queue_draw(LV2_Inline_Display_Handle handle) | ||||
{ | { | ||||
CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); | 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(); | ((CarlaPluginLV2*)handle)->handleInlineDisplayQueueRedraw(); | ||||
} | } | ||||
@@ -88,10 +88,16 @@ SHARED += -Wl,-exported_symbol,_lv2_descriptor | |||||
SHARED += -Wl,-exported_symbol,_lv2ui_descriptor | SHARED += -Wl,-exported_symbol,_lv2ui_descriptor | ||||
endif | endif | ||||
# ---------------------------------------------------------------------------------------------------------------------------- | |||||
# ... | |||||
LIBS_ui = $(MODULEDIR)/water.a | |||||
# ---------------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------------- | ||||
TARGETS = \ | TARGETS = \ | ||||
$(BINDIR)/carla.lv2/carla$(LIB_EXT) \ | $(BINDIR)/carla.lv2/carla$(LIB_EXT) \ | ||||
$(BINDIR)/carla.lv2/carla_ui$(LIB_EXT) \ | |||||
$(BINDIR)/carla.lv2/manifest.ttl | $(BINDIR)/carla.lv2/manifest.ttl | ||||
ifeq ($(LINUX),true) | 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)" | @echo "Linking carla.lv2/carla$(LIB_EXT)" | ||||
@$(CXX) $< $(LIBS_START) $(LIBS) $(LIBS_END) $(SHARED) $(LINK_FLAGS) -o $@ | @$(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) | $(BINDIR)/CarlaRack$(LIB_EXT): $(OBJDIR)/carla-vst.cpp.rack-syn.o $(LIBS) | ||||
-@mkdir -p $(BINDIR) | -@mkdir -p $(BINDIR) | ||||
@echo "Linking CarlaRack$(LIB_EXT)" | @echo "Linking CarlaRack$(LIB_EXT)" | ||||
@@ -166,6 +177,11 @@ $(OBJDIR)/carla-lv2.cpp.o: carla-lv2.cpp | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | @$(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 | $(OBJDIR)/carla-vst.cpp.rack-fx.o: carla-vst.cpp | ||||
-@mkdir -p $(OBJDIR) | -@mkdir -p $(OBJDIR) | ||||
@echo "Compiling $< (RackFX)" | @echo "Compiling $< (RackFX)" | ||||
@@ -0,0 +1,515 @@ | |||||
/* | |||||
* Carla Native Plugins | |||||
* Copyright (C) 2013-2018 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* 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" | |||||
// ----------------------------------------------------------------------- |
@@ -41,7 +41,8 @@ public: | |||||
#ifdef CARLA_PROPER_CPP11_SUPPORT | #ifdef CARLA_PROPER_CPP11_SUPPORT | ||||
fProgramDesc({0, 0, nullptr}), | fProgramDesc({0, 0, nullptr}), | ||||
#endif | #endif | ||||
fMidiEventCount(0) | |||||
fMidiEventCount(0), | |||||
fWorkerUISignal(0) | |||||
{ | { | ||||
carla_zeroStruct(fHost); | carla_zeroStruct(fHost); | ||||
@@ -107,8 +108,8 @@ public: | |||||
fPorts.usesTime = fDescriptor->hints & NATIVE_PLUGIN_USES_TIME; | fPorts.usesTime = fDescriptor->hints & NATIVE_PLUGIN_USES_TIME; | ||||
fPorts.numAudioIns = fDescriptor->audioIns; | fPorts.numAudioIns = fDescriptor->audioIns; | ||||
fPorts.numAudioOuts = fDescriptor->audioOuts; | 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 && | if (fDescriptor->get_parameter_count != nullptr && | ||||
fDescriptor->get_parameter_info != nullptr && | fDescriptor->get_parameter_info != nullptr && | ||||
@@ -198,6 +199,17 @@ public: | |||||
{ | { | ||||
if (event == nullptr) | if (event == nullptr) | ||||
continue; | 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) | if (event->body.type != fURIs.midiEvent) | ||||
continue; | continue; | ||||
if (event->body.size > 4) | if (event->body.size > 4) | ||||
@@ -230,6 +242,31 @@ public: | |||||
const_cast<float**>(fPorts.audioIns), fPorts.audioOuts, frames, | const_cast<float**>(fPorts.audioIns), fPorts.audioOuts, frames, | ||||
fMidiEvents, fMidiEventCount); | 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<uint32_t>(sizeof(LV2_Atom_Event) + msgSize)); | |||||
mData.offset += size; | |||||
seq->atom.size += size; | |||||
fWorkerUISignal = 0; | |||||
} | |||||
} | |||||
lv2_post_run(frames); | lv2_post_run(frames); | ||||
updateParameterOutputs(); | 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, | void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, | ||||
LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed) | 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 | 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 | void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const | ||||
@@ -558,13 +634,17 @@ protected: | |||||
void handleUiClosed() | void handleUiClosed() | ||||
{ | { | ||||
fUI.isVisible = false; | |||||
if (fWorkerUISignal) | |||||
fWorkerUISignal = -1; | |||||
if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr) | if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr) | ||||
fUI.host->ui_closed(fUI.controller); | fUI.host->ui_closed(fUI.controller); | ||||
fUI.host = nullptr; | fUI.host = nullptr; | ||||
fUI.writeFunction = nullptr; | fUI.writeFunction = nullptr; | ||||
fUI.controller = nullptr; | fUI.controller = nullptr; | ||||
fUI.isVisible = false; | |||||
} | } | ||||
const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const | const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const | ||||
@@ -639,6 +719,8 @@ private: | |||||
uint32_t fMidiEventCount; | uint32_t fMidiEventCount; | ||||
NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | ||||
int fWorkerUISignal; | |||||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
#define handlePtr ((NativePlugin*)handle) | #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); | 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) | static const void* lv2_extension_data(const char* uri) | ||||
{ | { | ||||
carla_debug("lv2_extension_data(\"%s\")", 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_Options_Interface options = { lv2_get_options, lv2_set_options }; | ||||
static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; | 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_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) | if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) | ||||
return &options; | return &options; | ||||
@@ -837,6 +932,8 @@ static const void* lv2_extension_data(const char* uri) | |||||
return &programs; | return &programs; | ||||
if (std::strcmp(uri, LV2_STATE__interface) == 0) | if (std::strcmp(uri, LV2_STATE__interface) == 0) | ||||
return &state; | return &state; | ||||
if (std::strcmp(uri, LV2_WORKER__interface) == 0) | |||||
return &worker; | |||||
return nullptr; | 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) | 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); | uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); | ||||
} | } | ||||
@@ -554,6 +554,7 @@ public: | |||||
fBufferSize(0), | fBufferSize(0), | ||||
fSampleRate(sampleRate), | fSampleRate(sampleRate), | ||||
fUridMap(nullptr), | fUridMap(nullptr), | ||||
fWorker(nullptr), | |||||
fTimeInfo(), | fTimeInfo(), | ||||
fLastPositionData(), | fLastPositionData(), | ||||
fURIs(), | fURIs(), | ||||
@@ -572,20 +573,23 @@ public: | |||||
const LV2_Options_Option* options = nullptr; | const LV2_Options_Option* options = nullptr; | ||||
const LV2_URID_Map* uridMap = nullptr; | const LV2_URID_Map* uridMap = nullptr; | ||||
const LV2_URID_Unmap* uridUnmap = nullptr; | const LV2_URID_Unmap* uridUnmap = nullptr; | ||||
const LV2_Worker_Schedule* worker = nullptr; | |||||
for (int i=0; features[i] != nullptr; ++i) | 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; | options = (const LV2_Options_Option*)features[i]->data; | ||||
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | ||||
uridMap = (const LV2_URID_Map*)features[i]->data; | uridMap = (const LV2_URID_Map*)features[i]->data; | ||||
else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0) | else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0) | ||||
uridUnmap = (const LV2_URID_Unmap*)features[i]->data; | 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; | return; | ||||
} | } | ||||
@@ -640,6 +644,8 @@ public: | |||||
fUridMap = uridMap; | fUridMap = uridMap; | ||||
fURIs.map(uridMap); | fURIs.map(uridMap); | ||||
fWorker = worker; | |||||
carla_zeroStruct(fTimeInfo); | carla_zeroStruct(fTimeInfo); | ||||
carla_zeroStruct(fLastPositionData); | carla_zeroStruct(fLastPositionData); | ||||
} | } | ||||
@@ -1140,6 +1146,7 @@ protected: | |||||
// LV2 host features | // LV2 host features | ||||
const LV2_URID_Map* fUridMap; | const LV2_URID_Map* fUridMap; | ||||
const LV2_Worker_Schedule* fWorker; | |||||
// Time info stuff | // Time info stuff | ||||
TimeInfoStruct fTimeInfo; | TimeInfoStruct fTimeInfo; | ||||
@@ -1433,6 +1440,7 @@ protected: | |||||
LV2_URID timeFrame; | LV2_URID timeFrame; | ||||
LV2_URID timeSpeed; | LV2_URID timeSpeed; | ||||
LV2_URID timeTicksPerBeat; | LV2_URID timeTicksPerBeat; | ||||
LV2_URID uiEvents; | |||||
URIDs() | URIDs() | ||||
: atomBlank(0), | : atomBlank(0), | ||||
@@ -1452,7 +1460,8 @@ protected: | |||||
timeBeatUnit(0), | timeBeatUnit(0), | ||||
timeFrame(0), | timeFrame(0), | ||||
timeSpeed(0), | timeSpeed(0), | ||||
timeTicksPerBeat(0) {} | |||||
timeTicksPerBeat(0), | |||||
uiEvents(0) {} | |||||
void map(const LV2_URID_Map* const uridMap) | void map(const LV2_URID_Map* const uridMap) | ||||
{ | { | ||||
@@ -1474,6 +1483,7 @@ protected: | |||||
timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar); | timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar); | ||||
timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute); | timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute); | ||||
timeTicksPerBeat = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat); | timeTicksPerBeat = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat); | ||||
uiEvents = uridMap->map(uridMap->handle, "urn:carla:transmitEv"); | |||||
} | } | ||||
} fURIs; | } fURIs; | ||||