Browse Source

Put NSM code into its own separate file

tags/1.9.6
falkTX 9 years ago
parent
commit
e72d0c3929
5 changed files with 725 additions and 690 deletions
  1. +0
    -10
      source/backend/CarlaHost.h
  2. +3
    -2
      source/backend/CarlaHostCommon.cpp
  3. +1
    -672
      source/backend/CarlaStandalone.cpp
  4. +712
    -0
      source/backend/CarlaStandaloneNSM.cpp
  5. +9
    -6
      source/backend/Makefile

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

@@ -899,16 +899,6 @@ CARLA_EXPORT const char* carla_get_library_filename();
*/
CARLA_EXPORT const char* carla_get_library_folder();

/*!
* Initialize NSM (announce).
*/
CARLA_EXPORT bool carla_nsm_init(int pid, const char* executableName);

/*!
* Allow NSM callbacks.
*/
CARLA_EXPORT void carla_nsm_ready(int action);

/** @} */

#endif /* CARLA_HOST_H_INCLUDED */

+ 3
- 2
source/backend/CarlaHostCommon.cpp View File

@@ -16,6 +16,7 @@
*/

#include "CarlaHost.h"
#include "CarlaUtils.hpp"

#include "juce_core.h"

@@ -105,7 +106,7 @@ const char* carla_get_library_filename()

if (ret.isEmpty())
{
using juce::File;
using namespace juce;
ret = File(File::getSpecialLocation(File::currentExecutableFile)).getFullPathName().toRawUTF8();
}

@@ -120,7 +121,7 @@ const char* carla_get_library_folder()

if (ret.isEmpty())
{
using juce::File;
using namespace juce;
ret = File(File::getSpecialLocation(File::currentExecutableFile).getParentDirectory()).getFullPathName().toRawUTF8();
}



+ 1
- 672
source/backend/CarlaStandalone.cpp View File

@@ -28,7 +28,6 @@
#include "CarlaBase64Utils.hpp"

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

#include "juce_audio_formats.h"
@@ -80,656 +79,7 @@ struct CarlaBackendStandalone {
CARLA_DECLARE_NON_COPY_STRUCT(CarlaBackendStandalone)
};

static CarlaBackendStandalone gStandalone;

#ifdef HAVE_LIBLO
// -------------------------------------------------------------------------------------------------------------------
// NSM support

#define NSM_API_VERSION_MAJOR 1
#define NSM_API_VERSION_MINOR 2

#define NSM_CLIENT_FEATURES ":switch:"
//#define NSM_CLIENT_FEATURES ":switch:optional-gui:"

class CarlaNSM
{
public:
CarlaNSM() noexcept
: fOscAddress(nullptr),
fOscServer(nullptr),
fOscServerThread(nullptr),
fClientNameId(),
fProjectPath(),
fHasBroadcast(false),
fHasOptionalGui(false),
fHasServerControl(false),
fStarted(),
fReadyActionOpen(true),
fReadyActionSave(true) {}

~CarlaNSM()
{
CARLA_SAFE_ASSERT(fReadyActionOpen);
CARLA_SAFE_ASSERT(fReadyActionSave);

if (fOscServerThread != nullptr)
{
lo_server_thread_stop(fOscServerThread);
lo_server_thread_free(fOscServerThread);
fOscServerThread = nullptr;
fOscServer = nullptr;
}

if (fOscAddress != nullptr)
{
lo_address_free(fOscAddress);
fOscAddress = nullptr;
}
}

bool announce(const int pid, const char* const executableName)
{
CARLA_SAFE_ASSERT_RETURN(pid != 0, false);
CARLA_SAFE_ASSERT_RETURN(executableName != nullptr && executableName[0] != '\0', false);

const char* const NSM_URL(std::getenv("NSM_URL"));

if (NSM_URL == nullptr)
return false;

const lo_address addr = lo_address_new_from_url(NSM_URL);
CARLA_SAFE_ASSERT_RETURN(addr != nullptr, false);

const int proto = lo_address_get_protocol(addr);

if (fOscServerThread == nullptr)
{
// create new OSC server
fOscServerThread = lo_server_thread_new_with_proto(nullptr, proto, _osc_error_handler);
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, false);

// register message handlers
lo_server_thread_add_method(fOscServerThread, "/error", "sis", _error_handler, this);
lo_server_thread_add_method(fOscServerThread, "/reply", "ssss", _reply_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/open", "sss", _open_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/save", "", _save_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/session_is_loaded", "", _loaded_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/show_optional_gui", "", _show_gui_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this);
lo_server_thread_add_method(fOscServerThread, nullptr, nullptr, _broadcast_handler, this);

fOscServer = lo_server_thread_get_server(fOscServerThread);
}

#ifndef BUILD_ANSI_TEST
lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
"Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid);
#endif

lo_address_free(addr);

return true;
}

