| @@ -1062,6 +1062,7 @@ protected: | |||||
| friend class CarlaPluginInstance; | friend class CarlaPluginInstance; | ||||
| friend class EngineInternalGraph; | friend class EngineInternalGraph; | ||||
| friend class ScopedActionLock; | friend class ScopedActionLock; | ||||
| friend class ScopedEngineEnvironmentLocker; | |||||
| friend class PendingRtEventsRunner; | friend class PendingRtEventsRunner; | ||||
| friend struct PatchbayGraph; | friend struct PatchbayGraph; | ||||
| friend struct RackGraph; | friend struct RackGraph; | ||||
| @@ -1108,6 +1109,12 @@ protected: | |||||
| */ | */ | ||||
| bool loadProjectInternal(juce::XmlDocument& xmlDoc); | 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 | #ifndef BUILD_BRIDGE | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| // Patchbay stuff | // Patchbay stuff | ||||
| @@ -1450,6 +1450,16 @@ EngineEvent* CarlaEngine::getInternalEventBuffer(const bool isInput) const noexc | |||||
| return isInput ? pData->events.in : pData->events.out; | 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 | // Internal stuff | ||||
| @@ -109,6 +109,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept | |||||
| curPluginCount(0), | curPluginCount(0), | ||||
| maxPluginNumber(0), | maxPluginNumber(0), | ||||
| nextPluginId(0), | nextPluginId(0), | ||||
| envMutex(), | |||||
| lastError(), | lastError(), | ||||
| name(), | name(), | ||||
| options(), | options(), | ||||
| @@ -175,6 +175,7 @@ struct CarlaEngine::ProtectedData { | |||||
| uint maxPluginNumber; // number of plugins allowed (0, 16, 99 or 255) | uint maxPluginNumber; // number of plugins allowed (0, 16, 99 or 255) | ||||
| uint nextPluginId; // invalid if == maxPluginNumber | uint nextPluginId; // invalid if == maxPluginNumber | ||||
| CarlaMutex envMutex; | |||||
| CarlaString lastError; | CarlaString lastError; | ||||
| CarlaString name; | CarlaString name; | ||||
| EngineOptions options; | EngineOptions options; | ||||
| @@ -20,11 +20,11 @@ | |||||
| #endif | #endif | ||||
| #include "CarlaPluginInternal.hpp" | #include "CarlaPluginInternal.hpp" | ||||
| #include "CarlaEngine.hpp" | |||||
| #include "CarlaBackendUtils.hpp" | #include "CarlaBackendUtils.hpp" | ||||
| #include "CarlaBase64Utils.hpp" | #include "CarlaBase64Utils.hpp" | ||||
| #include "CarlaBridgeUtils.hpp" | #include "CarlaBridgeUtils.hpp" | ||||
| #include "CarlaEngineUtils.hpp" | |||||
| #include "CarlaMathUtils.hpp" | #include "CarlaMathUtils.hpp" | ||||
| #include "CarlaShmUtils.hpp" | #include "CarlaShmUtils.hpp" | ||||
| #include "CarlaThread.hpp" | #include "CarlaThread.hpp" | ||||
| @@ -487,21 +487,22 @@ public: | |||||
| fBinary(), | fBinary(), | ||||
| fLabel(), | fLabel(), | ||||
| fShmIds(), | fShmIds(), | ||||
| fPluginType(PLUGIN_NONE), | |||||
| fProcess(), | fProcess(), | ||||
| leakDetector_CarlaPluginBridgeThread() {} | 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(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_RETURN(shmIds != nullptr && shmIds[0] != '\0',); | ||||
| CARLA_SAFE_ASSERT(! isThreadRunning()); | CARLA_SAFE_ASSERT(! isThreadRunning()); | ||||
| fBinary = binary; | fBinary = binary; | ||||
| fLabel = binary; | fLabel = binary; | ||||
| fShmIds = shmIds; | fShmIds = shmIds; | ||||
| fPluginType = ptype; | |||||
| if (fLabel.isEmpty()) | |||||
| fLabel = "\"\""; | |||||
| } | } | ||||
| protected: | protected: | ||||
| @@ -529,32 +530,176 @@ protected: | |||||
| #ifndef CARLA_OS_WIN | #ifndef CARLA_OS_WIN | ||||
| // start with "wine" if needed | // start with "wine" if needed | ||||
| if (fBinary.endsWith(".exe")) | |||||
| if (fBinary.endsWithIgnoreCase(".exe")) | |||||
| arguments.add("wine"); | arguments.add("wine"); | ||||
| #endif | #endif | ||||
| // binary | // 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<juce::int64>(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<juce::int64>(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 | #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: | private: | ||||
| CarlaEngine* const kEngine; | CarlaEngine* const kEngine; | ||||
| CarlaPlugin* const kPlugin; | CarlaPlugin* const kPlugin; | ||||
| CarlaString fBinary; | |||||
| CarlaString fLabel; | |||||
| CarlaString fShmIds; | |||||
| PluginType fPluginType; | |||||
| String fBinary; | |||||
| String fLabel; | |||||
| String fShmIds; | |||||
| ScopedPointer<ChildProcess> fProcess; | ScopedPointer<ChildProcess> fProcess; | ||||
| @@ -720,14 +865,14 @@ public: | |||||
| void getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override | 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); | std::strncpy(strBuf, fParams[parameterId].name.buffer(), STR_MAX); | ||||
| } | } | ||||
| void getParameterUnit(const uint32_t parameterId, char* const strBuf) const noexcept override | 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); | std::strncpy(strBuf, fParams[parameterId].unit.buffer(), STR_MAX); | ||||
| } | } | ||||
| @@ -886,28 +1031,44 @@ public: | |||||
| CarlaPlugin::setMidiProgram(index, sendGui, sendOsc, sendCallback); | 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 | 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<uint32_t>(std::strlen(type))); | |||||
| const uint32_t keyLen(static_cast<uint32_t>(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<uint32_t>(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); | CarlaPlugin::setCustomData(type, key, value, sendGui); | ||||
| } | } | ||||
| #endif | |||||
| void setChunkData(const void* const data, const std::size_t dataSize) override | 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(data != nullptr,); | ||||
| CARLA_SAFE_ASSERT_RETURN(dataSize > 0,); | 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,); | CARLA_SAFE_ASSERT_RETURN(dataBase64.length() > 0,); | ||||
| String filePath(File::getSpecialLocation(File::tempDirectory).getFullPathName()); | 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; | filePath += fShmAudioPool.filename.buffer() + 18; | ||||
| if (File(filePath).replaceWithText(dataBase64.buffer())) | if (File(filePath).replaceWithText(dataBase64.buffer())) | ||||
| @@ -1047,6 +1207,7 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| portName += "input"; | portName += "input"; | ||||
| portName.truncate(portNameSize); | portName.truncate(portNameSize); | ||||
| pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true); | pData->audioIn.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true); | ||||
| @@ -1071,6 +1232,7 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| portName += "output"; | portName += "output"; | ||||
| portName.truncate(portNameSize); | portName.truncate(portNameSize); | ||||
| pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false); | pData->audioOut.ports[j].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false); | ||||
| @@ -1996,6 +2158,8 @@ public: | |||||
| case kPluginBridgeNonRtServerSetCustomData: { | case kPluginBridgeNonRtServerSetCustomData: { | ||||
| // uint/size, str[], uint/size, str[], uint/size, str[] (compressed) | // uint/size, str[], uint/size, str[], uint/size, str[] (compressed) | ||||
| using namespace juce; | |||||
| // type | // type | ||||
| const uint32_t typeSize(fShmNonRtServerControl.readUInt()); | const uint32_t typeSize(fShmNonRtServerControl.readUInt()); | ||||
| char type[typeSize+1]; | char type[typeSize+1]; | ||||
| @@ -2009,12 +2173,17 @@ public: | |||||
| fShmNonRtServerControl.readCustomData(key, keySize); | fShmNonRtServerControl.readCustomData(key, keySize); | ||||
| // value | // 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<uint8_t> 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; | } break; | ||||
| case kPluginBridgeNonRtServerSetChunkDataFile: { | case kPluginBridgeNonRtServerSetChunkDataFile: { | ||||
| @@ -2192,7 +2361,7 @@ public: | |||||
| std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); | std::strncpy(shmIdsStr+6*2, &fShmNonRtClientControl.filename[fShmNonRtClientControl.filename.length()-6], 6); | ||||
| std::strncpy(shmIdsStr+6*3, &fShmNonRtServerControl.filename[fShmNonRtServerControl.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(); | fBridgeThread.startThread(); | ||||
| } | } | ||||
| @@ -19,7 +19,7 @@ | |||||
| // TODO: common laterncy code | // TODO: common laterncy code | ||||
| #include "CarlaPluginInternal.hpp" | #include "CarlaPluginInternal.hpp" | ||||
| #include "CarlaEngine.hpp" | |||||
| #include "CarlaEngineUtils.hpp" | |||||
| #include "CarlaDssiUtils.hpp" | #include "CarlaDssiUtils.hpp" | ||||
| #include "CarlaMathUtils.hpp" | #include "CarlaMathUtils.hpp" | ||||
| @@ -111,14 +111,6 @@ public: | |||||
| return; | 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 name(kPlugin->getName()); | ||||
| String filename(kPlugin->getFilename()); | String filename(kPlugin->getFilename()); | ||||
| @@ -145,9 +137,28 @@ public: | |||||
| // ui-title | // ui-title | ||||
| arguments.add(name + String(" (GUI)")); | 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!"); | carla_stdout("failed!"); | ||||
| fProcess = nullptr; | fProcess = nullptr; | ||||
| @@ -16,373 +16,8 @@ | |||||
| */ | */ | ||||
| #if 0 | #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<juce::int64>(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<int>(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); | //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; | |||||
| } | } | ||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| @@ -56,8 +56,8 @@ enum PluginBridgeNonRtClientOpcode { | |||||
| kPluginBridgeNonRtClientSetParameterMidiCC, // uint, short | kPluginBridgeNonRtClientSetParameterMidiCC, // uint, short | ||||
| kPluginBridgeNonRtClientSetProgram, // int | kPluginBridgeNonRtClientSetProgram, // int | ||||
| kPluginBridgeNonRtClientSetMidiProgram, // 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 | kPluginBridgeNonRtClientSetCtrlChannel, // short | ||||
| kPluginBridgeNonRtClientSetOption, // uint/option, bool | kPluginBridgeNonRtClientSetOption, // uint/option, bool | ||||
| kPluginBridgeNonRtClientPrepareForSave, | kPluginBridgeNonRtClientPrepareForSave, | ||||
| @@ -92,8 +92,8 @@ enum PluginBridgeNonRtServerOpcode { | |||||
| kPluginBridgeNonRtServerCurrentMidiProgram, // int/index | kPluginBridgeNonRtServerCurrentMidiProgram, // int/index | ||||
| kPluginBridgeNonRtServerProgramName, // uint/index, uint/size, str[] (name) | kPluginBridgeNonRtServerProgramName, // uint/index, uint/size, str[] (name) | ||||
| kPluginBridgeNonRtServerMidiProgramData, // uint/index, uint/bank, uint/program, 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 | kPluginBridgeNonRtServerSetLatency, // uint | ||||
| kPluginBridgeNonRtServerReady, | kPluginBridgeNonRtServerReady, | ||||
| kPluginBridgeNonRtServerSaved, | kPluginBridgeNonRtServerSaved, | ||||
| @@ -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 | CARLA_BACKEND_END_NAMESPACE | ||||
| @@ -252,7 +252,7 @@ shm_t carla_shm_create_temp(char* const fileBase) noexcept | |||||
| const std::size_t fileBaseLen(std::strlen(fileBase)); | const std::size_t fileBaseLen(std::strlen(fileBase)); | ||||
| CARLA_SAFE_ASSERT_RETURN(fileBaseLen > 6, gNullCarlaShm); | 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 | // character set to use randomly | ||||
| static const char charSet[] = "abcdefghijklmnopqrstuvwxyz" | static const char charSet[] = "abcdefghijklmnopqrstuvwxyz" | ||||