Browse Source

NSM support for JACK Applications (#829)

Signed-off-by: falkTX <falktx@gmail.com>
tags/v2.1-alpha2
Filipe Coelho GitHub 5 years ago
parent
commit
e2a2c45f22
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 627 additions and 88 deletions
  1. +118
    -32
      resources/ui/carla_add_jack.ui
  2. +14
    -2
      source/backend/CarlaEngine.hpp
  3. +5
    -0
      source/backend/CarlaHost.h
  4. +11
    -2
      source/backend/CarlaStandalone.cpp
  5. +29
    -3
      source/backend/engine/CarlaEngine.cpp
  6. +1
    -0
      source/backend/engine/CarlaEngineInternal.cpp
  7. +1
    -0
      source/backend/engine/CarlaEngineInternal.hpp
  8. +6
    -2
      source/backend/engine/CarlaEngineNative.cpp
  9. +319
    -19
      source/backend/plugin/CarlaPluginJack.cpp
  10. +1
    -1
      source/bridges-plugin/CarlaBridgeSingleLV2.cpp
  11. +18
    -1
      source/frontend/carla_backend.py
  12. +23
    -8
      source/frontend/carla_database.py
  13. +1
    -0
      source/frontend/carla_host.py
  14. +2
    -2
      source/libjack/libjack.cpp
  15. +4
    -0
      source/libjack/libjack.hpp
  16. +19
    -14
      source/libjack/libjack_port-searching.cpp
  17. +39
    -2
      source/libjack/libjack_ports.cpp
  18. +15
    -0
      source/modules/water/threads/ChildProcess.cpp
  19. +1
    -0
      source/modules/water/threads/ChildProcess.h

+ 118
- 32
resources/ui/carla_add_jack.ui View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>471</width>
<height>369</height>
<width>496</width>
<height>438</height>
</rect>
</property>
<property name="windowTitle">
@@ -27,39 +27,23 @@
<string>Application</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Command:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="le_command"/>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_3">
<item row="0" column="0" rowspan="3">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Ignored</enum>
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>87</width>
<height>1</height>
<width>20</width>
<height>60</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="le_name"/>
</item>
<item row="0" column="0">
<item row="0" column="1">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Name:</string>
@@ -69,22 +53,124 @@
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer_4">
<item row="0" column="2" colspan="2">
<widget class="QLineEdit" name="le_name"/>
</item>
<item row="0" column="4" rowspan="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Ignored</enum>
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>1</height>
<width>20</width>
<height>60</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Application:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QRadioButton" name="rb_template">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>From template</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QRadioButton" name="rb_custom">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Custom</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="page_template">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="l_template">
<property name="text">
<string>Template:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cb_template">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_command">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="l_command">
<property name="text">
<string>Command:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_command"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
@@ -122,7 +208,7 @@
<item>
<widget class="QComboBox" name="cb_session_mgr">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<item>
<property name="text">
@@ -131,12 +217,12 @@
</item>
<item>
<property name="text">
<string>Auto</string>
<string>LADISH (SIGUSR1)</string>
</property>
</item>
<item>
<property name="text">
<string>LADISH (SIGUSR1)</string>
<string>NSM</string>
</property>
</item>
</widget>


+ 14
- 2
source/backend/CarlaEngine.hpp View File

@@ -895,12 +895,24 @@ public:
* Load a project file.
* @note Already loaded plugins are not removed; call removeAllPlugins() first if needed.
*/
bool loadProject(const char* const filename);
bool loadProject(const char* const filename, const bool setAsCurrentProject);

/*!
* Save current project to a file.
*/
bool saveProject(const char* const filename);
bool saveProject(const char* const filename, const bool setAsCurrentProject);

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
/*!
* Get the currently set project filename.
*/
const char* getCurrentProjectFilename() const noexcept;

/*!
* Clear the currently set project filename.
*/
void clearCurrentProjectFilename() noexcept;
#endif

// -------------------------------------------------------------------
// Information (base)


+ 5
- 0
source/backend/CarlaHost.h View File

@@ -410,6 +410,11 @@ CARLA_EXPORT bool carla_load_project(const char* filename);
CARLA_EXPORT bool carla_save_project(const char* filename);

#ifndef BUILD_BRIDGE
/*!
* Clear the currently set project filename.
*/
CARLA_EXPORT void carla_clear_project_filename();

/*!
* Connect two patchbay ports.
* @param groupIdA Output group


+ 11
- 2
source/backend/CarlaStandalone.cpp View File

@@ -812,7 +812,7 @@ bool carla_load_project(const char* filename)

carla_debug("carla_load_project(\"%s\")", filename);

return gStandalone.engine->loadProject(filename);
return gStandalone.engine->loadProject(filename, true);
}

bool carla_save_project(const char* filename)
@@ -822,10 +822,19 @@ bool carla_save_project(const char* filename)

carla_debug("carla_save_project(\"%s\")", filename);

return gStandalone.engine->saveProject(filename);
return gStandalone.engine->saveProject(filename, true);
}

#ifndef BUILD_BRIDGE
void carla_clear_project_filename()
{
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,);

carla_debug("carla_clear_project_filename()");

gStandalone.engine->clearCurrentProjectFilename();
}

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

bool carla_patchbay_connect(uint groupIdA, uint portIdA, uint groupIdB, uint portIdB)


+ 29
- 3
source/backend/engine/CarlaEngine.cpp View File

@@ -990,7 +990,7 @@ bool CarlaEngine::loadFile(const char* const filename)
// NOTE: please keep in sync with carla_get_supported_file_extensions!!

if (extension == "carxp" || extension == "carxs")
return loadProject(filename);
return loadProject(filename, false);

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

@@ -1126,7 +1126,7 @@ bool CarlaEngine::loadFile(const char* const filename)
return false;
}

bool CarlaEngine::loadProject(const char* const filename)
bool CarlaEngine::loadProject(const char* const filename, const bool setAsCurrentProject)
{
CARLA_SAFE_ASSERT_RETURN_ERR(pData->isIdling == 0, "An operation is still being processed, please wait for it to finish");
CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename");
@@ -1136,11 +1136,18 @@ bool CarlaEngine::loadProject(const char* const filename)
File file(jfilename);
CARLA_SAFE_ASSERT_RETURN_ERR(file.existsAsFile(), "Requested file does not exist or is not a readable file");

if (setAsCurrentProject)
{
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->currentProjectFilename = filename;
#endif
}

XmlDocument xml(file);
return loadProjectInternal(xml);
}

bool CarlaEngine::saveProject(const char* const filename)
bool CarlaEngine::saveProject(const char* const filename, const bool setAsCurrentProject)
{
CARLA_SAFE_ASSERT_RETURN_ERR(filename != nullptr && filename[0] != '\0', "Invalid filename");
carla_debug("CarlaEngine::saveProject(\"%s\")", filename);
@@ -1151,6 +1158,13 @@ bool CarlaEngine::saveProject(const char* const filename)
const String jfilename = String(CharPointer_UTF8(filename));
File file(jfilename);

if (setAsCurrentProject)
{
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
pData->currentProjectFilename = filename;
#endif
}

if (file.replaceWithData(out.getData(), out.getDataSize()))
return true;

@@ -1158,6 +1172,18 @@ bool CarlaEngine::saveProject(const char* const filename)
return false;
}

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
const char* CarlaEngine::getCurrentProjectFilename() const noexcept
{
return pData->currentProjectFilename;
}

void CarlaEngine::clearCurrentProjectFilename() noexcept
{
pData->currentProjectFilename.clear();
}
#endif

// -----------------------------------------------------------------------
// Information (base)



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

@@ -375,6 +375,7 @@ CarlaEngine::ProtectedData::ProtectedData(CarlaEngine* const engine) noexcept
actionCanceled(false),
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
loadingProject(false),
currentProjectFilename(),
#endif
hints(0x0),
bufferSize(0),


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

@@ -223,6 +223,7 @@ struct CarlaEngine::ProtectedData {

#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
bool loadingProject;
CarlaString currentProjectFilename;
#endif

uint hints;


+ 6
- 2
source/backend/engine/CarlaEngineNative.cpp View File

@@ -175,7 +175,7 @@ protected:
CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true);

try {
ok = fEngine->loadProject(filename);
ok = fEngine->loadProject(filename, true);
} CARLA_SAFE_EXCEPTION("loadProject");

delete[] filename;
@@ -187,11 +187,15 @@ protected:
CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(filename), true);

try {
ok = fEngine->saveProject(filename);
ok = fEngine->saveProject(filename, true);
} CARLA_SAFE_EXCEPTION("saveProject");

delete[] filename;
}
else if (std::strcmp(msg, "clear_project_filename") == 0)
{
fEngine->clearCurrentProjectFilename();
}
else if (std::strcmp(msg, "patchbay_connect") == 0)
{
uint32_t groupA, portA, groupB, portB;


+ 319
- 19
source/backend/plugin/CarlaPluginJack.cpp View File

@@ -1,6 +1,6 @@
/*
* Carla Plugin JACK
* Copyright (C) 2016-2018 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2016-2019 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -28,6 +28,11 @@
#include "CarlaShmUtils.hpp"
#include "CarlaThread.hpp"

#ifdef HAVE_LIBLO
# include "CarlaOscUtils.hpp"
#endif

#include "water/files/File.h"
#include "water/misc/Time.h"
#include "water/text/StringArray.h"
#include "water/threads/ChildProcess.h"
@@ -47,6 +52,28 @@ using water::Time;

CARLA_BACKEND_START_NAMESPACE

enum {
FLAG_CONTROL_WINDOW = 0x01,
FLAG_CAPTURE_FIRST_WINDOW = 0x02,
FLAG_BUFFERS_ADDITION_MODE = 0x10,
};

enum {
SESSION_MGR_NONE = 0,
SESSION_MGR_AUTO = 1,
SESSION_MGR_JACK = 2,
SESSION_MGR_LADISH = 3,
SESSION_MGR_NSM = 4,
};

static size_t safe_rand(const size_t limit)
{
const int r = std::rand();
CARLA_SAFE_ASSERT_RETURN(r >= 0, 0);

return static_cast<uint>(r) % limit;
}

// -------------------------------------------------------------------------------------------------------------------
// Fallback data

@@ -62,17 +89,22 @@ public:
kEngine(engine),
kPlugin(plugin),
fShmIds(),
fNumPorts(),
fSetupLabel(),
#ifdef HAVE_LIBLO
fOscClientAddress(nullptr),
fOscServer(nullptr),
fProject(),
#endif
fProcess() {}

void setData(const char* const shmIds, const char* const numPorts) noexcept
void setData(const char* const shmIds, const char* const setupLabel) noexcept
{
CARLA_SAFE_ASSERT_RETURN(shmIds != nullptr && shmIds[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(numPorts != nullptr && numPorts[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(setupLabel != nullptr && setupLabel[0] != '\0',);
CARLA_SAFE_ASSERT(! isThreadRunning());

fShmIds = shmIds;
fNumPorts = numPorts;
fShmIds = shmIds;
fSetupLabel = setupLabel;
}

uintptr_t getProcessID() const noexcept
@@ -82,9 +114,156 @@ public:
return (uintptr_t)fProcess->getPID();
}

void sendTerminate() const noexcept
{
CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr,);

fProcess->terminate();
}

#ifdef HAVE_LIBLO
void nsmSave(const char* const setupLabel)
{
if (fOscClientAddress == nullptr)
return;

if (fSetupLabel != setupLabel)
fSetupLabel = setupLabel;

maybeOpenFirstTime();

lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/save", "");
}

void nsmShowGui(const bool yesNo)
{
if (fOscClientAddress == nullptr)
return;

lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE,
yesNo ? "/nsm/client/show_optional_gui"
: "/nsm/client/hide_optional_gui", "");
}
#endif

protected:
#ifdef HAVE_LIBLO
static void _osc_error_handler(int num, const char* msg, const char* path)
{
carla_stderr2("CarlaPluginJackThread::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
}

static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{
CARLA_SAFE_ASSERT_RETURN(data != nullptr, 0);
carla_stdout("CarlaPluginJackThread::_broadcast_handler(%s, %s, %p, %i)", path, types, argv, argc);

return ((CarlaPluginJackThread*)data)->handleBroadcast(path, types, argv, msg);
}

void maybeOpenFirstTime()
{
if (fProject.path.isNotEmpty())
return;
if (fSetupLabel.length() <= 6)
return;

if (fProject.init(kEngine->getCurrentProjectFilename(), &fSetupLabel[6]))
{
carla_stdout("Sending first open signal %s %s %s",
fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer());

lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/open", "sss",
fProject.path.buffer(), fProject.display.buffer(), fProject.clientName.buffer());
}
}

int handleBroadcast(const char* path, const char* types, lo_arg** argv, lo_message msg)
{
if (std::strcmp(path, "/nsm/server/announce") == 0)
{
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sssiii") == 0, 0);

const lo_address msgAddress(lo_message_get_source(msg));
CARLA_SAFE_ASSERT_RETURN(msgAddress != nullptr, 0);

char* const msgURL(lo_address_get_url(msgAddress));
CARLA_SAFE_ASSERT_RETURN(msgURL != nullptr, 0);

if (fOscClientAddress != nullptr)
lo_address_free(fOscClientAddress);

fOscClientAddress = lo_address_new_from_url(msgURL);
CARLA_SAFE_ASSERT_RETURN(fOscClientAddress != nullptr, 0);

fProject.appName = &argv[0]->s;

static const char* const method = "/nsm/server/announce";
static const char* const message = "Howdy, what took you so long?";
static const char* const smName = "Carla";
static const char* const features = ":server-control:optional-gui:";

lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ssss",
method, message, smName, features);

maybeOpenFirstTime();
}

else if (std::strcmp(path, "/reply") == 0)
{
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ss") == 0, 0);

const char* const method = &argv[0]->s;
const char* const message = &argv[1]->s;

carla_stdout("Got reply of '%s' as '%s'", method, message);

if (std::strcmp(method, "/nsm/client/open") == 0)
{
carla_stdout("Sending 'Session is loaded' to %s", fProject.appName.buffer());
lo_send_from(fOscClientAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/session_is_loaded", "");
}
}

else if (std::strcmp(path, "/nsm/client/gui_is_shown") == 0)
{
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0);

kEngine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, kPlugin->getId(), 1, 0, 0.0f, nullptr);
}

else if (std::strcmp(path, "/nsm/client/gui_is_hidden") == 0)
{
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "") == 0, 0);

kEngine->callback(ENGINE_CALLBACK_UI_STATE_CHANGED, kPlugin->getId(), 0, 0, 0.0f, nullptr);
}

return 0;
}
#endif

void run()
{
#ifdef HAVE_LIBLO
if (fOscClientAddress != nullptr)
{
lo_address_free(fOscClientAddress);
fOscClientAddress = nullptr;
}

const int sessionManager = fSetupLabel[4] - '0';

if (sessionManager == SESSION_MGR_NSM)
{
// NSM support
fOscServer = lo_server_new_with_proto(nullptr, LO_UDP, _osc_error_handler);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr,);

lo_server_add_method(fOscServer, nullptr, nullptr, _broadcast_handler, this);
}
#endif

if (fProcess == nullptr)
{
fProcess = new ChildProcess();
@@ -127,6 +306,7 @@ protected:

const ScopedEngineEnvironmentLocker _seel(kEngine);

const ScopedEnvVar sev3("NSM_URL", lo_server_get_url(fOscServer));
const ScopedEnvVar sev2("LD_LIBRARY_PATH", libjackdir.buffer());
const ScopedEnvVar sev1("LD_PRELOAD", ldpreload.isNotEmpty() ? ldpreload.buffer() : nullptr);

@@ -135,7 +315,7 @@ protected:
else
carla_unsetenv("CARLA_FRONTEND_WIN_ID");

carla_setenv("CARLA_LIBJACK_SETUP", fNumPorts.buffer());
carla_setenv("CARLA_LIBJACK_SETUP", fSetupLabel.buffer());
carla_setenv("CARLA_SHM_IDS", fShmIds.buffer());

started = fProcess->start(arguments);
@@ -149,7 +329,32 @@ protected:
}

for (; fProcess->isRunning() && ! shouldThreadExit();)
carla_msleep(50);
{
#ifdef HAVE_LIBLO
if (sessionManager == SESSION_MGR_NSM)
{
lo_server_recv_noblock(fOscServer, 50);
}
else
#endif
{
carla_msleep(50);
}
}

#ifdef HAVE_LIBLO
if (sessionManager == SESSION_MGR_NSM)
{
lo_server_free(fOscServer);
fOscServer = nullptr;

if (fOscClientAddress != nullptr)
{
lo_address_free(fOscClientAddress);
fOscClientAddress = nullptr;
}
}
#endif

// we only get here if bridge crashed or thread asked to exit
if (fProcess->isRunning() && shouldThreadExit())
@@ -184,7 +389,42 @@ private:
CarlaPlugin* const kPlugin;

CarlaString fShmIds;
CarlaString fNumPorts;
CarlaString fSetupLabel;

#ifdef HAVE_LIBLO
lo_address fOscClientAddress;
lo_server fOscServer;

struct ProjectData {
CarlaString appName;
CarlaString path;
CarlaString display;
CarlaString clientName;

ProjectData()
: appName(),
path(),
display(),
clientName() {}

bool init(const char* const engineProjectFilename, const char* const uniqueCodeID)
{
CARLA_SAFE_ASSERT_RETURN(engineProjectFilename != nullptr && engineProjectFilename[0] != '\0', false);
CARLA_SAFE_ASSERT_RETURN(uniqueCodeID != nullptr && uniqueCodeID[0] != '\0', false);
CARLA_SAFE_ASSERT_RETURN(appName.isNotEmpty(), false);

const File file(File(engineProjectFilename).withFileExtension(uniqueCodeID));

path = file.getFullPathName().toRawUTF8();
display = file.getFileNameWithoutExtension().toRawUTF8();
clientName = appName + "." + uniqueCodeID;

return true;
}

CARLA_DECLARE_NON_COPY_STRUCT(ProjectData)
} fProject;
#endif

ScopedPointer<ChildProcess> fProcess;

@@ -247,6 +487,8 @@ public:
fShmNonRtClientControl.writeOpcode(kPluginBridgeNonRtClientQuit);
fShmNonRtClientControl.commitWrite();

fBridgeThread.sendTerminate();

if (! fTimedOut)
waitForClient("stopping", 3000);
}
@@ -326,6 +568,13 @@ public:

void prepareForSave() noexcept override
{
#ifdef HAVE_LIBLO
if (fInfo.setupLabel.length() == 6)
setupUniqueProjectID();

fBridgeThread.nsmSave(fInfo.setupLabel);
#endif

{
const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);

@@ -402,6 +651,10 @@ public:
CARLA_SAFE_ASSERT_RETURN(restartBridgeThread(),);
}

#ifdef HAVE_LIBLO
fBridgeThread.nsmShowGui(yesNo);
#endif

const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex);

fShmNonRtClientControl.writeOpcode(yesNo ? kPluginBridgeNonRtClientShowUI : kPluginBridgeNonRtClientHideUI);
@@ -1236,7 +1489,7 @@ public:
// ---------------------------------------------------------------
// check setup

if (std::strlen(label) != 6)
if (std::strlen(label) < 6)
{
pData->engine->setLastError("invalid application setup received");
return false;
@@ -1256,7 +1509,11 @@ public:

fInfo.setupLabel = label;

const int setupHints = label[5] - '0';
// ---------------------------------------------------------------
// set project unique id

if (label[6] == '\0')
setupUniqueProjectID();

// ---------------------------------------------------------------
// set info
@@ -1306,6 +1563,8 @@ public:
// ---------------------------------------------------------------
// setup hints and options

const int setupHints = label[5] - '0';

// FIXME dryWet broken
pData->hints = PLUGIN_IS_BRIDGE | PLUGIN_OPTION_FIXED_BUFFERS;
#ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
@@ -1313,7 +1572,7 @@ public:
#endif
//fInfo.optionsAvailable = optionAv;

if (setupHints & 0x1)
if (setupHints & FLAG_CONTROL_WINDOW)
pData->hints |= PLUGIN_HAS_CUSTOM_UI;

// ---------------------------------------------------------------
@@ -1328,7 +1587,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(shmIdsStr, label);
fBridgeThread.setData(shmIdsStr, fInfo.setupLabel);
}

if (! restartBridgeThread())
@@ -1415,16 +1674,45 @@ private:
waitForClient("resize-pool", 5000);
}

void waitForClient(const char* const action, const uint msecs)
void setupUniqueProjectID()
{
CARLA_SAFE_ASSERT_RETURN(! fTimedOut,);
CARLA_SAFE_ASSERT_RETURN(! fTimedError,);
const char* const engineProjectFilename = pData->engine->getCurrentProjectFilename();
carla_stdout("setupUniqueProjectID %s", engineProjectFilename);

if (fShmRtClientControl.waitForClient(msecs))
if (engineProjectFilename == nullptr || engineProjectFilename[0] == '\0')
return;

fTimedOut = true;
carla_stderr2("waitForClient(%s) timed out", action);
const File file(engineProjectFilename);
CARLA_SAFE_ASSERT_RETURN(file.existsAsFile(),);
CARLA_SAFE_ASSERT_RETURN(file.getFileExtension().isNotEmpty(),);

char code[6];
code[5] = '\0';

for (;;)
{
static const char* const kValidChars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789";

static const size_t kValidCharsLen(std::strlen(kValidChars)-1U);

code[0] = kValidChars[safe_rand(kValidCharsLen)];
code[1] = kValidChars[safe_rand(kValidCharsLen)];
code[2] = kValidChars[safe_rand(kValidCharsLen)];
code[3] = kValidChars[safe_rand(kValidCharsLen)];
code[4] = kValidChars[safe_rand(kValidCharsLen)];

const File newFile(file.withFileExtension(code));

if (newFile.existsAsFile())
continue;

fInfo.setupLabel += code;
carla_stdout("new label %s", fInfo.setupLabel.buffer());
break;
}
}

bool restartBridgeThread()
@@ -1515,6 +1803,18 @@ private:
return true;
}

void waitForClient(const char* const action, const uint msecs)
{
CARLA_SAFE_ASSERT_RETURN(! fTimedOut,);
CARLA_SAFE_ASSERT_RETURN(! fTimedError,);

if (fShmRtClientControl.waitForClient(msecs))
return;

fTimedOut = true;
carla_stderr2("waitForClient(%s) timed out", action);
}

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginJack)
};



+ 1
- 1
source/bridges-plugin/CarlaBridgeSingleLV2.cpp View File

@@ -93,7 +93,7 @@ public:
using water::File;
const File pluginFile(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("xml"));

if (! loadProject(pluginFile.getFullPathName().toRawUTF8()))
if (! loadProject(pluginFile.getFullPathName().toRawUTF8(), true))
{
carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError());
return;


+ 18
- 1
source/frontend/carla_backend.py View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-

# Carla Backend code
# Copyright (C) 2011-2018 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -1386,6 +1386,11 @@ class CarlaHostMeta(object):
def save_project(self, filename):
raise NotImplementedError

# Clear the currently set project filename.
@abstractmethod
def clear_project_filename(self):
raise NotImplementedError

# Connect two patchbay ports.
# @param groupIdA Output group
# @param portIdA Output port
@@ -1968,6 +1973,9 @@ class CarlaHostNull(CarlaHostMeta):
def save_project(self, filename):
return False

def clear_project_filename(self):
return

def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
return False

@@ -2256,6 +2264,9 @@ class CarlaHostDLL(CarlaHostMeta):
self.lib.carla_save_project.argtypes = [c_char_p]
self.lib.carla_save_project.restype = c_bool

self.lib.carla_clear_project_filename.argtypes = None
self.lib.carla_clear_project_filename.restype = None

self.lib.carla_patchbay_connect.argtypes = [c_uint, c_uint, c_uint, c_uint]
self.lib.carla_patchbay_connect.restype = c_bool

@@ -2536,6 +2547,9 @@ class CarlaHostDLL(CarlaHostMeta):
def save_project(self, filename):
return bool(self.lib.carla_save_project(filename.encode("utf-8")))

def clear_project_filename(self):
self.lib.carla_clear_project_filename()

def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
return bool(self.lib.carla_patchbay_connect(groupIdA, portIdA, groupIdB, portIdB))

@@ -2879,6 +2893,9 @@ class CarlaHostPlugin(CarlaHostMeta):
def save_project(self, filename):
return self.sendMsgAndSetError(["save_project", filename])

def clear_project_filename(self):
return self.sendMsgAndSetError(["clear_project_filename"])

def patchbay_connect(self, groupIdA, portIdA, groupIdB, portIdB):
return self.sendMsgAndSetError(["patchbay_connect", groupIdA, portIdA, groupIdB, portIdB])



+ 23
- 8
source/frontend/carla_database.py View File

@@ -1934,6 +1934,10 @@ class JackApplicationW(QDialog):
SESSION_MGR_LADISH = 3
SESSION_MGR_NSM = 4

UI_SESSION_NONE = 0
UI_SESSION_LADISH = 1
UI_SESSION_NSM = 2

FLAG_CONTROL_WINDOW = 0x01
FLAG_CAPTURE_FIRST_WINDOW = 0x02
FLAG_BUFFERS_ADDITION_MODE = 0x10
@@ -1959,6 +1963,7 @@ class JackApplicationW(QDialog):
# Set-up connections

self.finished.connect(self.slot_saveSettings)
self.ui.cb_session_mgr.currentIndexChanged.connect(self.slot_sessionManagerChanged)
self.ui.le_command.textChanged.connect(self.slot_commandChanged)

# ------------------------------------------------------------------------------------------------------------------
@@ -1970,16 +1975,13 @@ class JackApplicationW(QDialog):
flags = 0x0

if not name:
name = os.path.basename(command.split(" ",1)[0])
name = os.path.basename(command.split(" ",1)[0]).title()

# TODO finalize flag definitions
uiSessionMgrIndex = self.ui.cb_session_mgr.currentIndex()
if uiSessionMgrIndex == 1:
smgr = self.SESSION_MGR_AUTO
elif uiSessionMgrIndex == 2:
if uiSessionMgrIndex == self.UI_SESSION_LADISH:
smgr = self.SESSION_MGR_LADISH
#elif uiSessionMgrIndex == 2:
#smgr = self.SESSION_MGR_NSM
elif uiSessionMgrIndex == self.UI_SESSION_NSM:
smgr = self.SESSION_MGR_NSM

if self.ui.cb_manage_window.isChecked():
flags |= self.FLAG_CONTROL_WINDOW
@@ -1997,6 +1999,15 @@ class JackApplicationW(QDialog):
chr(baseIntVal+flags))
return (command, name, labelSetup)

def checkIfButtonBoxShouldBeEnabled(self, index, text):
enabled = len(text) > 0

# NSM applications must not be abstract or absolute paths, and must not contain arguments
if enabled and index == self.UI_SESSION_NSM:
enabled = text[0] not in (".", "/") and " " not in text

self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled)

def loadSettings(self):
settings = QSettings("falkTX", "CarlaAddJackApp")

@@ -2015,7 +2026,11 @@ class JackApplicationW(QDialog):

@pyqtSlot(str)
def slot_commandChanged(self, text):
self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(len(text) > 0)
self.checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(), text)

@pyqtSlot(int)
def slot_sessionManagerChanged(self, index):
self.checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command.text())

@pyqtSlot()
def slot_saveSettings(self):


+ 1
- 0
source/frontend/carla_host.py View File

@@ -733,6 +733,7 @@ class HostWindow(QMainWindow):
self.pluginRemoveAll()
self.fProjectFilename = ""
self.setProperWindowTitle()
self.host.clear_project_filename()

@pyqtSlot()
def slot_fileOpen(self):


+ 2
- 2
source/libjack/libjack.cpp View File

@@ -133,7 +133,7 @@ public:
CARLA_SAFE_ASSERT_INT2_RETURN(shmIds != nullptr && std::strlen(shmIds) == 6*4, std::strlen(shmIds), 6*4,);

const char* const libjackSetup(std::getenv("CARLA_LIBJACK_SETUP"));
CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) == 6,);
CARLA_SAFE_ASSERT_RETURN(libjackSetup != nullptr && std::strlen(libjackSetup) >= 6,);

// make sure we don't get loaded again
carla_unsetenv("CARLA_SHM_IDS");
@@ -943,7 +943,7 @@ bool CarlaJackAppClient::handleNonRtData()

case kPluginBridgeNonRtClientPrepareForSave:
{
if (fSessionManager == 1) // auto
if (fSessionManager == 1 && std::getenv("NSM_URL") == nullptr) // auto
{
struct sigaction sig;
carla_zeroStruct(sig);


+ 4
- 0
source/libjack/libjack.hpp View File

@@ -37,6 +37,7 @@
#endif

#include <cerrno>
#include <map>

#ifdef __SSE2_MATH__
# include <xmmintrin.h>
@@ -164,6 +165,8 @@ struct JackClientState {
LinkedList<JackPortState*> midiIns;
LinkedList<JackPortState*> midiOuts;

std::map<std::string, JackPortState*> portNameMapping;

JackShutdownCallback shutdownCb;
void* shutdownCbPtr;

@@ -195,6 +198,7 @@ struct JackClientState {
audioOuts(),
midiIns(),
midiOuts(),
portNameMapping(),
shutdownCb(nullptr),
shutdownCbPtr(nullptr),
infoShutdownCb(nullptr),


+ 19
- 14
source/libjack/libjack_port-searching.cpp View File

@@ -104,26 +104,26 @@ const char** jack_get_ports(jack_client_t* client, const char* a, const char* b,
CARLA_EXPORT
jack_port_t* jack_port_by_name(jack_client_t* client, const char* name)
{
carla_stdout("%s(%p, %s) WIP", __FUNCTION__, client, name);
carla_debug("%s(%p, %s)", __FUNCTION__, client, name);

JackClientState* const jclient = (JackClientState*)client;
CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, 0);

const JackServerState& jserver(jclient->server);
const int commonFlags = JackPortIsPhysical|JackPortIsTerminal;

static JackPortState retPort(
/* name */ nullptr,
/* fullname */ nullptr,
/* index */ 0,
/* flags */ 0x0,
/* isMidi */ false,
/* isSystem */ true,
/* isConnected */ false
);

