From f4b6b4fd28e4000362dd5bca75dfa322782b84f6 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 28 Dec 2014 02:45:58 +0000 Subject: [PATCH] More plugin-bridge work (server-side mostly complete now) --- source/backend/CarlaEngine.hpp | 7 + source/backend/engine/CarlaEngine.cpp | 10 + source/backend/engine/CarlaEngineInternal.cpp | 1 + source/backend/engine/CarlaEngineInternal.hpp | 1 + source/backend/plugin/CarlaPluginBridge.cpp | 255 +++++++++--- source/backend/plugin/CarlaPluginDSSI.cpp | 33 +- source/backend/plugin/CarlaPluginThread.cpp | 365 ------------------ source/utils/CarlaBridgeUtils.hpp | 8 +- source/utils/CarlaEngineUtils.hpp | 24 ++ source/utils/CarlaShmUtils.hpp | 2 +- 10 files changed, 282 insertions(+), 424 deletions(-) diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index 6d9f57b6b..fb18d5279 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -1062,6 +1062,7 @@ protected: friend class CarlaPluginInstance; friend class EngineInternalGraph; friend class ScopedActionLock; + friend class ScopedEngineEnvironmentLocker; friend class PendingRtEventsRunner; friend struct PatchbayGraph; friend struct RackGraph; @@ -1108,6 +1109,12 @@ protected: */ bool loadProjectInternal(juce::XmlDocument& xmlDoc); + /*! + * Lock/Unlock environment mutex, to prevent simultaneous changes from different threads. + */ + void lockEnvironment() const noexcept; + void unlockEnvironment() const noexcept; + #ifndef BUILD_BRIDGE // ------------------------------------------------------------------- // Patchbay stuff diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index f364fc8ce..8a2202cce 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -1450,6 +1450,16 @@ EngineEvent* CarlaEngine::getInternalEventBuffer(const bool isInput) const noexc return isInput ? pData->events.in : pData->events.out; } +void CarlaEngine::lockEnvironment() const noexcept +{ + pData->envMutex.lock(); +} + +void CarlaEngine::unlockEnvironment() const noexcept +{ + pData->envMutex.unlock(); +} + // ----------------------------------------------------------------------- // Internal stuff diff --git a/source/backend/engine/CarlaEngineInternal.cpp b/source/backend/engine/CarlaEngineInternal.cpp index 86a211df1..ac6ebea3a 100644 --- a/source/backend/engine/CarlaEngineInternal.cpp +++ b/source/backend/engine/CarlaEngineInternal.cpp @@ -109,6 +109,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept curPluginCount(0), maxPluginNumber(0), nextPluginId(0), + envMutex(), lastError(), name(), options(), diff --git a/source/backend/engine/CarlaEngineInternal.hpp b/source/backend/engine/CarlaEngineInternal.hpp index 571a3129d..e7609793d 100644 --- a/source/backend/engine/CarlaEngineInternal.hpp +++ b/source/backend/engine/CarlaEngineInternal.hpp @@ -175,6 +175,7 @@ struct CarlaEngine::ProtectedData { uint maxPluginNumber; // number of plugins allowed (0, 16, 99 or 255) uint nextPluginId; // invalid if == maxPluginNumber + CarlaMutex envMutex; CarlaString lastError; CarlaString name; EngineOptions options; diff --git a/source/backend/plugin/CarlaPluginBridge.cpp b/source/backend/plugin/CarlaPluginBridge.cpp index 7851ec6f1..8b16e1494 100644 --- a/source/backend/plugin/CarlaPluginBridge.cpp +++ b/source/backend/plugin/CarlaPluginBridge.cpp @@ -20,11 +20,11 @@ #endif #include "CarlaPluginInternal.hpp" -#include "CarlaEngine.hpp" #include "CarlaBackendUtils.hpp" #include "CarlaBase64Utils.hpp" #include "CarlaBridgeUtils.hpp" +#include "CarlaEngineUtils.hpp" #include "CarlaMathUtils.hpp" #include "CarlaShmUtils.hpp" #include "CarlaThread.hpp" @@ -487,21 +487,22 @@ public: fBinary(), fLabel(), fShmIds(), - fPluginType(PLUGIN_NONE), fProcess(), leakDetector_CarlaPluginBridgeThread() {} - void setData(const char* const binary, const char* const label, const char* const shmIds, const PluginType ptype) noexcept + void setData(const char* const binary, const char* const label, const char* const shmIds) noexcept { CARLA_SAFE_ASSERT_RETURN(binary != nullptr && binary[0] != '\0',); - CARLA_SAFE_ASSERT_RETURN(label != nullptr && label[0] != '\0',); + CARLA_SAFE_ASSERT_RETURN(label != nullptr,); CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',); CARLA_SAFE_ASSERT(! isThreadRunning()); fBinary = binary; fLabel = binary; fShmIds = shmIds; - fPluginType = ptype; + + if (fLabel.isEmpty()) + fLabel = "\"\""; } protected: @@ -529,32 +530,176 @@ protected: #ifndef CARLA_OS_WIN // start with "wine" if needed - if (fBinary.endsWith(".exe")) + if (fBinary.endsWithIgnoreCase(".exe")) arguments.add("wine"); #endif // binary - arguments.add(fBinary.buffer()); + arguments.add(fBinary); + + // plugin type + arguments.add(PluginType2Str(kPlugin->getType())); + + // filename + arguments.add(filename); + + // label + arguments.add(fLabel); + + // uniqueId + arguments.add(String(static_cast(kPlugin->getUniqueId()))); + + bool started; + + { + char strBuf[STR_MAX+1]; + strBuf[STR_MAX] = '\0'; + + const EngineOptions& options(kEngine->getOptions()); + const ScopedEngineEnvironmentLocker _seel(kEngine); + +#ifdef CARLA_OS_LINUX + const char* const oldPreload(std::getenv("LD_PRELOAD")); + + if (oldPreload != nullptr) + ::unsetenv("LD_PRELOAD"); +#endif + + carla_setenv("ENGINE_OPTION_FORCE_STEREO", bool2str(options.forceStereo)); + carla_setenv("ENGINE_OPTION_PREFER_PLUGIN_BRIDGES", bool2str(options.preferPluginBridges)); + carla_setenv("ENGINE_OPTION_PREFER_UI_BRIDGES", bool2str(options.preferUiBridges)); + carla_setenv("ENGINE_OPTION_UIS_ALWAYS_ON_TOP", bool2str(options.uisAlwaysOnTop)); + + std::snprintf(strBuf, STR_MAX, "%u", options.maxParameters); + carla_setenv("ENGINE_OPTION_MAX_PARAMETERS", strBuf); + + std::snprintf(strBuf, STR_MAX, "%u", options.uiBridgesTimeout); + carla_setenv("ENGINE_OPTION_UI_BRIDGES_TIMEOUT",strBuf); + + if (options.pathLADSPA != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", options.pathLADSPA); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", ""); + + if (options.pathDSSI != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", options.pathDSSI); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", ""); + + if (options.pathLV2 != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", options.pathLV2); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", ""); + + if (options.pathVST2 != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", options.pathVST2); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", ""); + + if (options.pathVST3 != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", options.pathVST3); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", ""); + + if (options.pathAU != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", options.pathAU); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", ""); + + if (options.pathGIG != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", options.pathGIG); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", ""); + + if (options.pathSF2 != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", options.pathSF2); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", ""); + + if (options.pathSFZ != nullptr) + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", options.pathSFZ); + else + carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", ""); + + if (options.binaryDir != nullptr) + carla_setenv("ENGINE_OPTION_PATH_BINARIES", options.binaryDir); + else + carla_setenv("ENGINE_OPTION_PATH_BINARIES", ""); + + if (options.resourceDir != nullptr) + carla_setenv("ENGINE_OPTION_PATH_RESOURCES", options.resourceDir); + else + carla_setenv("ENGINE_OPTION_PATH_RESOURCES", ""); + + carla_setenv("ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR", bool2str(options.preventBadBehaviour)); + + std::snprintf(strBuf, STR_MAX, P_UINTPTR, options.frontendWinId); + carla_setenv("ENGINE_OPTION_FRONTEND_WIN_ID", strBuf); + + carla_setenv("ENGINE_BRIDGE_SHM_IDS", fShmIds.toRawUTF8()); + carla_setenv("WINEDEBUG", "-all"); -#if 0 - /* stype */ arguments.add(fExtra1.buffer()); - /* filename */ arguments.add(filename); - /* label */ arguments.add(fLabel.buffer()); - /* uniqueId */ arguments.add(String(static_cast(fPlugin->getUniqueId()))); + carla_stdout("starting plugin bridge.."); + started = fProcess->start(arguments); - carla_setenv("ENGINE_BRIDGE_SHM_IDS", fShmIds.buffer()); - carla_setenv("WINEDEBUG", "-all"); +#ifdef CARLA_OS_LINUX + if (oldPreload != nullptr) + ::setenv("LD_PRELOAD", oldPreload, 1); #endif + } + + if (! started) + { + carla_stdout("failed!"); + fProcess = nullptr; + return; + } + + for (; fProcess->isRunning() && ! shouldThreadExit();) + carla_sleep(1); + + // we only get here if bridge crashed or thread asked to exit + if (fProcess->isRunning() && shouldThreadExit()) + { + fProcess->waitForProcessToFinish(2000); + + if (fProcess->isRunning()) + { + carla_stdout("CarlaPluginBridgeThread::run() - bridge refused to close, force kill now"); + fProcess->kill(); + } + else + { + carla_stdout("CarlaPluginBridgeThread::run() - bridge auto-closed successfully"); + } + } + else + { + // forced quit, may have crashed + if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) + { + carla_stderr("CarlaPluginBridgeThread::run() - bridge 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(CarlaBackend::ENGINE_CALLBACK_ERROR, kPlugin->getId(), 0, 0, 0.0f, errorString); + } + else + carla_stderr("CarlaPluginBridgeThread::run() - bridge closed cleanly"); + } + + carla_stdout("plugin bridge finished"); + fProcess = nullptr; } private: CarlaEngine* const kEngine; CarlaPlugin* const kPlugin; - CarlaString fBinary; - CarlaString fLabel; - CarlaString fShmIds; - PluginType fPluginType; + String fBinary; + String fLabel; + String fShmIds; ScopedPointer fProcess; @@ -720,14 +865,14 @@ public: void getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override { - CARLA_ASSERT(parameterId < pData->param.count); + CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, nullStrBuf(strBuf)); std::strncpy(strBuf, fParams[parameterId].name.buffer(), STR_MAX); } void getParameterUnit(const uint32_t parameterId, char* const strBuf) const noexcept override { - CARLA_ASSERT(parameterId < pData->param.count); + CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, nullStrBuf(strBuf)); std::strncpy(strBuf, fParams[parameterId].unit.buffer(), STR_MAX); } @@ -886,28 +1031,44 @@ public: CarlaPlugin::setMidiProgram(index, sendGui, sendOsc, sendCallback); } -#if 0 void setCustomData(const char* const type, const char* const key, const char* const value, const bool sendGui) override { - CARLA_ASSERT(type); - CARLA_ASSERT(key); - CARLA_ASSERT(value); + CARLA_SAFE_ASSERT_RETURN(type != nullptr && type[0] != '\0',); + CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); + CARLA_SAFE_ASSERT_RETURN(value != nullptr,); + + using namespace juce; + + const uint32_t typeLen(static_cast(std::strlen(type))); + const uint32_t keyLen(static_cast(std::strlen(key))); + + MemoryOutputStream valueMemStream; + GZIPCompressorOutputStream compressedValueStream(&valueMemStream, 9, false); + compressedValueStream.write(value, std::strlen(value)); + + const CarlaString valueBase64(CarlaString::asBase64(valueMemStream.getData(), valueMemStream.getDataSize())); + const uint32_t valueBase64Len(static_cast(valueBase64.length())); + CARLA_SAFE_ASSERT_RETURN(valueBase64.length() > 0,); - if (sendGui) { - // TODO - if type is chunk|binary, store it in a file and send path instead - QString cData; - cData = type; - cData += "·"; - cData += key; - cData += "·"; - cData += value; - osc_send_configure(&osc.data, CARLA_BRIDGE_MSG_SET_CUSTOM, cData.toUtf8().constData()); + const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); + + fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientSetCustomData); + + fShmNonRtClientControl.writeUInt(typeLen); + fShmNonRtClientControl.writeCustomData(type, typeLen); + + fShmNonRtClientControl.writeUInt(keyLen); + fShmNonRtClientControl.writeCustomData(key, keyLen); + + fShmNonRtClientControl.writeUInt(valueBase64Len); + fShmNonRtClientControl.writeCustomData(valueBase64.buffer(), valueBase64Len); + + fShmNonRtClientControl.commitWrite(); } CarlaPlugin::setCustomData(type, key, value, sendGui); } -#endif void setChunkData(const void* const data, const std::size_t dataSize) override { @@ -915,13 +1076,12 @@ public: CARLA_SAFE_ASSERT_RETURN(data != nullptr,); CARLA_SAFE_ASSERT_RETURN(dataSize > 0,); - CarlaString dataBase64 = CarlaString::asBase64(data, dataSize); + CarlaString dataBase64(CarlaString::asBase64(data, dataSize)); CARLA_SAFE_ASSERT_RETURN(dataBase64.length() > 0,); String filePath(File::getSpecialLocation(File::tempDirectory).getFullPathName()); - filePath += CARLA_OS_SEP_STR; - filePath += ".CarlaChunk_"; + filePath += CARLA_OS_SEP_STR ".CarlaChunk_"; filePath += fShmAudioPool.filename.buffer() + 18; if (File(filePath).replaceWithText(dataBase64.buffer())) @@ -1047,6 +1207,7 @@ public: } else portName += "input"; + portName.truncate(portNameSize); pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true); @@ -1071,6 +1232,7 @@ public: } else portName += "output"; + portName.truncate(portNameSize); pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false); @@ -1996,6 +2158,8 @@ public: case kPluginBridgeNonRtServerSetCustomData: { // uint/size, str[], uint/size, str[], uint/size, str[] (compressed) + using namespace juce; + // type const uint32_t typeSize(fShmNonRtServerControl.readUInt()); char type[typeSize+1]; @@ -2009,12 +2173,17 @@ public: fShmNonRtServerControl.readCustomData(key, keySize); // value - const uint32_t valueSize(fShmNonRtServerControl.readUInt()); - char value[valueSize+1]; - carla_zeroChar(value, valueSize+1); - fShmNonRtServerControl.readCustomData(value, valueSize); + const uint32_t valueBase64Size(fShmNonRtServerControl.readUInt()); + char valueBase64[valueBase64Size+1]; + carla_zeroChar(valueBase64, valueBase64Size+1); + fShmNonRtServerControl.readCustomData(valueBase64, valueBase64Size); + + const std::vector valueChunk(carla_getChunkFromBase64String(valueBase64)); + + MemoryInputStream valueMemStream(valueChunk.data(), valueChunk.size(), false); + GZIPDecompressorInputStream decompressedValueStream(valueMemStream); - CarlaPlugin::setCustomData(type, key, value, false); + CarlaPlugin::setCustomData(type, key, decompressedValueStream.readEntireStreamAsString().toRawUTF8(), false); } break; case kPluginBridgeNonRtServerSetChunkDataFile: { @@ -2192,7 +2361,7 @@ public: std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.filename.length()-6], 6); - fBridgeThread.setData(bridgeBinary, label, shmIdsStr, fPluginType); + fBridgeThread.setData(bridgeBinary, label, shmIdsStr); fBridgeThread.startThread(); } diff --git a/source/backend/plugin/CarlaPluginDSSI.cpp b/source/backend/plugin/CarlaPluginDSSI.cpp index d5f40706c..becd00ac2 100644 --- a/source/backend/plugin/CarlaPluginDSSI.cpp +++ b/source/backend/plugin/CarlaPluginDSSI.cpp @@ -19,7 +19,7 @@ // TODO: common laterncy code #include "CarlaPluginInternal.hpp" -#include "CarlaEngine.hpp" +#include "CarlaEngineUtils.hpp" #include "CarlaDssiUtils.hpp" #include "CarlaMathUtils.hpp" @@ -111,14 +111,6 @@ public: return; } -#if 0 //def CARLA_OS_LINUX - const char* const oldPreload(std::getenv("LD_PRELOAD")); - ::unsetenv("LD_PRELOAD"); - - if (oldPreload != nullptr) - ::setenv("LD_PRELOAD", oldPreload, 1); -#endif - String name(kPlugin->getName()); String filename(kPlugin->getFilename()); @@ -145,9 +137,28 @@ public: // ui-title arguments.add(name + String(" (GUI)")); - carla_stdout("starting DSSI UI..."); + bool started; + + { +#ifdef CARLA_OS_LINUX + const ScopedEngineEnvironmentLocker _seel(kEngine); + + const char* const oldPreload(std::getenv("LD_PRELOAD")); + + if (oldPreload != nullptr) + ::unsetenv("LD_PRELOAD"); +#endif + + carla_stdout("starting DSSI UI..."); + started = fProcess->start(arguments); + +#ifdef CARLA_OS_LINUX + if (oldPreload != nullptr) + ::setenv("LD_PRELOAD", oldPreload, 1); +#endif + } - if (! fProcess->start(arguments)) + if (! started) { carla_stdout("failed!"); fProcess = nullptr; diff --git a/source/backend/plugin/CarlaPluginThread.cpp b/source/backend/plugin/CarlaPluginThread.cpp index 1e4675bc6..783108b77 100644 --- a/source/backend/plugin/CarlaPluginThread.cpp +++ b/source/backend/plugin/CarlaPluginThread.cpp @@ -16,373 +16,8 @@ */ #if 0 - -#include "CarlaPlugin.hpp" -#include "CarlaPluginThread.hpp" -#include "CarlaEngine.hpp" - -#include "juce_core.h" - -using juce::String; -using juce::StringArray; - -CARLA_BACKEND_START_NAMESPACE - -// ----------------------------------------------------------------------- - -#ifdef DEBUG -static inline -const char* PluginThreadMode2str(const CarlaPluginThread::Mode mode) noexcept -{ - switch (mode) - { - case CarlaPluginThread::PLUGIN_THREAD_NULL: - return "PLUGIN_THREAD_NULL"; - case CarlaPluginThread::PLUGIN_THREAD_DSSI_GUI: - return "PLUGIN_THREAD_DSSI_GUI"; - case CarlaPluginThread::PLUGIN_THREAD_LV2_GUI: - return "PLUGIN_THREAD_LV2_GUI"; - case CarlaPluginThread::PLUGIN_THREAD_VST2_GUI: - return "PLUGIN_THREAD_VST2_GUI"; - case CarlaPluginThread::PLUGIN_THREAD_BRIDGE: - return "PLUGIN_THREAD_BRIDGE"; - } - - carla_stderr("CarlaPluginThread::PluginThreadMode2str(%i) - invalid mode", mode); - return nullptr; -} -#endif - -// ----------------------------------------------------------------------- - -CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine, CarlaBackend::CarlaPlugin* const plugin, const Mode mode) noexcept - : CarlaThread("CarlaPluginThread"), - fEngine(engine), - fPlugin(plugin), - fMode(mode), - fBinary(), - fLabel(), - fExtra1(), - fExtra2(), - fProcess(nullptr), - leakDetector_CarlaPluginThread() -{ - carla_debug("CarlaPluginThread::CarlaPluginThread(%p, %p, %s)", engine, plugin, PluginThreadMode2str(mode)); -} - -CarlaPluginThread::~CarlaPluginThread() noexcept -{ - carla_debug("CarlaPluginThread::~CarlaPluginThread()"); - - if (fProcess != nullptr) - { - //fProcess.release(); - //try { - //delete fProcess; - //} CARLA_SAFE_EXCEPTION("~CarlaPluginThread(): delete ChildProcess"); - fProcess = nullptr; - } -} - -void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode) noexcept -{ - CARLA_SAFE_ASSERT(! isThreadRunning()); - carla_debug("CarlaPluginThread::setMode(%s)", PluginThreadMode2str(mode)); - - fMode = mode; -} - -void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra1, const char* const extra2) noexcept -{ - CARLA_SAFE_ASSERT(! isThreadRunning()); - carla_debug("CarlaPluginThread::setOscData(\"%s\", \"%s\", \"%s\", \"%s\")", binary, label, extra1, extra2); - - fBinary = binary; - fLabel = label; - fExtra1 = extra1; - fExtra2 = extra2; -} - -uintptr_t CarlaPluginThread::getPid() const -{ - CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr, 0); - - return (uintptr_t)fProcess->getPID(); -} - -void CarlaPluginThread::run() -{ - carla_debug("CarlaPluginThread::run()"); - - if (fProcess == nullptr) - { - fProcess = new ChildProcess; - //fProcess->setProcessChannelMode(QProcess::ForwardedChannels); - } - else if (fProcess->isRunning()) - { - carla_stderr("CarlaPluginThread::run() - already running, giving up..."); - - switch (fMode) - { - case PLUGIN_THREAD_NULL: - case PLUGIN_THREAD_BRIDGE: - break; - - case PLUGIN_THREAD_DSSI_GUI: - case PLUGIN_THREAD_LV2_GUI: - case PLUGIN_THREAD_VST2_GUI: - fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr); - fProcess->kill(); - fProcess = nullptr; - return; - } - } - - String name(fPlugin->getName()); - String filename(fPlugin->getFilename()); - - if (name.isEmpty()) - name = "(none)"; - - if (filename.isEmpty()) - filename = "\"\""; - - if (fLabel.isEmpty()) - fLabel = "\"\""; - - StringArray arguments; - -#ifndef CARLA_OS_WIN - if (fBinary.endsWith(".exe")) - arguments.add("wine"); -#endif - - arguments.add(fBinary.buffer()); - - // use a global mutex to ensure bridge environment is correct - static CarlaMutex sEnvMutex; - - char strBuf[STR_MAX+1]; - strBuf[STR_MAX] = '\0'; - - const EngineOptions& options(fEngine->getOptions()); - - sEnvMutex.lock(); - - carla_setenv("CARLA_CLIENT_NAME", name.toRawUTF8()); - - std::snprintf(strBuf, STR_MAX, "%f", fEngine->getSampleRate()); - carla_setenv("CARLA_SAMPLE_RATE", strBuf); - - carla_setenv("ENGINE_OPTION_FORCE_STEREO", bool2str(options.forceStereo)); - carla_setenv("ENGINE_OPTION_PREFER_PLUGIN_BRIDGES", bool2str(options.preferPluginBridges)); - carla_setenv("ENGINE_OPTION_PREFER_UI_BRIDGES", bool2str(options.preferUiBridges)); - carla_setenv("ENGINE_OPTION_UIS_ALWAYS_ON_TOP", bool2str(options.uisAlwaysOnTop)); - - std::snprintf(strBuf, STR_MAX, "%u", options.maxParameters); - carla_setenv("ENGINE_OPTION_MAX_PARAMETERS", strBuf); - - std::snprintf(strBuf, STR_MAX, "%u", options.uiBridgesTimeout); - carla_setenv("ENGINE_OPTION_UI_BRIDGES_TIMEOUT",strBuf); - - if (options.pathLADSPA != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", options.pathLADSPA); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", ""); - - if (options.pathDSSI != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", options.pathDSSI); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", ""); - - if (options.pathLV2 != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", options.pathLV2); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", ""); - - if (options.pathVST2 != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", options.pathVST2); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", ""); - - if (options.pathVST3 != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", options.pathVST3); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", ""); - - if (options.pathAU != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", options.pathAU); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", ""); - - if (options.pathGIG != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", options.pathGIG); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", ""); - - if (options.pathSF2 != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", options.pathSF2); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", ""); - - if (options.pathSFZ != nullptr) - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", options.pathSFZ); - else - carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", ""); - - if (options.binaryDir != nullptr) - carla_setenv("ENGINE_OPTION_PATH_BINARIES", options.binaryDir); - else - carla_setenv("ENGINE_OPTION_PATH_BINARIES", ""); - - if (options.resourceDir != nullptr) - carla_setenv("ENGINE_OPTION_PATH_RESOURCES", options.resourceDir); - else - carla_setenv("ENGINE_OPTION_PATH_RESOURCES", ""); - - carla_setenv("ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR", bool2str(options.preventBadBehaviour)); - - std::snprintf(strBuf, STR_MAX, P_UINTPTR, options.frontendWinId); - carla_setenv("ENGINE_OPTION_FRONTEND_WIN_ID", strBuf); - -#ifdef CARLA_OS_LINUX - const char* const oldPreload(std::getenv("LD_PRELOAD")); - ::unsetenv("LD_PRELOAD"); -#endif - - switch (fMode) - { - case PLUGIN_THREAD_NULL: - break; - - case PLUGIN_THREAD_DSSI_GUI: - /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId())); - /* filename */ arguments.add(filename); - /* label */ arguments.add(fLabel.buffer()); - /* ui-title */ arguments.add(name + String(" (GUI)")); - break; - - case PLUGIN_THREAD_LV2_GUI: - /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId())); - /* URI */ arguments.add(fLabel.buffer()); - /* UI URI */ arguments.add(fExtra1.buffer()); - /* UI Bundle */ arguments.add(fExtra2.buffer()); - /* UI Title */ arguments.add(name + String(" (GUI)")); - break; - - case PLUGIN_THREAD_VST2_GUI: - /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId())); - /* filename */ arguments.add(filename); - /* ui-title */ arguments.add(name + String(" (GUI)")); - break; - - case PLUGIN_THREAD_BRIDGE: - /* stype */ arguments.add(fExtra1.buffer()); - /* filename */ arguments.add(filename); - /* label */ arguments.add(fLabel.buffer()); - /* uniqueId */ arguments.add(String(static_cast(fPlugin->getUniqueId()))); - - carla_setenv("ENGINE_BRIDGE_OSC_URL", String(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId())).toRawUTF8()); - carla_setenv("ENGINE_BRIDGE_SHM_IDS", fExtra2.buffer()); - carla_setenv("WINEDEBUG", "-all"); - break; - } - - carla_stdout("starting app.."); - - fProcess->start(arguments); - -#ifdef CARLA_OS_LINUX - if (oldPreload != nullptr) - ::setenv("LD_PRELOAD", oldPreload, 1); -#endif - - sEnvMutex.unlock(); - - switch (fMode) - { - case PLUGIN_THREAD_NULL: - break; - - case PLUGIN_THREAD_DSSI_GUI: - case PLUGIN_THREAD_LV2_GUI: - case PLUGIN_THREAD_VST2_GUI: - if (fPlugin->waitForOscGuiShow()) - { - while (fProcess->isRunning() && ! shouldThreadExit()) - carla_sleep(1); - - // we only get here if UI was closed or thread asked to exit - if (fProcess->isRunning() && shouldThreadExit()) - { - fProcess->waitForProcessToFinish(static_cast(fEngine->getOptions().uiBridgesTimeout)); - - if (fProcess->isRunning()) - { - carla_stdout("CarlaPluginThread::run() - UI refused to close, force kill now"); - fProcess->kill(); - } - else - { - carla_stdout("CarlaPluginThread::run() - UI auto-closed successfully"); - } - } - else if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) - carla_stderr("CarlaPluginThread::run() - UI crashed while running"); - else - carla_stdout("CarlaPluginThread::run() - UI closed cleanly"); - - fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr); - } - else - { - fProcess->kill(); - - carla_stdout("CarlaPluginThread::run() - GUI timeout"); - fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr); - } - break; - - case PLUGIN_THREAD_BRIDGE: //fProcess->waitForFinished(-1); - while (fProcess->isRunning() && ! shouldThreadExit()) - carla_sleep(1); - - // we only get here if bridge crashed or thread asked to exit - if (fProcess->isRunning() && shouldThreadExit()) - { - fProcess->waitForProcessToFinish(2000); - - if (fProcess->isRunning()) - { - carla_stdout("CarlaPluginThread::run() - bridge refused to close, force kill now"); - fProcess->kill(); - } - else - { - carla_stdout("CarlaPluginThread::run() - bridge auto-closed successfully"); - } - } - else - { - // forced quit, may have crashed - if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/) - { - carla_stderr("CarlaPluginThread::run() - bridge crashed"); - - CarlaString errorString("Plugin '" + CarlaString(fPlugin->getName()) + "' has crashed!\n" - "Saving now will lose its current settings.\n" - "Please remove this plugin, and not rely on it from this point."); - fEngine->callback(CarlaBackend::ENGINE_CALLBACK_ERROR, fPlugin->getId(), 0, 0, 0.0f, errorString); - } - } - break; - } - - carla_stdout("app finished"); - fProcess = nullptr; } // ----------------------------------------------------------------------- diff --git a/source/utils/CarlaBridgeUtils.hpp b/source/utils/CarlaBridgeUtils.hpp index 1aebb0ff0..001580df9 100644 --- a/source/utils/CarlaBridgeUtils.hpp +++ b/source/utils/CarlaBridgeUtils.hpp @@ -56,8 +56,8 @@ enum PluginBridgeNonRtClientOpcode { kPluginBridgeNonRtClientSetParameterMidiCC, // uint, short kPluginBridgeNonRtClientSetProgram, // int kPluginBridgeNonRtClientSetMidiProgram, // int - kPluginBridgeNonRtClientSetCustomData, // uint/size, str[], uint/size, str[], uint/size, str[] (compressed) - kPluginBridgeNonRtClientSetChunkDataFile, // uint/size, str[] (filename) + kPluginBridgeNonRtClientSetCustomData, // uint/size, str[], uint/size, str[], uint/size, str[] (base64, compressed) + kPluginBridgeNonRtClientSetChunkDataFile, // uint/size, str[] (filename, base64 content) kPluginBridgeNonRtClientSetCtrlChannel, // short kPluginBridgeNonRtClientSetOption, // uint/option, bool kPluginBridgeNonRtClientPrepareForSave, @@ -92,8 +92,8 @@ enum PluginBridgeNonRtServerOpcode { kPluginBridgeNonRtServerCurrentMidiProgram, // int/index kPluginBridgeNonRtServerProgramName, // uint/index, uint/size, str[] (name) kPluginBridgeNonRtServerMidiProgramData, // uint/index, uint/bank, uint/program, uint/size, str[] (name) - kPluginBridgeNonRtServerSetCustomData, // uint/size, str[], uint/size, str[], uint/size, str[] (compressed) - kPluginBridgeNonRtServerSetChunkDataFile, // uint/size, str[] (filename) + kPluginBridgeNonRtServerSetCustomData, // uint/size, str[], uint/size, str[], uint/size, str[] (base64, compressed) + kPluginBridgeNonRtServerSetChunkDataFile, // uint/size, str[] (filename, base64 content) kPluginBridgeNonRtServerSetLatency, // uint kPluginBridgeNonRtServerReady, kPluginBridgeNonRtServerSaved, diff --git a/source/utils/CarlaEngineUtils.hpp b/source/utils/CarlaEngineUtils.hpp index 539c5fea0..7e42db50c 100644 --- a/source/utils/CarlaEngineUtils.hpp +++ b/source/utils/CarlaEngineUtils.hpp @@ -194,6 +194,30 @@ void fillJuceMidiBufferFromEngineEvents(juce::MidiBuffer& midiBuffer, const Engi } } +// ------------------------------------------------------------------- +// Helper classes + +class ScopedEngineEnvironmentLocker +{ +public: + ScopedEngineEnvironmentLocker(CarlaEngine* const engine) noexcept + : kEngine(engine) + { + kEngine->lockEnvironment(); + } + + ~ScopedEngineEnvironmentLocker() noexcept + { + kEngine->unlockEnvironment(); + } + +private: + CarlaEngine* const kEngine; + + CARLA_PREVENT_HEAP_ALLOCATION + CARLA_DECLARE_NON_COPY_CLASS(ScopedEngineEnvironmentLocker) +}; + // ----------------------------------------------------------------------- CARLA_BACKEND_END_NAMESPACE diff --git a/source/utils/CarlaShmUtils.hpp b/source/utils/CarlaShmUtils.hpp index 4438faaad..e6c091ced 100644 --- a/source/utils/CarlaShmUtils.hpp +++ b/source/utils/CarlaShmUtils.hpp @@ -252,7 +252,7 @@ shm_t carla_shm_create_temp(char* const fileBase) noexcept const std::size_t fileBaseLen(std::strlen(fileBase)); CARLA_SAFE_ASSERT_RETURN(fileBaseLen > 6, gNullCarlaShm); - CARLA_SAFE_ASSERT_RETURN(std::strcmp(fileBase + fileBaseLen - 6, "XXXXXX") == 0, gNullCarlaShm); + CARLA_SAFE_ASSERT_RETURN(std::strcmp(fileBase + (fileBaseLen - 6), "XXXXXX") == 0, gNullCarlaShm); // character set to use randomly static const char charSet[] = "abcdefghijklmnopqrstuvwxyz"