From 84c69c851c87f2c7b9de502159a2f2fd5dd4b375 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 24 Oct 2019 01:21:59 +0200 Subject: [PATCH] Add option to add-jack for using externally started applications --- resources/ui/carla_add_jack.ui | 13 +- source/backend/engine/CarlaEngineJuce.cpp | 4 +- source/backend/plugin/CarlaPluginJack.cpp | 165 +++++++++++++++------- source/frontend/carla_database.py | 10 +- source/includes/CarlaLibJackHints.h | 10 +- source/utils/CarlaString.hpp | 13 ++ 6 files changed, 150 insertions(+), 65 deletions(-) diff --git a/resources/ui/carla_add_jack.ui b/resources/ui/carla_add_jack.ui index efe73bfc2..489b5d990 100644 --- a/resources/ui/carla_add_jack.ui +++ b/resources/ui/carla_add_jack.ui @@ -6,8 +6,8 @@ 0 0 - 496 - 438 + 540 + 514 @@ -488,13 +488,20 @@ - + Use previous client output buffer as input for the next client + + + + Wait for external application start (Advanced) + + + diff --git a/source/backend/engine/CarlaEngineJuce.cpp b/source/backend/engine/CarlaEngineJuce.cpp index 17c35ec21..f94184297 100644 --- a/source/backend/engine/CarlaEngineJuce.cpp +++ b/source/backend/engine/CarlaEngineJuce.cpp @@ -25,7 +25,6 @@ #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcast-qual" -# pragma GCC diagnostic ignored "-Wclass-memaccess" # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wdouble-promotion" # pragma GCC diagnostic ignored "-Weffc++" @@ -33,6 +32,9 @@ # pragma GCC diagnostic ignored "-Wsign-conversion" # pragma GCC diagnostic ignored "-Wundef" # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +# if __GNUC__ > 7 +# pragma GCC diagnostic ignored "-Wclass-memaccess" +# endif #endif #include "AppConfig.h" diff --git a/source/backend/plugin/CarlaPluginJack.cpp b/source/backend/plugin/CarlaPluginJack.cpp index 6010a4788..461069c5d 100644 --- a/source/backend/plugin/CarlaPluginJack.cpp +++ b/source/backend/plugin/CarlaPluginJack.cpp @@ -126,6 +126,40 @@ public: } #endif + char* getEnvVarsToExport() + { + const EngineOptions& options(kEngine->getOptions()); + CarlaString binaryDir(options.binaryDir); +#ifdef HAVE_LIBLO + const int sessionManager = fSetupLabel[4] - '0'; +#endif + + CarlaString ret; + ret += "export LD_LIBRARY_PATH=" + binaryDir + "/jack\n"; +#ifdef HAVE_X11 + ret += "export LD_PRELOAD=" + binaryDir + "/libcarla_interposer-jack-x11.so\n"; +#endif +#ifdef HAVE_LIBLO + if (sessionManager == LIBJACK_SESSION_MANAGER_NSM) + { + for (int i=50; fOscServer == nullptr && --i>=0;) + carla_msleep(100); + + ret += "export NSM_URL="; + ret += lo_server_get_url(fOscServer); + ret += "\n"; + } +#endif + + if (kPlugin->getHints() & PLUGIN_HAS_CUSTOM_UI) + ret += "export CARLA_FRONTEND_WIN_ID=" + CarlaString(options.frontendWinId) + "\n"; + + ret += "export CARLA_LIBJACK_SETUP=" + fSetupLabel + "\n"; + ret += "export CARLA_SHM_IDS=" + fShmIds + "\n"; + + return ret.releaseBufferPointer(); + } + protected: #ifdef HAVE_LIBLO static void _osc_error_handler(int num, const char* msg, const char* path) @@ -250,31 +284,32 @@ protected: } #endif - if (fProcess == nullptr) - { - fProcess = new ChildProcess(); - } - else if (fProcess->isRunning()) - { - carla_stderr("CarlaPluginJackThread::run() - already running"); - } + const bool externalProcess = (fSetupLabel[5] - '0') & LIBJACK_FLAG_EXTERNAL_START; - String name(kPlugin->getName()); - String filename(kPlugin->getFilename()); + if (! externalProcess) + { + if (fProcess == nullptr) + { + fProcess = new ChildProcess(); + } + else if (fProcess->isRunning()) + { + carla_stderr("CarlaPluginJackThread::run() - already running"); + } - if (name.isEmpty()) - name = "(none)"; + String name(kPlugin->getName()); + String filename(kPlugin->getFilename()); - CARLA_SAFE_ASSERT_RETURN(filename.isNotEmpty(),); + if (name.isEmpty()) + name = "(none)"; - StringArray arguments; + CARLA_SAFE_ASSERT_RETURN(filename.isNotEmpty(),); - // binary - arguments.addTokens(filename, true); + StringArray arguments; - bool started; + // binary + arguments.addTokens(filename, true); - { const EngineOptions& options(kEngine->getOptions()); char winIdStr[STR_MAX+1]; @@ -287,7 +322,7 @@ protected: CarlaString ldpreload; #ifdef HAVE_X11 ldpreload = (CarlaString(options.binaryDir) - + "/libcarla_interposer-jack-x11.so"); + + "/libcarla_interposer-jack-x11.so"); #endif const ScopedEngineEnvironmentLocker _seel(kEngine); @@ -306,17 +341,15 @@ protected: carla_setenv("CARLA_LIBJACK_SETUP", fSetupLabel.buffer()); carla_setenv("CARLA_SHM_IDS", fShmIds.buffer()); - started = fProcess->start(arguments); - } - - if (! started) - { - carla_stdout("failed!"); - fProcess = nullptr; - return; + if (! fProcess->start(arguments)) + { + carla_stdout("failed!"); + fProcess = nullptr; + return; + } } - for (; fProcess->isRunning() && ! shouldThreadExit();) + for (; (externalProcess || fProcess->isRunning()) && ! shouldThreadExit();) { #ifdef HAVE_LIBLO if (sessionManager == LIBJACK_SESSION_MANAGER_NSM) @@ -344,32 +377,35 @@ protected: } #endif - // we only get here if bridge crashed or thread asked to exit - if (fProcess->isRunning() && shouldThreadExit()) + if (! externalProcess) { - fProcess->waitForProcessToFinish(2000); - - if (fProcess->isRunning()) + // we only get here if bridge crashed or thread asked to exit + if (fProcess->isRunning() && shouldThreadExit()) { - carla_stdout("CarlaPluginJackThread::run() - application refused to close, force kill now"); - fProcess->kill(); + fProcess->waitForProcessToFinish(2000); + + if (fProcess->isRunning()) + { + carla_stdout("CarlaPluginJackThread::run() - application refused to close, force kill now"); + fProcess->kill(); + } } - } - else - { - // forced quit, may have crashed - if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) + else { - carla_stderr("CarlaPluginJackThread::run() - application crashed"); - - CarlaString errorString("Plugin '" + CarlaString(kPlugin->getName()) + "' has crashed!\n" - "Saving now will lose its current settings.\n" - "Please remove this plugin, and not rely on it from this point."); - kEngine->callback(true, true, - ENGINE_CALLBACK_ERROR, - kPlugin->getId(), - 0, 0, 0, 0.0f, - errorString); + // forced quit, may have crashed + if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) + { + carla_stderr("CarlaPluginJackThread::run() - application crashed"); + + CarlaString errorString("Plugin '" + CarlaString(kPlugin->getName()) + "' has crashed!\n" + "Saving now will lose its current settings.\n" + "Please remove this plugin, and not rely on it from this point."); + kEngine->callback(true, true, + ENGINE_CALLBACK_ERROR, + kPlugin->getId(), + 0, 0, 0, 0.0f, + errorString); + } } } @@ -437,6 +473,7 @@ public: fProcCanceled(false), fBufferSize(engine->getBufferSize()), fProcWaitTime(0), + fSetupHints(0x0), fBridgeThread(engine, this), fShmAudioPool(), fShmRtClientControl(), @@ -1574,7 +1611,7 @@ public: // --------------------------------------------------------------- // setup hints and options - const int setupHints = label[5] - '0'; + fSetupHints = static_cast(label[5] - '0'); // FIXME dryWet broken pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS; @@ -1583,7 +1620,7 @@ public: #endif //fInfo.optionsAvailable = optionAv; - if (setupHints & LIBJACK_FLAG_CONTROL_WINDOW) + if (fSetupHints & LIBJACK_FLAG_CONTROL_WINDOW) pData->hints |= PLUGIN_HAS_CUSTOM_UI; // --------------------------------------------------------------- @@ -1629,6 +1666,7 @@ private: bool fProcCanceled; uint fBufferSize; uint fProcWaitTime; + uint fSetupHints; CarlaPluginJackThread fBridgeThread; @@ -1780,15 +1818,34 @@ private: const bool needsCancelableAction = ! pData->engine->isLoadingProject(); const bool needsEngineIdle = pData->engine->getType() != kEngineTypePlugin; + CarlaString actionName; + if (needsCancelableAction) { + if (fSetupHints & LIBJACK_FLAG_EXTERNAL_START) + { + const EngineOptions& options(pData->engine->getOptions()); + CarlaString binaryDir(options.binaryDir); + + char* const hwVars = fBridgeThread.getEnvVarsToExport(); + + actionName = "Waiting for external JACK application start, please use the following environment variables:\n"; + actionName += hwVars; + + delete[] hwVars; + } + else + { + actionName = "Loading JACK application"; + } + pData->engine->setActionCanceled(false); pData->engine->callback(true, true, ENGINE_CALLBACK_CANCELABLE_ACTION, pData->id, 1, 0, 0, 0.0f, - "Loading JACK application"); + actionName.buffer()); } for (;fBridgeThread.isThreadRunning();) @@ -1815,7 +1872,7 @@ private: pData->id, 0, 0, 0, 0.0f, - "Loading JACK application"); + actionName.buffer()); } if (fInitError || ! fInitiated) diff --git a/source/frontend/carla_database.py b/source/frontend/carla_database.py index 5493c4212..cf3fcb879 100755 --- a/source/frontend/carla_database.py +++ b/source/frontend/carla_database.py @@ -1964,9 +1964,11 @@ class JackApplicationW(QDialog): UI_SESSION_LADISH = 1 UI_SESSION_NSM = 2 - FLAG_CONTROL_WINDOW = 0x01 - FLAG_CAPTURE_FIRST_WINDOW = 0x02 - FLAG_BUFFERS_ADDITION_MODE = 0x10 + FLAG_CONTROL_WINDOW = 0x01 + FLAG_CAPTURE_FIRST_WINDOW = 0x02 + FLAG_BUFFERS_ADDITION_MODE = 0x10 + FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN = 0x20 + FLAG_EXTERNAL_START = 0x40 def __init__(self, parent, host): QDialog.__init__(self, parent) @@ -2015,6 +2017,8 @@ class JackApplicationW(QDialog): flags |= self.FLAG_CAPTURE_FIRST_WINDOW if self.ui.cb_buffers_addition_mode.isChecked(): flags |= self.FLAG_BUFFERS_ADDITION_MODE + if self.ui.cb_external_start.isChecked(): + flags |= self.FLAG_EXTERNAL_START baseIntVal = ord('0') labelSetup = "%s%s%s%s%s%s" % (chr(baseIntVal+self.ui.sb_audio_ins.value()), diff --git a/source/includes/CarlaLibJackHints.h b/source/includes/CarlaLibJackHints.h index b2acf7c19..d9c8fb5a0 100644 --- a/source/includes/CarlaLibJackHints.h +++ b/source/includes/CarlaLibJackHints.h @@ -26,11 +26,13 @@ extern "C" { enum SetupHints { // Application Window management - LIBJACK_FLAG_CONTROL_WINDOW = 0x01, - LIBJACK_FLAG_CAPTURE_FIRST_WINDOW = 0x02, + LIBJACK_FLAG_CONTROL_WINDOW = 0x01, + LIBJACK_FLAG_CAPTURE_FIRST_WINDOW = 0x02, // Audio/MIDI Buffers management - LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION = 0x10, - LIBJACK_FLAG_MIDI_OUTPUT_BUFFERS_CHANNEL_MIXDOWN = 0x20, + LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION = 0x10, + LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN = 0x20, + // Developer options + LIBJACK_FLAG_EXTERNAL_START = 0x40, }; enum SessionManager { diff --git a/source/utils/CarlaString.hpp b/source/utils/CarlaString.hpp index 81d8ed5cb..cd84a35eb 100644 --- a/source/utils/CarlaString.hpp +++ b/source/utils/CarlaString.hpp @@ -597,6 +597,19 @@ public: return carla_strdup_safe(fBuffer); } + /* + * Release the buffer pointer while clearing this string. + * This allows to keep a pointer to the buffer after this object is deleted. + */ + char* releaseBufferPointer() noexcept + { + char* ret = fBufferLen > 0 ? fBuffer : nullptr; + fBuffer = _null(); + fBufferLen = 0; + fBufferAlloc = false; + return ret; + } + // ------------------------------------------------------------------- // base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html // Copyright (C) 2004-2008 René Nyffenegger