void ready(const int action)
{
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr,);

switch (action)
{
case -1: // init
CARLA_SAFE_ASSERT_BREAK(! fStarted);
fStarted = true;
lo_server_thread_start(fOscServerThread);
break;

case 0: // error
break;

case 1: // reply
break;

case 2: // open
fReadyActionOpen = true;
break;

case 3: // save
fReadyActionSave = true;
break;

case 4: // session loaded
break;

case 5: // show gui
CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr);
CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");
break;

case 6: // hide gui
CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr);
CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "");
break;
}
}

static CarlaNSM& getInstance()
{
static CarlaNSM sInstance;
return sInstance;
}

protected:
int handleError(const char* const method, const int code, const char* const message)
{
carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message);

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message);

return 1;

// may be unused
(void)method;
}

int handleReply(const char* const method, const char* const message, const char* const smName, const char* const features,
const lo_message msg)
{
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, 1);
carla_stdout("CarlaNSM::handleReply(\"%s\", \"%s\", \"%s\", \"%s\")", method, message, smName, features);

if (std::strcmp(method, "/nsm/server/announce") == 0)
{
char* const addressURL(lo_address_get_url(lo_message_get_source(msg)));
CARLA_SAFE_ASSERT_RETURN(addressURL != nullptr, 0);

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

fOscAddress = lo_address_new_from_url(addressURL);
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 0);

fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr;
fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr;
fHasServerControl = std::strstr(features, ":server_control:") != nullptr;

// UI starts visible
if (fHasOptionalGui)
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");

carla_stdout("Carla started via '%s', message: %s", smName, message);

if (gStandalone.engineCallback != nullptr)
{
int flags = 0;

if (fHasBroadcast)
flags |= 1 << 0;
if (fHasOptionalGui)
flags |= 1 << 1;
if (fHasServerControl)
flags |= 1 << 2;

gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, flags, 0.0f, smName);
}
}
else
{
carla_stdout("Got unknown NSM reply method '%s'", method);
}

return 0;
}

int handleOpen(const char* const projectPath, const char* const displayName, const char* const clientNameId)
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleOpen(\"%s\", \"%s\", \"%s\")", projectPath, displayName, clientNameId);

if (gStandalone.engineCallback != nullptr)
{
fReadyActionOpen = false;
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath);

for (; ! fReadyActionOpen;)
carla_msleep(10);
}
else
{
using namespace juce;

if (carla_is_engine_running())
carla_engine_close();

carla_engine_init("JACK", clientNameId);

fProjectPath = projectPath;
fProjectPath += ".carxp";

const String jfilename = String(CharPointer_UTF8(fProjectPath));

if (File(jfilename).existsAsFile())
carla_load_project(fProjectPath);
}

fClientNameId = clientNameId;

lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK");

// Broadcast ourselves
if (fHasBroadcast)
{
char* const oscServerAddress(lo_server_get_url(fOscServer));

if (lo_message msg = lo_message_new())
{
lo_message_add(msg, "sssss",
"/non/hello",
oscServerAddress,
"Carla",
CARLA_VERSION_STRING,
fClientNameId.buffer());

lo_send_message_from(fOscAddress, fOscServer, "/nsm/server/broadcast", msg);
lo_message_free(msg);
}

std::free(oscServerAddress);
}

return 0;
}

