Browse Source

Rework NSM code, actually works now :)

tags/1.9.6
falkTX 10 years ago
parent
commit
1ce3132801
8 changed files with 348 additions and 187 deletions
  1. +15
    -14
      source/backend/CarlaBackend.h
  2. +10
    -0
      source/backend/CarlaHost.h
  3. +236
    -152
      source/backend/CarlaStandalone.cpp
  4. +0
    -1
      source/backend/engine/CarlaEngine.cpp
  5. +26
    -12
      source/carla_backend.py
  6. +1
    -0
      source/carla_backend_qt.py
  7. +58
    -6
      source/carla_host.py
  8. +2
    -2
      source/utils/CarlaBackendUtils.hpp

+ 15
- 14
source/backend/CarlaBackend.h View File

@@ -914,29 +914,35 @@ typedef enum {
*/ */
ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34, ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34,


/*!
* NSM callback.
* (Work in progress, values are not defined yet)
*/
ENGINE_CALLBACK_NSM = 35,

/*! /*!
* Idle frontend. * Idle frontend.
* This is used by the engine during long operations that might block the frontend, * This is used by the engine during long operations that might block the frontend,
* giving it the possibility to idle while the operation is still in place. * giving it the possibility to idle while the operation is still in place.
*/ */
ENGINE_CALLBACK_IDLE = 35,
ENGINE_CALLBACK_IDLE = 36,


/*! /*!
* Show a message as information. * Show a message as information.
* @a valueStr The message * @a valueStr The message
*/ */
ENGINE_CALLBACK_INFO = 36,
ENGINE_CALLBACK_INFO = 37,


/*! /*!
* Show a message as an error. * Show a message as an error.
* @a valueStr The message * @a valueStr The message
*/ */
ENGINE_CALLBACK_ERROR = 37,
ENGINE_CALLBACK_ERROR = 38,


/*! /*!
* The engine has crashed or malfunctioned and will no longer work. * The engine has crashed or malfunctioned and will no longer work.
*/ */
ENGINE_CALLBACK_QUIT = 38
ENGINE_CALLBACK_QUIT = 39


} EngineCallbackOpcode; } EngineCallbackOpcode;


@@ -1030,42 +1036,37 @@ typedef enum {
*/ */
ENGINE_OPTION_AUDIO_DEVICE = 12, ENGINE_OPTION_AUDIO_DEVICE = 12,


/*!
* Set data needed for NSM support.
*/
ENGINE_OPTION_NSM_INIT = 13,

/*! /*!
* Set path used for a specific plugin type. * Set path used for a specific plugin type.
* Uses value as the plugin format, valueStr as actual path. * Uses value as the plugin format, valueStr as actual path.
* @see PluginType * @see PluginType
*/ */
ENGINE_OPTION_PLUGIN_PATH = 14,
ENGINE_OPTION_PLUGIN_PATH = 13,


/*! /*!
* Set path to the binary files. * Set path to the binary files.
* Default unset. * Default unset.
* @note Must be set for plugin and UI bridges to work * @note Must be set for plugin and UI bridges to work
*/ */
ENGINE_OPTION_PATH_BINARIES = 15,
ENGINE_OPTION_PATH_BINARIES = 14,


/*! /*!
* Set path to the resource files. * Set path to the resource files.
* Default unset. * Default unset.
* @note Must be set for some internal plugins to work * @note Must be set for some internal plugins to work
*/ */
ENGINE_OPTION_PATH_RESOURCES = 16,
ENGINE_OPTION_PATH_RESOURCES = 15,


/*! /*!
* Prevent bad plugin and UI behaviour. * Prevent bad plugin and UI behaviour.
* @note: Linux only * @note: Linux only
*/ */
ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR = 17,
ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR = 16,


/*! /*!
* Set frontend winId, used to define as parent window for plugin UIs. * Set frontend winId, used to define as parent window for plugin UIs.
*/ */
ENGINE_OPTION_FRONTEND_WIN_ID = 18
ENGINE_OPTION_FRONTEND_WIN_ID = 17


} EngineOption; } EngineOption;




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

@@ -899,6 +899,16 @@ CARLA_EXPORT const char* carla_get_library_filename();
*/ */
CARLA_EXPORT const char* carla_get_library_folder(); 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();

/** @} */ /** @} */


#endif /* CARLA_HOST_H_INCLUDED */ #endif /* CARLA_HOST_H_INCLUDED */