if (std::strncmp(name, "system:", 7) == 0)
{
static JackPortState retPort(
/* name */ nullptr,
/* fullname */ nullptr,
/* index */ 0,
/* flags */ 0x0,
/* isMidi */ false,
/* isSystem */ true,
/* isConnected */ false
);

const JackServerState& jserver(jclient->server);
const int commonFlags = JackPortIsPhysical|JackPortIsTerminal;

std::free(retPort.fullname);
retPort.fullname = strdup(name);

@@ -188,6 +188,11 @@ jack_port_t* jack_port_by_name(jack_client_t* client, const char* name)

return (jack_port_t*)&retPort;
}
else
{
if (JackPortState* const port = jclient->portNameMapping[name])
return (jack_port_t*)port;
}

carla_stderr2("jack_port_by_name: invalid port name '%s'", name);
return nullptr;


+ 39
- 2
source/libjack/libjack_ports.cpp View File

@@ -48,6 +48,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co
jclient->audioIns.append(port);
}

jclient->portNameMapping[port->fullname] = port;
return (jack_port_t*)port;
}

@@ -62,6 +63,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co
jclient->audioOuts.append(port);
}

jclient->portNameMapping[port->fullname] = port;
return (jack_port_t*)port;
}

@@ -82,6 +84,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co
jclient->midiIns.append(port);
}