int handleSave()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleSave()");

if (gStandalone.engineCallback != nullptr)
{
fReadyActionSave = false;
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 3, 0, 0.0f, nullptr);

for (; ! fReadyActionSave;)
carla_msleep(10);
}
else
{
CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0);

carla_save_project(fProjectPath);
}

lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK");

return 0;
}

int handleSessionIsLoaded()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleSessionIsLoaded()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 4, 0, 0.0f, nullptr);

return 0;
}

int handleShowOptionalGui()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleShowOptionalGui()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr);

return 0;
}

int handleHideOptionalGui()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleHideOptionalGui()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 6, 0, 0.0f, nullptr);

return 0;
}

int handleBroadcast(const char* const path, const char* const types, lo_arg** const argv, const int argc,
const lo_message msg)
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(argc >= 0, 0);
carla_stdout("CarlaNSM::handleBroadcast(%s, %s, %p, %i)", path, types, argv, argc);

if (std::strcmp(path, "/non/hello") == 0)
{
CARLA_SAFE_ASSERT_RETURN(argc == 4, 0);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 0);

const char* const url = &argv[0]->s;
//const char* const name = &argv[1]->s;
//const char* const version = &argv[2]->s;
//const char* const clientId = &argv[3]->s;

const lo_address targetAddress(lo_address_new_from_url(url));
CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);

char* const oscServerAddress(lo_server_get_url(fOscServer));
CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0);

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "ss",
fClientNameId.buffer(),
oscServerAddress);

lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent hello");
}

lo_address_free(targetAddress);
std::free(oscServerAddress);

return 0;
}

if (std::strcmp(path, "/signal/hello") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got hello");

//const char* const name = &argv[0]->s;
const char* const url = &argv[1]->s;

const lo_address targetAddress(lo_address_new_from_url(url));
CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);

char* const oscServerAddress(lo_server_get_url(fOscServer));
CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0);

//lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/signal/list", "");

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "ss",
fClientNameId.buffer(),
oscServerAddress);

lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent hello");
}

lo_address_free(targetAddress);
std::free(oscServerAddress);

return 0;
}

if (std::strcmp(path, "/signal/list") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got list");
CARLA_SAFE_ASSERT_RETURN(carla_is_engine_running(), 0);

//const char* prefix = nullptr;

//if (argc > 0)
//prefix = &argv[0]->s;

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

CarlaString stripName;

for (uint32_t i = 0, pluginCount = carla_get_current_plugin_count(); i < pluginCount; ++i)
{
const CarlaPluginInfo* const pluginInfo(carla_get_plugin_info(i));
CARLA_SAFE_ASSERT_CONTINUE(pluginInfo != nullptr);

for (uint32_t j=0, paramCount = carla_get_parameter_count(i); j < paramCount; ++j)
{
const CarlaParameterInfo* const paramInfo(carla_get_parameter_info(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramInfo != nullptr);

const ParameterData* const paramData(carla_get_parameter_data(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramData != nullptr);

const ParameterRanges* const paramRanges(carla_get_parameter_ranges(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramRanges != nullptr);

if (paramData->type != CB::PARAMETER_INPUT && paramData->type != CB::PARAMETER_OUTPUT)
continue;
if ((paramData->hints & CB::PARAMETER_IS_ENABLED) == 0)
continue;
if ((paramData->hints & CB::PARAMETER_IS_AUTOMABLE) == 0)
continue;
if (paramData->hints & CB::PARAMETER_IS_READ_ONLY)
continue;

if (lo_message msg2 = lo_message_new())
{
stripName = fClientNameId + "/strip/Unnamed/" + pluginInfo->name + "/" + paramInfo->name;
stripName.replace(' ','_');

lo_message_add(msg2, "sssfff",
path,
stripName.buffer(),
paramData->type == CB::PARAMETER_INPUT ? "in" : "out",
paramRanges->min,
paramRanges->max,
paramRanges->def);
lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);
lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent list at %i : %i, path: %s", i+1, j+1, stripName.buffer());
}

#if 0
if (prefix == nullptr /*|| strncmp(o->path(), prefix, std::strlen(prefix)) == 0*/)
{
lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "sssfff",
path,
paramInfo->name,
paramData->type == CB::PARAMETER_INPUT ? "in" : "out",
paramRanges->min,
paramRanges->max,
paramRanges->def);
}
#endif
}
}

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "s", path);

lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent list final");
}

//lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "s", path);

//return 0;
}

#if 0
if (std::strcmp(path, "/reply") == 0)
{
CARLA_SAFE_ASSERT_RETURN(argc > 0, 0);

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

if (std::strcmp(method, "/signal/list") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got new list");

if (argc == 1)
return 0;
CARLA_SAFE_ASSERT_RETURN(argc == 6, 0);

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

const char* const stripName = &argv[1]->s;
const char* const orientation = &argv[2]->s;
const float min = argv[3]->f;
const float max = argv[4]->f;
const float def = argv[5]->f;

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "sssfff",
method,
stripName,
orientation,
min,
max,
def);
lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);
lo_message_free(msg2);
}
}
else
{
CARLA_SAFE_ASSERT(false);
}

return 0;
}
#endif

// check if all args are strings
for (int i=0; i<argc; ++i)
{
if (types[i] != 's')
return 0;
}

for (int i=0; i<argc; ++i)
carla_stdout("%i: %s", i+1, &argv[i]->s);

return 0;
}

private:
lo_address fOscAddress;
lo_server fOscServer;
lo_server_thread fOscServerThread;
CarlaString fClientNameId;
CarlaString fProjectPath;

bool fHasBroadcast;
bool fHasOptionalGui;
bool fHasServerControl;
bool fStarted;

volatile bool fReadyActionOpen;
volatile bool fReadyActionSave;

#define handlePtr ((CarlaNSM*)data)

static void _osc_error_handler(int num, const char* msg, const char* path)
{
carla_stderr2("CarlaNSM::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
}

static int _error_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sis") == 0, 1);

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

return handlePtr->handleError(method, code, message);
}

static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 4, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 1);

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

return handlePtr->handleReply(method, message, smName, features, msg);
}

static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sss") == 0, 1);

const char* const projectPath = &argv[0]->s;
const char* const displayName = &argv[1]->s;
const char* const clientNameId = &argv[2]->s;

return handlePtr->handleOpen(projectPath, displayName, clientNameId);
}

static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSave();
}

static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSessionIsLoaded();
}

static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleShowOptionalGui();
}

static int _hide_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleHideOptionalGui();
}

static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{
return handlePtr->handleBroadcast(path, types, argv, argc, msg);
}

#undef handlePtr

CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM)
};

#endif // HAVE_LIBLO
CarlaBackendStandalone gStandalone;

// -------------------------------------------------------------------------------------------------------------------
// API
@@ -2564,27 +1914,6 @@ const char* carla_get_host_osc_url_udp()

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

bool carla_nsm_init(int pid, const char* executableName)
{
#ifdef HAVE_LIBLO
return CarlaNSM::getInstance().announce(pid, executableName);
#else
return false;

// unused
(void)pid; (void)executableName;
#endif
}

void carla_nsm_ready(int action)
{
#ifdef HAVE_LIBLO
CarlaNSM::getInstance().ready(action);
#endif
}

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

#include "CarlaPluginUI.cpp"
#include "CarlaDssiUtils.cpp"
#include "CarlaPatchbayUtils.cpp"


+ 712
- 0
source/backend/CarlaStandaloneNSM.cpp View File

