Browse Source

More plugin-bridge work (server-side mostly complete now)

tags/1.9.6
falkTX 10 years ago
parent
commit
f4b6b4fd28
10 changed files with 282 additions and 424 deletions
  1. +7
    -0
      source/backend/CarlaEngine.hpp
  2. +10
    -0
      source/backend/engine/CarlaEngine.cpp
  3. +1
    -0
      source/backend/engine/CarlaEngineInternal.cpp
  4. +1
    -0
      source/backend/engine/CarlaEngineInternal.hpp
  5. +212
    -43
      source/backend/plugin/CarlaPluginBridge.cpp
  6. +22
    -11
      source/backend/plugin/CarlaPluginDSSI.cpp
  7. +0
    -365
      source/backend/plugin/CarlaPluginThread.cpp
  8. +4
    -4
      source/utils/CarlaBridgeUtils.hpp
  9. +24
    -0
      source/utils/CarlaEngineUtils.hpp
  10. +1
    -1
      source/utils/CarlaShmUtils.hpp

+ 7
- 0
source/backend/CarlaEngine.hpp View File

@@ -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


+ 10
- 0
source/backend/engine/CarlaEngine.cpp View File

@@ -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




+ 1
- 0
source/backend/engine/CarlaEngineInternal.cpp View File

@@ -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(),


+ 1
- 0
source/backend/engine/CarlaEngineInternal.hpp View File

@@ -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;


+ 212
- 43
source/backend/plugin/CarlaPluginBridge.cpp View File

@@ -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();
} }




+ 22
- 11
source/backend/plugin/CarlaPluginDSSI.cpp View File

@@ -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;


+ 0
- 365
source/backend/plugin/CarlaPluginThread.cpp View File

@@ -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;
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


+ 4
- 4
source/utils/CarlaBridgeUtils.hpp View File

@@ -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,


+ 24
- 0
source/utils/CarlaEngineUtils.hpp View File

@@ -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


+ 1
- 1
source/utils/CarlaShmUtils.hpp View File

@@ -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"


Loading…
Cancel
Save