+ 236
- 152
source/backend/CarlaStandalone.cpp View File

@@ -89,257 +89,333 @@ static CarlaBackendStandalone gStandalone;
#define NSM_API_VERSION_MAJOR 1 #define NSM_API_VERSION_MAJOR 1
#define NSM_API_VERSION_MINOR 2 #define NSM_API_VERSION_MINOR 2


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


class CarlaNSM class CarlaNSM
{ {
public: public:
CarlaNSM() noexcept CarlaNSM() noexcept
: fOscServer(nullptr),
fClientId(),
: fOscAddress(nullptr),
fOscServer(nullptr),
fOscServerThread(nullptr),
fProjectPath(), fProjectPath(),
fHasBroadcast(false), fHasBroadcast(false),
fHasShowHideUI(false) {}
fHasOptionalGui(false),
fHasServerControl(false),
fStarted() {}


~CarlaNSM() ~CarlaNSM()
{ {
if (fOscServer != nullptr)
if (fOscServerThread != nullptr)
{ {
lo_server_del_method(fOscServer, "/reply", "ssss");
lo_server_del_method(fOscServer, "/nsm/client/open", "sss");
lo_server_del_method(fOscServer, "/nsm/client/save", "");
//lo_server_del_method(fOscServer, "/nsm/client/show_optional_gui", "");
//lo_server_del_method(fOscServer, "/nsm/client/hide_optional_gui", "");
lo_server_free(fOscServer);
lo_server_thread_stop(fOscServerThread);
lo_server_thread_free(fOscServerThread);
fOscServerThread = nullptr;
fOscServer = nullptr; fOscServer = nullptr;
} }

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


void announce(const int pid, const char* const initName)
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")); const char* const NSM_URL(std::getenv("NSM_URL"));


if (NSM_URL == nullptr) if (NSM_URL == nullptr)
return;
return false;


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


const int proto = lo_address_get_protocol(addr); const int proto = lo_address_get_protocol(addr);


if (fOscServer == nullptr)
if (fOscServerThread == nullptr)
{ {
// create new OSC server // create new OSC server
fOscServer = lo_server_new_with_proto(nullptr, proto, _error_handler);

// register message handlers and start OSC thread
lo_server_add_method(fOscServer, "/reply", "ssss", _reply_handler, this);
lo_server_add_method(fOscServer, "/nsm/client/open", "sss", _open_handler, this);
lo_server_add_method(fOscServer, "/nsm/client/save", "", _save_handler, this);
//lo_server_add_method(fOscServer, "/nsm/client/show_optional_gui", "", _show_gui_handler, this);
//lo_server_add_method(fOscServer, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this);
// /nsm/client/session_is_loaded
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 #ifndef BUILD_ANSI_TEST
lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", lo_send_from(addr, fOscServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
"Carla", NSM_CLIENT_FEATURES, initName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid);
"Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid);
#endif #endif


lo_address_free(addr); lo_address_free(addr);

return true;
} }


void idle() noexcept
void start()
{ {
if (fOscServer == nullptr)
return;
CARLA_SAFE_ASSERT_RETURN(fOscServerThread != nullptr,);
CARLA_SAFE_ASSERT_RETURN(! fStarted,);


for (;;)
{
try {
if (lo_server_recv_noblock(fOscServer, 0) == 0)
break;
} CARLA_SAFE_EXCEPTION_CONTINUE("NSM OSC idle")
}
fStarted = true;
lo_server_thread_start(fOscServerThread);
}

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


protected: protected:
int handleReply(const char* const path, const char* const types, lo_arg** const argv, const int argc, const lo_message msg)
int handleError(const char* const method, const int code, const char* const message)
{ {
carla_debug("CarlaNSM::handleReply(%s, %i, %p, %s, %p)", path, argc, argv, types, msg);
carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message);


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;
if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message);


CARLA_SAFE_ASSERT_RETURN(std::strcmp(method, "/nsm/server/announce") == 0, 0);
return 1;


fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr;
fHasShowHideUI = std::strstr(features, ":optional-gui:") != nullptr;
// may be unused
(void)method;
}


carla_stdout("'%s' started: %s", smName, message);
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);


// TODO: send callback, disable open+save etc
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);


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


#ifndef DEBUG
// unused
(void)path; (void)types; (void)argc; (void)msg;
#endif
}
fOscAddress = lo_address_new_from_url(addressURL);
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 0);