@@ -0,0 +1,712 @@
/*
* Carla Standalone
* Copyright (C) 2011-2015 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
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#include "CarlaDefines.h"

#ifdef HAVE_LIBLO

#define NSM_API_VERSION_MAJOR 1
#define NSM_API_VERSION_MINOR 2

#define NSM_CLIENT_FEATURES ":switch:"
//#define NSM_CLIENT_FEATURES ":switch:optional-gui:"

#include "CarlaHost.h"
#include "CarlaOscUtils.hpp"
#include "CarlaString.hpp"
#include "juce_core.h"

namespace CB = CarlaBackend;

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

struct CarlaBackendStandalone {
CarlaEngine* engine;
EngineCallbackFunc engineCallback;
void* engineCallbackPtr;
// ...
};

extern CarlaBackendStandalone gStandalone;

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

class CarlaNSM
{
public:
CarlaNSM() noexcept
: fOscAddress(nullptr),
fOscServer(nullptr),
fOscServerThread(nullptr),
fClientNameId(),
fProjectPath(),
fHasBroadcast(false),
fHasOptionalGui(false),
fHasServerControl(false),
fStarted(),
fReadyActionOpen(true),
fReadyActionSave(true) {}

~CarlaNSM()
{
CARLA_SAFE_ASSERT(fReadyActionOpen);
CARLA_SAFE_ASSERT(fReadyActionSave);

if (fOscServerThread != nullptr)
{
lo_server_thread_stop(fOscServerThread);
lo_server_thread_free(fOscServerThread);
fOscServerThread = nullptr;
fOscServer = nullptr;
}

if (fOscAddress != nullptr)
{
lo_address_free(fOscAddress);
fOscAddress = nullptr;
}
}

bool announce(const int pid, const char* const executableName)
{
CARLA_SAFE_ASSERT_RETURN(pid != 0, false);
CARLA_SAFE_ASSERT_RETURN(executableName != nullptr && executableName[0] != '\0', false);

const char* const NSM_URL(std::getenv("NSM_URL"));

if (NSM_URL == nullptr)
return false;

const lo_address addr = lo_address_new_from_url(NSM_URL);
CARLA_SAFE_ASSERT_RETURN(addr != nullptr, false);

const int proto = lo_address_get_protocol(addr);

if (fOscServerThread == nullptr)
{
// create new OSC server
fOscServerThread = lo_server_thread_new_with_proto(nullptr, proto, _osc_error_handler);
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, false);

// register message handlers
lo_server_thread_add_method(fOscServerThread, "/error", "sis", _error_handler, this);
lo_server_thread_add_method(fOscServerThread, "/reply", "ssss", _reply_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/open", "sss", _open_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/save", "", _save_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/session_is_loaded", "", _loaded_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/show_optional_gui", "", _show_gui_handler, this);
lo_server_thread_add_method(fOscServerThread, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this);
lo_server_thread_add_method(fOscServerThread, nullptr, nullptr, _broadcast_handler, this);

fOscServer = lo_server_thread_get_server(fOscServerThread);
}

lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
"Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid);

lo_address_free(addr);

return true;
}

void ready(const int action)
{
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr,);

switch (action)
{
case -1: // init
CARLA_SAFE_ASSERT_BREAK(! fStarted);
fStarted = true;
lo_server_thread_start(fOscServerThread);
break;

case 0: // error
break;

case 1: // reply
break;

case 2: // open
fReadyActionOpen = true;
break;

case 3: // save
fReadyActionSave = true;
break;

case 4: // session loaded
break;

case 5: // show gui
CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr);
CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");
break;

case 6: // hide gui
CARLA_SAFE_ASSERT_BREAK(fOscAddress != nullptr);
CARLA_SAFE_ASSERT_BREAK(fOscServer != nullptr);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "");
break;
}
}

static CarlaNSM& getInstance()
{
static CarlaNSM sInstance;
return sInstance;
}

protected:
int handleError(const char* const method, const int code, const char* const message)
{
carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message);

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message);

return 1;

// may be unused
(void)method;
}

int handleReply(const char* const method, const char* const message, const char* const smName, const char* const features,
const lo_message msg)
{
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr, 1);
carla_stdout("CarlaNSM::handleReply(\"%s\", \"%s\", \"%s\", \"%s\")", method, message, smName, features);

if (std::strcmp(method, "/nsm/server/announce") == 0)
{
char* const addressURL(lo_address_get_url(lo_message_get_source(msg)));
CARLA_SAFE_ASSERT_RETURN(addressURL != nullptr, 0);

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

fOscAddress = lo_address_new_from_url(addressURL);
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 0);

fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr;
fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr;
fHasServerControl = std::strstr(features, ":server_control:") != nullptr;

// UI starts visible
if (fHasOptionalGui)
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");

carla_stdout("Carla started via '%s', message: %s", smName, message);

if (gStandalone.engineCallback != nullptr)
{
int flags = 0;

if (fHasBroadcast)
flags |= 1 << 0;
if (fHasOptionalGui)
flags |= 1 << 1;
if (fHasServerControl)
flags |= 1 << 2;

gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, flags, 0.0f, smName);
}
}
else
{
carla_stdout("Got unknown NSM reply method '%s'", method);
}

return 0;
}

int handleOpen(const char* const projectPath, const char* const displayName, const char* const clientNameId)
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleOpen(\"%s\", \"%s\", \"%s\")", projectPath, displayName, clientNameId);

if (gStandalone.engineCallback != nullptr)
{
fReadyActionOpen = false;
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath);

for (; ! fReadyActionOpen;)
carla_msleep(10);
}
else
{
using namespace juce;

if (carla_is_engine_running())
carla_engine_close();

carla_engine_init("JACK", clientNameId);

fProjectPath = projectPath;
fProjectPath += ".carxp";

const String jfilename = String(CharPointer_UTF8(fProjectPath));

if (File(jfilename).existsAsFile())
carla_load_project(fProjectPath);
}

fClientNameId = clientNameId;

lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK");

// Broadcast ourselves
if (fHasBroadcast)
{
char* const oscServerAddress(lo_server_get_url(fOscServer));

if (lo_message msg = lo_message_new())
{
lo_message_add(msg, "sssss",
"/non/hello",
oscServerAddress,
"Carla",
CARLA_VERSION_STRING,
fClientNameId.buffer());

lo_send_message_from(fOscAddress, fOscServer, "/nsm/server/broadcast", msg);
lo_message_free(msg);
}

std::free(oscServerAddress);
}

return 0;
}

int handleSave()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleSave()");

if (gStandalone.engineCallback != nullptr)
{
fReadyActionSave = false;
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 3, 0, 0.0f, nullptr);

for (; ! fReadyActionSave;)
carla_msleep(10);
}
else
{
CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0);

carla_save_project(fProjectPath);
}

lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK");

return 0;
}

int handleSessionIsLoaded()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleSessionIsLoaded()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 4, 0, 0.0f, nullptr);

return 0;
}

int handleShowOptionalGui()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleShowOptionalGui()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr);

return 0;
}

int handleHideOptionalGui()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleHideOptionalGui()");

if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 6, 0, 0.0f, nullptr);

return 0;
}

int handleBroadcast(const char* const path, const char* const types, lo_arg** const argv, const int argc,
const lo_message msg)
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(argc >= 0, 0);
carla_stdout("CarlaNSM::handleBroadcast(%s, %s, %p, %i)", path, types, argv, argc);

if (std::strcmp(path, "/non/hello") == 0)
{
CARLA_SAFE_ASSERT_RETURN(argc == 4, 0);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 0);

const char* const url = &argv[0]->s;
//const char* const name = &argv[1]->s;
//const char* const version = &argv[2]->s;
//const char* const clientId = &argv[3]->s;

const lo_address targetAddress(lo_address_new_from_url(url));
CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);

char* const oscServerAddress(lo_server_get_url(fOscServer));
CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0);

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "ss",
fClientNameId.buffer(),
oscServerAddress);

lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent hello");
}

lo_address_free(targetAddress);
std::free(oscServerAddress);

return 0;
}

if (std::strcmp(path, "/signal/hello") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got hello");

//const char* const name = &argv[0]->s;
const char* const url = &argv[1]->s;

const lo_address targetAddress(lo_address_new_from_url(url));
CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);

char* const oscServerAddress(lo_server_get_url(fOscServer));
CARLA_SAFE_ASSERT_RETURN(oscServerAddress != nullptr, 0);

//lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/signal/list", "");

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "ss",
fClientNameId.buffer(),
oscServerAddress);

lo_send_message_from(targetAddress, fOscServer, "/signal/hello", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent hello");
}

lo_address_free(targetAddress);
std::free(oscServerAddress);

return 0;
}

if (std::strcmp(path, "/signal/list") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got list");
CARLA_SAFE_ASSERT_RETURN(carla_is_engine_running(), 0);

//const char* prefix = nullptr;

//if (argc > 0)
//prefix = &argv[0]->s;

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

CarlaString stripName;

for (uint32_t i = 0, pluginCount = carla_get_current_plugin_count(); i < pluginCount; ++i)
{
const CarlaPluginInfo* const pluginInfo(carla_get_plugin_info(i));
CARLA_SAFE_ASSERT_CONTINUE(pluginInfo != nullptr);

for (uint32_t j=0, paramCount = carla_get_parameter_count(i); j < paramCount; ++j)
{
const CarlaParameterInfo* const paramInfo(carla_get_parameter_info(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramInfo != nullptr);

const ParameterData* const paramData(carla_get_parameter_data(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramData != nullptr);

const ParameterRanges* const paramRanges(carla_get_parameter_ranges(i, j));
CARLA_SAFE_ASSERT_CONTINUE(paramRanges != nullptr);

if (paramData->type != CB::PARAMETER_INPUT && paramData->type != CB::PARAMETER_OUTPUT)
continue;
if ((paramData->hints & CB::PARAMETER_IS_ENABLED) == 0)
continue;
if ((paramData->hints & CB::PARAMETER_IS_AUTOMABLE) == 0)
continue;
if (paramData->hints & CB::PARAMETER_IS_READ_ONLY)
continue;

if (lo_message msg2 = lo_message_new())
{
stripName = fClientNameId + "/strip/Unnamed/" + pluginInfo->name + "/" + paramInfo->name;
stripName.replace(' ','_');

lo_message_add(msg2, "sssfff",
path,
stripName.buffer(),
paramData->type == CB::PARAMETER_INPUT ? "in" : "out",
paramRanges->min,
paramRanges->max,
paramRanges->def);
lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);
lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent list at %i : %i, path: %s", i+1, j+1, stripName.buffer());
}

#if 0
if (prefix == nullptr /*|| strncmp(o->path(), prefix, std::strlen(prefix)) == 0*/)
{
lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "sssfff",
path,
paramInfo->name,
paramData->type == CB::PARAMETER_INPUT ? "in" : "out",
paramRanges->min,
paramRanges->max,
paramRanges->def);
}
#endif
}
}

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "s", path);

lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);

lo_message_free(msg2);

carla_stdout("CarlaNSM::handleBroadcast - sent list final");
}

//lo_send_from(targetAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "s", path);

//return 0;
}

#if 0
if (std::strcmp(path, "/reply") == 0)
{
CARLA_SAFE_ASSERT_RETURN(argc > 0, 0);

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

if (std::strcmp(method, "/signal/list") == 0)
{
carla_stdout("CarlaNSM::handleBroadcast - got new list");

if (argc == 1)
return 0;
CARLA_SAFE_ASSERT_RETURN(argc == 6, 0);

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

const char* const stripName = &argv[1]->s;
const char* const orientation = &argv[2]->s;
const float min = argv[3]->f;
const float max = argv[4]->f;
const float def = argv[5]->f;

if (lo_message msg2 = lo_message_new())
{
lo_message_add(msg2, "sssfff",
method,
stripName,
orientation,
min,
max,
def);
lo_send_message_from(targetAddress, fOscServer, "/reply", msg2);
lo_message_free(msg2);
}
}
else
{
CARLA_SAFE_ASSERT(false);
}

return 0;
}
#endif

// check if all args are strings
for (int i=0; i<argc; ++i)
{
if (types[i] != 's')
return 0;
}

for (int i=0; i<argc; ++i)
carla_stdout("%i: %s", i+1, &argv[i]->s);