jclient->portNameMapping[port->fullname] = port;
return (jack_port_t*)port;
}

@@ -96,6 +99,7 @@ jack_port_t* jack_port_register(jack_client_t* client, const char* port_name, co
jclient->midiOuts.append(port);
}

jclient->portNameMapping[port->fullname] = port;
return (jack_port_t*)port;
}

@@ -130,12 +134,14 @@ int jack_port_unregister(jack_client_t* client, jack_port_t* port)
if (jport->flags & JackPortIsInput)
{
CARLA_SAFE_ASSERT_RETURN(jclient->midiIns.removeOne(jport), ENOENT);
jclient->portNameMapping.erase(jport->fullname);
return 0;
}

if (jport->flags & JackPortIsOutput)
{
CARLA_SAFE_ASSERT_RETURN(jclient->midiOuts.removeOne(jport), ENOENT);
jclient->portNameMapping.erase(jport->fullname);
return 0;
}
}
@@ -144,12 +150,14 @@ int jack_port_unregister(jack_client_t* client, jack_port_t* port)
if (jport->flags & JackPortIsInput)
{
CARLA_SAFE_ASSERT_RETURN(jclient->audioIns.removeOne(jport), ENOENT);
jclient->portNameMapping.erase(jport->fullname);
return 0;
}