// FIXME
int handleOpen(const char* const path, const char* const types, lo_arg** const argv, const int argc, const lo_message msg)
{
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 0);
carla_debug("CarlaNSM::handleOpen(\"%s\", \"%s\", %p, %i, %p)", path, types, argv, argc, msg);
fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr;
fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr;
fHasServerControl = std::strstr(features, ":server_control:") != nullptr;


const char* const projectPath = &argv[0]->s;
//const char* const displayName = &argv[1]->s;
const char* const clientId = &argv[2]->s;
carla_stdout("Carla started via '%s', message: %s", smName, message);


if (! carla_is_engine_running())
{
#ifndef BUILD_BRIDGE
gStandalone.engineOptions.processMode = CB::ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS;
gStandalone.engineOptions.transportMode = CB::ENGINE_TRANSPORT_MODE_JACK;
#endif
if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, 0, 0.0f, smName);


carla_engine_init("JACK", clientId);
// UI starts visible
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");
} }
else else
{ {
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, 0);

for (uint i=0, count=gStandalone.engine->getCurrentPluginCount(); i < count; ++i)
gStandalone.engine->removePlugin(i);
carla_stdout("Got unknown NSM reply method '%s'", method);
} }


fClientId = clientId;
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 (carla_is_engine_running())
carla_engine_close();

carla_engine_init("JACK", clientNameId);


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


CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, 0);
using namespace juce;


if (juce::File(fProjectPath.buffer()).existsAsFile())
gStandalone.engine->loadProject(fProjectPath);
const String jfilename = String(CharPointer_UTF8(fProjectPath));


#ifndef BUILD_ANSI_TEST
lo_send_from(lo_message_get_source(msg), fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK");
#endif
if (File(jfilename).existsAsFile())
carla_load_project(fProjectPath);


#if 0
if (fHasBroadcast)
{
char* const url = lo_server_get_url(fOscServer);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK");


lo_send(lo_message_get_source(msg), "/nsm/server/broadcast", "sssss",
"/non/hello", url, "Carla", CARLA_VERSION_STRING, clientId);
if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath);


//lo_send_from(lo_message_get_source(msg), fOscServer, LO_TT_IMMEDIATE, "/nsm/server/broadcast", "sssss"
// "/non/hello", url, "Carla", CARLA_VERSION_STRING, clientId);
return 0;
}


lo_send(lo_message_get_source(msg), "/signal/created", "ssfff", "/path/somewhere", true ? "in" : "out", 0.0f, 1.0f, 0.5f);
int handleSave()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0);
carla_stdout("CarlaNSM::handleSave()");


std::free(url);
carla_save_project(fProjectPath);


carla_stdout("Broadcast sent!");
}
else
carla_stdout("Broadcast NOT NOT NOT sent!");
#endif
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK");

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


return 0; return 0;
}


#ifndef DEBUG
// unused
(void)path; (void)types; (void)argc; (void)msg;
#endif
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 handleSave(const char* const path, const char* const types, lo_arg** const argv, const int argc, const lo_message msg)
int handleShowOptionalGui()
{ {
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 0);
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, 0);
CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0);
carla_debug("CarlaNSM::handleSave(\"%s\", \"%s\", %p, %i, %p)", path, types, argv, argc, msg);
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleShowOptionalGui()");


gStandalone.engine->saveProject(fProjectPath);
lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");


#ifndef BUILD_ANSI_TEST
lo_send_from(lo_message_get_source(msg), fOscServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK");
#endif
if (gStandalone.engineCallback != nullptr)
gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr);


return 0; return 0;
}


#ifndef DEBUG
// unused
(void)path; (void)types; (void)argv; (void)argc; (void)msg;
#endif
int handleHideOptionalGui()
{
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleHideOptionalGui()");

lo_send_from(fOscAddress, fOscServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "");

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

return 0;
} }


#if 0
int handleShowHideGui(const lo_message msg, const bool show)
int handleBroadcast(const char* const types, lo_arg** const argv, const int argc)
{ {
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 0);
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr, 0);
//CARLA_SAFE_ASSERT_RETURN(gStandalone.frontendWinId != 0, 0);
carla_debug("CarlaNSM::handleShowHideGui(%s)", bool2str(show));
CARLA_SAFE_ASSERT_RETURN(fOscAddress != nullptr, 1);
CARLA_SAFE_ASSERT_RETURN(fOscServer != nullptr, 1);
carla_stdout("CarlaNSM::handleBroadcast(%s, %p, %i)", types, argv, argc);