return 0;
}

private:
lo_address fOscAddress;
lo_server fOscServer;
lo_server_thread fOscServerThread;
CarlaString fClientNameId;
CarlaString fProjectPath;

bool fHasBroadcast;
bool fHasOptionalGui;
bool fHasServerControl;
bool fStarted;

volatile bool fReadyActionOpen;
volatile bool fReadyActionSave;

#define handlePtr ((CarlaNSM*)data)

static void _osc_error_handler(int num, const char* msg, const char* path)
{
carla_stderr2("CarlaNSM::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
}

static int _error_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sis") == 0, 1);

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

return handlePtr->handleError(method, code, message);
}

static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 4, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 1);

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

return handlePtr->handleReply(method, message, smName, features, msg);
}

static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sss") == 0, 1);

const char* const projectPath = &argv[0]->s;
const char* const displayName = &argv[1]->s;
const char* const clientNameId = &argv[2]->s;

return handlePtr->handleOpen(projectPath, displayName, clientNameId);
}

static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSave();
}

static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSessionIsLoaded();
}

static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleShowOptionalGui();
}

static int _hide_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleHideOptionalGui();
}

static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{
return handlePtr->handleBroadcast(path, types, argv, argc, msg);
}

#undef handlePtr

CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM)
};

#endif // HAVE_LIBLO

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