if (jport->flags & JackPortIsOutput)
{
CARLA_SAFE_ASSERT_RETURN(jclient->audioOuts.removeOne(jport), ENOENT);
jclient->portNameMapping.erase(jport->fullname);
return 0;
}
}
@@ -325,8 +333,37 @@ int jack_port_set_name(jack_port_t *port, const char *port_name)
CARLA_EXPORT
int jack_port_rename(jack_client_t* client, jack_port_t *port, const char *port_name)
{
carla_stderr2("%s(%p, %p, %s)", __FUNCTION__, client, port, port_name);
return ENOSYS;
carla_stderr2("%s(%p, %p, %s) WIP", __FUNCTION__, client, port, port_name);

JackClientState* const jclient = (JackClientState*)client;
CARLA_SAFE_ASSERT_RETURN(jclient != nullptr, EINVAL);

CARLA_SAFE_ASSERT_RETURN(port_name != nullptr && port_name[0] != '\0', EINVAL);

JackPortState* const jport = (JackPortState*)port;
CARLA_SAFE_ASSERT_RETURN(jport != nullptr, EINVAL);
CARLA_SAFE_ASSERT_RETURN(! jport->isSystem, EINVAL);

// TODO: verify uniqueness

char* const name = strdup(port_name);
CARLA_SAFE_ASSERT_RETURN(name != nullptr, ENOMEM);

char* const fullname = (char*)std::malloc(STR_MAX);
CARLA_SAFE_ASSERT_RETURN(name != nullptr, ENOMEM);

std::free(jport->name);
jport->name = strdup(port_name);

std::snprintf(fullname, STR_MAX, "%s:%s", jclient->name, port_name);
fullname[STR_MAX-1] = '\0';

std::free(jport->fullname);
jport->fullname = fullname;

// TODO: port rename callback

return 0;
}