#ifndef BUILD_ANSI_TEST
lo_send_from(lo_message_get_source(msg), fOscServer, LO_TT_IMMEDIATE, show ? "/nsm/client/gui_is_shown" : "/nsm/client/gui_is_hidden", "");
#endif
// TODO


return 0; return 0;
} }
#endif


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


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


#define handlePtr ((CarlaNSM*)data) #define handlePtr ((CarlaNSM*)data)


static void _error_handler(int num, const char* msg, const char* path)
static void _osc_error_handler(int num, const char* msg, const char* path)
{ {
carla_stderr2("CarlaNSM::_error_handler(%i, \"%s\", \"%s\")", num, msg, 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* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
{ {
return handlePtr->handleReply(path, types, argv, argc, msg);
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* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
{ {
return handlePtr->handleOpen(path, types, argv, argc, msg);
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* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{ {
return handlePtr->handleSave(path, types, argv, argc, msg);
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSave();
} }


#if 0
static int _show_gui_handler(const char*, const char*, lo_arg**, int, lo_message, void* data)
static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{ {
return handlePtr->handleShowHideGui(true);
CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);

return handlePtr->handleSessionIsLoaded();
} }


static int _hide_gui_handler(const char*, const char*, lo_arg**, int, lo_message, void* data)
static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
{ {
return handlePtr->handleShowHideGui(false);
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, void* data)
{
return handlePtr->handleBroadcast(types, argv, argc);
} }
#endif


#undef handlePtr #undef handlePtr


@@ -347,8 +423,6 @@ private:
CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM) CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM)
}; };


static CarlaNSM gNSM;

#endif // HAVE_LIBLO #endif // HAVE_LIBLO


// ------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------
@@ -685,9 +759,6 @@ void carla_engine_idle()
{ {
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,); CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr,);


#ifdef HAVE_LIBLO
gNSM.idle();
#endif
gStandalone.engine->idle(); gStandalone.engine->idle();
} }