CARLA_EXPORT
bool carla_nsm_init(int pid, const char* executableName);

bool carla_nsm_init(int pid, const char* executableName)
{
#ifdef HAVE_LIBLO
return CarlaNSM::getInstance().announce(pid, executableName);
#else
return false;

// unused
(void)pid; (void)executableName;
#endif
}

CARLA_EXPORT
void carla_nsm_ready(int action);

void carla_nsm_ready(int action)
{
#ifdef HAVE_LIBLO
CarlaNSM::getInstance().ready(action);
#endif
}

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

+ 9
- 6
source/backend/Makefile View File

@@ -9,8 +9,11 @@ include Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

OBJS = \
OBJS_standalone = \
$(OBJDIR)/CarlaStandalone.cpp.o \
$(OBJDIR)/CarlaStandaloneNSM.cpp.o

OBJS_utils = \
$(OBJDIR)/CarlaUtils.cpp.o

TARGETS = \
@@ -123,7 +126,7 @@ all: $(TARGETS)
# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJS) $(TARGETS)
rm -f $(OBJS_standalone) $(OBJS_utils) $(TARGETS)
$(MAKE) clean -C engine
$(MAKE) clean -C plugin

@@ -135,15 +138,15 @@ doxygen: CarlaBackend.doxygen

# ----------------------------------------------------------------------------------------------------------------------------

$(BINDIR)/libcarla_standalone2$(LIB_EXT): $(OBJDIR)/CarlaStandalone.cpp.o $(STANDALONE_LIBS)
$(BINDIR)/libcarla_standalone2$(LIB_EXT): $(OBJS_standalone) $(STANDALONE_LIBS)
-@mkdir -p $(BINDIR)
@echo "Linking libcarla_standalone2$(LIB_EXT)"
@$(CXX) $< $(LIBS_START) $(STANDALONE_LIBS) $(LIBS_END) $(LINK_FLAGS) $(STANDALONE_LINK_FLAGS) $(SHARED) -o $@
@$(CXX) $(OBJS_standalone) $(LIBS_START) $(STANDALONE_LIBS) $(LIBS_END) $(LINK_FLAGS) $(STANDALONE_LINK_FLAGS) $(SHARED) -o $@

$(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJDIR)/CarlaUtils.cpp.o $(UTILS_LIBS)
$(BINDIR)/libcarla_utils$(LIB_EXT): $(OBJS_utils) $(UTILS_LIBS)
-@mkdir -p $(BINDIR)
@echo "Linking libcarla_utils$(LIB_EXT)"
@$(CXX) $< $(LIBS_START) $(UTILS_LIBS) $(LIBS_END) $(LINK_FLAGS) $(UTILS_LINK_FLAGS) $(SHARED) -o $@
@$(CXX) $(OBJS_utils) $(LIBS_START) $(UTILS_LIBS) $(LIBS_END) $(LINK_FLAGS) $(UTILS_LINK_FLAGS) $(SHARED) -o $@

# ----------------------------------------------------------------------------------------------------------------------------



Loading…
Cancel
Save