CARLA_EXPORT


+ 15
- 0
source/modules/water/threads/ChildProcess.cpp View File

@@ -125,6 +125,11 @@ public:
return TerminateProcess (processInfo.hProcess, 0) != FALSE;
}
bool terminateProcess() const noexcept
{
return TerminateProcess (processInfo.hProcess, 0) != FALSE;
}
uint32 getExitCode() const noexcept
{
DWORD exitCode = 0;
@@ -238,6 +243,11 @@ public:
return ::kill (childPID, SIGKILL) == 0;
}
bool terminateProcess() const noexcept
{
return ::kill (childPID, SIGTERM) == 0;
}
uint32 getExitCode() const noexcept
{
if (childPID != 0)
@@ -287,6 +297,11 @@ bool ChildProcess::kill()
return activeProcess == nullptr || activeProcess->killProcess();
}
bool ChildProcess::terminate()
{
return activeProcess == nullptr || activeProcess->terminateProcess();
}
uint32 ChildProcess::getExitCode() const
{
return activeProcess != nullptr ? activeProcess->getExitCode() : 0;


+ 1
- 0
source/modules/water/threads/ChildProcess.h View File

@@ -107,6 +107,7 @@ public:
result in undefined behaviour.
*/
bool kill();
bool terminate();
uint32 getPID() const noexcept;


Loading…
Cancel
Save