@@ -793,14 +864,6 @@ void carla_set_engine_option(EngineOption option, int value, const char* valueSt
gStandalone.engineOptions.audioDevice = carla_strdup_safe(valueStr); gStandalone.engineOptions.audioDevice = carla_strdup_safe(valueStr);
break; break;


case CB:: ENGINE_OPTION_NSM_INIT:
CARLA_SAFE_ASSERT_RETURN(value != 0,);
CARLA_SAFE_ASSERT_RETURN(valueStr != nullptr && valueStr[0] != '\0',);
#ifdef HAVE_LIBLO
gNSM.announce(value, valueStr);
#endif
break;

case CB::ENGINE_OPTION_PLUGIN_PATH: case CB::ENGINE_OPTION_PLUGIN_PATH:
CARLA_SAFE_ASSERT_RETURN(value > CB::PLUGIN_NONE,); CARLA_SAFE_ASSERT_RETURN(value > CB::PLUGIN_NONE,);
CARLA_SAFE_ASSERT_RETURN(value <= CB::PLUGIN_SFZ,); CARLA_SAFE_ASSERT_RETURN(value <= CB::PLUGIN_SFZ,);
@@ -2195,6 +2258,27 @@ 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()
{
#ifdef HAVE_LIBLO
CarlaNSM::getInstance().start();
#endif
}

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

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


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

@@ -1214,7 +1214,6 @@ void CarlaEngine::setOption(const EngineOption option, const int value, const ch
switch (option) switch (option)
{ {
case ENGINE_OPTION_DEBUG: case ENGINE_OPTION_DEBUG:
case ENGINE_OPTION_NSM_INIT:
break; break;


case ENGINE_OPTION_PROCESS_MODE: case ENGINE_OPTION_PROCESS_MODE:


+ 26
- 12
source/carla_backend.py View File

@@ -671,21 +671,25 @@ ENGINE_CALLBACK_BUFFER_SIZE_CHANGED = 33
# @a value3 New sample rate # @a value3 New sample rate
ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34 ENGINE_CALLBACK_SAMPLE_RATE_CHANGED = 34


# NSM callback.
# (Work in progress, values are not defined yet)
ENGINE_CALLBACK_NSM = 35

# Idle frontend. # Idle frontend.
# This is used by the engine during long operations that might block the frontend, # This is used by the engine during long operations that might block the frontend,
# giving it the possibility to idle while the operation is still in place. # giving it the possibility to idle while the operation is still in place.
ENGINE_CALLBACK_IDLE = 35
ENGINE_CALLBACK_IDLE = 36


# Show a message as information. # Show a message as information.
# @a valueStr The message # @a valueStr The message
ENGINE_CALLBACK_INFO = 36
ENGINE_CALLBACK_INFO = 37


# Show a message as an error. # Show a message as an error.
# @a valueStr The message # @a valueStr The message
ENGINE_CALLBACK_ERROR = 37
ENGINE_CALLBACK_ERROR = 38


# The engine has crashed or malfunctioned and will no longer work. # The engine has crashed or malfunctioned and will no longer work.
ENGINE_CALLBACK_QUIT = 38
ENGINE_CALLBACK_QUIT = 39


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# Engine Option # Engine Option
@@ -748,30 +752,27 @@ ENGINE_OPTION_AUDIO_SAMPLE_RATE = 11
# Default unset. # Default unset.
ENGINE_OPTION_AUDIO_DEVICE = 12 ENGINE_OPTION_AUDIO_DEVICE = 12


# Set data needed for NSM support.
ENGINE_OPTION_NSM_INIT = 13

# Set path used for a specific plugin type. # Set path used for a specific plugin type.
# Uses value as the plugin format, valueStr as actual path. # Uses value as the plugin format, valueStr as actual path.
# @see PluginType # @see PluginType
ENGINE_OPTION_PLUGIN_PATH = 14
ENGINE_OPTION_PLUGIN_PATH = 13


# Set path to the binary files. # Set path to the binary files.
# Default unset. # Default unset.
# @note Must be set for plugin and UI bridges to work # @note Must be set for plugin and UI bridges to work
ENGINE_OPTION_PATH_BINARIES = 15
ENGINE_OPTION_PATH_BINARIES = 14


# Set path to the resource files. # Set path to the resource files.
# Default unset. # Default unset.
# @note Must be set for some internal plugins to work # @note Must be set for some internal plugins to work
ENGINE_OPTION_PATH_RESOURCES = 16
ENGINE_OPTION_PATH_RESOURCES = 15


# Prevent bad plugin and UI behaviour. # Prevent bad plugin and UI behaviour.
# @note: Linux only # @note: Linux only
ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR = 17
ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR = 16


# Set frontend winId, used to define as parent window for plugin UIs. # Set frontend winId, used to define as parent window for plugin UIs.
ENGINE_OPTION_FRONTEND_WIN_ID = 18
ENGINE_OPTION_FRONTEND_WIN_ID = 17


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# Engine Process Mode # Engine Process Mode
@@ -1194,6 +1195,7 @@ class CarlaHostMeta(object):
# info about this host object # info about this host object
self.isControl = False self.isControl = False
self.isPlugin = False self.isPlugin = False
self.nsmOK = False


# settings # settings
self.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK self.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK
@@ -2343,6 +2345,12 @@ class CarlaHostDLL(CarlaHostMeta):
self.lib.carla_get_host_osc_url_udp.argtypes = None self.lib.carla_get_host_osc_url_udp.argtypes = None
self.lib.carla_get_host_osc_url_udp.restype = c_char_p self.lib.carla_get_host_osc_url_udp.restype = c_char_p


self.lib.carla_nsm_init.argtypes = [c_int, c_char_p]
self.lib.carla_nsm_init.restype = c_bool

self.lib.carla_nsm_ready.argtypes = None
self.lib.carla_nsm_ready.restype = None

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


def get_engine_driver_count(self): def get_engine_driver_count(self):
@@ -2605,6 +2613,12 @@ class CarlaHostDLL(CarlaHostMeta):
def get_host_osc_url_udp(self): def get_host_osc_url_udp(self):
return charPtrToString(self.lib.carla_get_host_osc_url_udp()) return charPtrToString(self.lib.carla_get_host_osc_url_udp())


def nsm_init(self, pid, executableName):
return bool(self.lib.carla_nsm_init(pid, executableName.encode("utf-8")))

def nsm_ready(self):
self.lib.carla_nsm_ready()

# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------
# Helper object for CarlaHostPlugin # Helper object for CarlaHostPlugin




+ 1
- 0
source/carla_backend_qt.py View File

@@ -80,6 +80,7 @@ class CarlaHostSignals(QObject):
TransportModeChangedCallback = pyqtSignal(int) TransportModeChangedCallback = pyqtSignal(int)
BufferSizeChangedCallback = pyqtSignal(int) BufferSizeChangedCallback = pyqtSignal(int)
SampleRateChangedCallback = pyqtSignal(float) SampleRateChangedCallback = pyqtSignal(float)
NSMCallback = pyqtSignal(int, int, str)
InfoCallback = pyqtSignal(str) InfoCallback = pyqtSignal(str)
ErrorCallback = pyqtSignal(str) ErrorCallback = pyqtSignal(str)
QuitCallback = pyqtSignal() QuitCallback = pyqtSignal()


+ 58
- 6
source/carla_host.py View File

@@ -121,9 +121,9 @@ class HostWindow(QMainWindow):
elif LADISH_APP_NAME: elif LADISH_APP_NAME:
self.fClientName = LADISH_APP_NAME self.fClientName = LADISH_APP_NAME
self.fSessionManagerName = "LADISH" self.fSessionManagerName = "LADISH"
elif NSM_URL:
self.fClientName = "Carla" # "Carla.tmp"
self.fSessionManagerName = "Non Session Manager"
elif NSM_URL and host.nsmOK:
self.fClientName = "Carla.tmp"
self.fSessionManagerName = "Non Session Manager TMP"
else: else:
self.fClientName = CARLA_CLIENT_NAME or "Carla" self.fClientName = CARLA_CLIENT_NAME or "Carla"
self.fSessionManagerName = "" self.fSessionManagerName = ""
@@ -387,6 +387,8 @@ class HostWindow(QMainWindow):
host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback) host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback) host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)


host.NSMCallback.connect(self.slot_handleNSMCallback)

host.DebugCallback.connect(self.slot_handleDebugCallback) host.DebugCallback.connect(self.slot_handleDebugCallback)
host.InfoCallback.connect(self.slot_handleInfoCallback) host.InfoCallback.connect(self.slot_handleInfoCallback)
host.ErrorCallback.connect(self.slot_handleErrorCallback) host.ErrorCallback.connect(self.slot_handleErrorCallback)
@@ -407,6 +409,13 @@ class HostWindow(QMainWindow):
if self.host.isPlugin: if self.host.isPlugin:
self.startTimers() self.startTimers()


# For NSM we wait for the open message
if NSM_URL and host.nsmOK:
self.fFirstEngineInit = False
self.setEngineSettings()
host.nsm_ready()
return

QTimer.singleShot(0, self.slot_engineStart) QTimer.singleShot(0, self.slot_engineStart)


# -------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------
@@ -429,7 +438,7 @@ class HostWindow(QMainWindow):
def setProperWindowTitle(self): def setProperWindowTitle(self):
title = self.fClientName title = self.fClientName


if self.fProjectFilename:
if self.fProjectFilename and not self.host.nsmOK:
title += " - %s" % os.path.basename(self.fProjectFilename) title += " - %s" % os.path.basename(self.fProjectFilename)
if self.fSessionManagerName: if self.fSessionManagerName:
title += " (%s)" % self.fSessionManagerName title += " (%s)" % self.fSessionManagerName
@@ -605,9 +614,11 @@ class HostWindow(QMainWindow):
self.ui.act_engine_start.setEnabled(False) self.ui.act_engine_start.setEnabled(False)
self.ui.act_engine_stop.setEnabled(True) self.ui.act_engine_stop.setEnabled(True)


canSave = (self.fProjectFilename and os.path.exists(self.fProjectFilename)) or not self.fSessionManagerName
self.ui.act_file_save.setEnabled(canSave)

if not self.fSessionManagerName: if not self.fSessionManagerName:
self.ui.act_file_open.setEnabled(True) self.ui.act_file_open.setEnabled(True)
self.ui.act_file_save.setEnabled(True)
self.ui.act_file_save_as.setEnabled(True) self.ui.act_file_save_as.setEnabled(True)


self.ui.panelTime.setEnabled(True) self.ui.panelTime.setEnabled(True)
@@ -1564,6 +1575,45 @@ class HostWindow(QMainWindow):


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


@pyqtSlot(int, int, str)
def slot_handleNSMCallback(self, value1, value2, valueStr):
print("--------------- NSM:", value1, value2, valueStr)

# Error
if value1 == 0:
pass

# Reply
elif value1 == 1:
self.fFirstEngineInit = False
self.fSessionManagerName = valueStr
self.setProperWindowTitle()

# Open
elif value1 == 2:
self.fClientName = os.path.basename(valueStr)
self.fProjectFilename = QFileInfo(valueStr+".carxp").absoluteFilePath()
self.setProperWindowTitle()
self.ui.act_file_save.setEnabled(True)

# Save
elif value1 == 3:
pass

# Session is Loaded
elif value1 == 4:
pass

# Show Optional Gui
elif value1 == 5:
self.show()

# Hide Optional Gui
elif value1 == 6:
self.hide()

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

@pyqtSlot(int, int, int, float, str) @pyqtSlot(int, int, int, float, str)
def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr): def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
print("DEBUG:", pluginId, value1, value2, value3, valueStr) print("DEBUG:", pluginId, value1, value2, value3, valueStr)
@@ -1951,6 +2001,8 @@ def engineCallback(host, action, pluginId, value1, value2, value3, valueStr):
host.BufferSizeChangedCallback.emit(value1) host.BufferSizeChangedCallback.emit(value1)
elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED: elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
host.SampleRateChangedCallback.emit(value3) host.SampleRateChangedCallback.emit(value3)
elif action == ENGINE_CALLBACK_NSM:
host.NSMCallback.emit(value1, value2, valueStr)
elif action == ENGINE_CALLBACK_IDLE: elif action == ENGINE_CALLBACK_IDLE:
QApplication.instance().processEvents() QApplication.instance().processEvents()
elif action == ENGINE_CALLBACK_INFO: elif action == ENGINE_CALLBACK_INFO:
@@ -2048,7 +2100,7 @@ def initHost(initName, libPrefixOrPluginClass, isControl, isPlugin, failError):
host.set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources) host.set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources)


if not isControl: if not isControl:
host.set_engine_option(ENGINE_OPTION_NSM_INIT, os.getpid(), initName)
host.nsmOK = host.nsm_init(os.getpid(), initName)


# -------------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------------
# Init utils # Init utils


+ 2
- 2
source/utils/CarlaBackendUtils.hpp View File

@@ -270,6 +270,8 @@ const char* EngineCallbackOpcode2Str(const EngineCallbackOpcode opcode) noexcept
return "ENGINE_CALLBACK_BUFFER_SIZE_CHANGED"; return "ENGINE_CALLBACK_BUFFER_SIZE_CHANGED";
case ENGINE_CALLBACK_SAMPLE_RATE_CHANGED: case ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
return "ENGINE_CALLBACK_SAMPLE_RATE_CHANGED"; return "ENGINE_CALLBACK_SAMPLE_RATE_CHANGED";
case ENGINE_CALLBACK_NSM:
return "ENGINE_CALLBACK_NSM";
case ENGINE_CALLBACK_IDLE: case ENGINE_CALLBACK_IDLE:
return "ENGINE_CALLBACK_IDLE"; return "ENGINE_CALLBACK_IDLE";
case ENGINE_CALLBACK_INFO: case ENGINE_CALLBACK_INFO:
@@ -315,8 +317,6 @@ const char* EngineOption2Str(const EngineOption option) noexcept
return "ENGINE_OPTION_AUDIO_SAMPLE_RATE"; return "ENGINE_OPTION_AUDIO_SAMPLE_RATE";
case ENGINE_OPTION_AUDIO_DEVICE: case ENGINE_OPTION_AUDIO_DEVICE:
return "ENGINE_OPTION_AUDIO_DEVICE"; return "ENGINE_OPTION_AUDIO_DEVICE";
case ENGINE_OPTION_NSM_INIT:
return "ENGINE_OPTION_NSM_INIT";
case ENGINE_OPTION_PLUGIN_PATH: case ENGINE_OPTION_PLUGIN_PATH:
return "ENGINE_OPTION_PLUGIN_PATH"; return "ENGINE_OPTION_PLUGIN_PATH";
case ENGINE_OPTION_PATH_BINARIES: case ENGINE_OPTION_PATH_BINARIES:


Loading…
Cancel
Save