Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

608 lines
20KB

  1. /*
  2. * Carla Standalone
  3. * Copyright (C) 2011-2017 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaDefines.h"
  18. #ifdef HAVE_LIBLO
  19. #define NSM_API_VERSION_MAJOR 1
  20. #define NSM_API_VERSION_MINOR 2
  21. #define NSM_CLIENT_FEATURES ":switch:"
  22. //#define NSM_CLIENT_FEATURES ":switch:optional-gui:"
  23. #include "CarlaHost.h"
  24. #include "CarlaOscUtils.hpp"
  25. #include "CarlaString.hpp"
  26. #include "AppConfig.h"
  27. #include "juce_core/juce_core.h"
  28. namespace CB = CarlaBackend;
  29. // -------------------------------------------------------------------------------------------------------------------
  30. struct CarlaBackendStandalone {
  31. CarlaEngine* engine;
  32. EngineCallbackFunc engineCallback;
  33. void* engineCallbackPtr;
  34. // ...
  35. };
  36. extern CarlaBackendStandalone gStandalone;
  37. // -------------------------------------------------------------------------------------------------------------------
  38. class CarlaNSM
  39. {
  40. public:
  41. CarlaNSM() noexcept
  42. : fReplyAddress(nullptr),
  43. fServer(nullptr),
  44. fServerThread(nullptr),
  45. fServerURL(nullptr),
  46. fClientNameId(),
  47. fProjectPath(),
  48. fHasBroadcast(false),
  49. fHasOptionalGui(false),
  50. fHasServerControl(false),
  51. fStarted(),
  52. fReadyActionOpen(true),
  53. fReadyActionSave(true) {}
  54. ~CarlaNSM()
  55. {
  56. CARLA_SAFE_ASSERT(fReadyActionOpen);
  57. CARLA_SAFE_ASSERT(fReadyActionSave);
  58. if (fServerThread != nullptr)
  59. {
  60. lo_server_thread_stop(fServerThread);
  61. lo_server_thread_free(fServerThread);
  62. fServerThread = nullptr;
  63. fServer = nullptr;
  64. }
  65. if (fServerURL != nullptr)
  66. {
  67. std::free(fServerURL);
  68. fServerURL = nullptr;
  69. }
  70. if (fReplyAddress != nullptr)
  71. {
  72. lo_address_free(fReplyAddress);
  73. fReplyAddress = nullptr;
  74. }
  75. }
  76. bool announce(const int pid, const char* const executableName)
  77. {
  78. CARLA_SAFE_ASSERT_RETURN(pid != 0, false);
  79. CARLA_SAFE_ASSERT_RETURN(executableName != nullptr && executableName[0] != '\0', false);
  80. const char* const NSM_URL(std::getenv("NSM_URL"));
  81. if (NSM_URL == nullptr)
  82. return false;
  83. const lo_address nsmAddress(lo_address_new_from_url(NSM_URL));
  84. CARLA_SAFE_ASSERT_RETURN(nsmAddress != nullptr, false);
  85. const int proto = lo_address_get_protocol(nsmAddress);
  86. if (fServerThread == nullptr)
  87. {
  88. // create new OSC server
  89. fServerThread = lo_server_thread_new_with_proto(nullptr, proto, _osc_error_handler);
  90. CARLA_SAFE_ASSERT_RETURN(fServerThread != nullptr, false);
  91. // register message handlers
  92. lo_server_thread_add_method(fServerThread, "/error", "sis", _error_handler, this);
  93. lo_server_thread_add_method(fServerThread, "/reply", "ssss", _reply_handler, this);
  94. lo_server_thread_add_method(fServerThread, "/nsm/client/open", "sss", _open_handler, this);
  95. lo_server_thread_add_method(fServerThread, "/nsm/client/save", "", _save_handler, this);
  96. lo_server_thread_add_method(fServerThread, "/nsm/client/session_is_loaded", "", _loaded_handler, this);
  97. lo_server_thread_add_method(fServerThread, "/nsm/client/show_optional_gui", "", _show_gui_handler, this);
  98. lo_server_thread_add_method(fServerThread, "/nsm/client/hide_optional_gui", "", _hide_gui_handler, this);
  99. lo_server_thread_add_method(fServerThread, nullptr, nullptr, _broadcast_handler, this);
  100. fServer = lo_server_thread_get_server(fServerThread);
  101. fServerURL = lo_server_thread_get_url(fServerThread);
  102. }
  103. lo_send_from(nsmAddress, fServer, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii",
  104. "Carla", NSM_CLIENT_FEATURES, executableName, NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, pid);
  105. lo_address_free(nsmAddress);
  106. return true;
  107. }
  108. void ready(const int action)
  109. {
  110. CARLA_SAFE_ASSERT_RETURN(fServerThread != nullptr,);
  111. switch (action)
  112. {
  113. case -1: // init
  114. CARLA_SAFE_ASSERT_BREAK(! fStarted);
  115. fStarted = true;
  116. lo_server_thread_start(fServerThread);
  117. break;
  118. case 0: // error
  119. break;
  120. case 1: // reply
  121. break;
  122. case 2: // open
  123. fReadyActionOpen = true;
  124. break;
  125. case 3: // save
  126. fReadyActionSave = true;
  127. break;
  128. case 4: // session loaded
  129. break;
  130. case 5: // show gui
  131. CARLA_SAFE_ASSERT_BREAK(fReplyAddress != nullptr);
  132. CARLA_SAFE_ASSERT_BREAK(fServer != nullptr);
  133. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");
  134. break;
  135. case 6: // hide gui
  136. CARLA_SAFE_ASSERT_BREAK(fReplyAddress != nullptr);
  137. CARLA_SAFE_ASSERT_BREAK(fServer != nullptr);
  138. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_hidden", "");
  139. break;
  140. }
  141. }
  142. static CarlaNSM& getInstance()
  143. {
  144. static CarlaNSM sInstance;
  145. return sInstance;
  146. }
  147. protected:
  148. int handleError(const char* const method, const int code, const char* const message)
  149. {
  150. carla_stdout("CarlaNSM::handleError(\"%s\", %i, \"%s\")", method, code, message);
  151. if (gStandalone.engineCallback != nullptr)
  152. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 0, code, 0.0f, message);
  153. return 1;
  154. // may be unused
  155. (void)method;
  156. }
  157. int handleReply(const char* const method, const char* const message, const char* const smName, const char* const features,
  158. const lo_message msg)
  159. {
  160. CARLA_SAFE_ASSERT_RETURN(fServerThread != nullptr, 1);
  161. carla_stdout("CarlaNSM::handleReply(\"%s\", \"%s\", \"%s\", \"%s\")", method, message, smName, features);
  162. if (std::strcmp(method, "/nsm/server/announce") == 0)
  163. {
  164. const lo_address msgAddress(lo_message_get_source(msg));
  165. CARLA_SAFE_ASSERT_RETURN(msgAddress != nullptr, 0);
  166. char* const msgURL(lo_address_get_url(msgAddress));
  167. CARLA_SAFE_ASSERT_RETURN(msgURL != nullptr, 0);
  168. if (fReplyAddress != nullptr)
  169. lo_address_free(fReplyAddress);
  170. fReplyAddress = lo_address_new_from_url(msgURL);
  171. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 0);
  172. fHasBroadcast = std::strstr(features, ":broadcast:") != nullptr;
  173. fHasOptionalGui = std::strstr(features, ":optional-gui:") != nullptr;
  174. fHasServerControl = std::strstr(features, ":server_control:") != nullptr;
  175. #if 0
  176. // UI starts visible
  177. if (fHasOptionalGui)
  178. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/nsm/client/gui_is_shown", "");
  179. #endif
  180. carla_stdout("Carla started via '%s', message: %s", smName, message);
  181. if (gStandalone.engineCallback != nullptr)
  182. {
  183. int flags = 0;
  184. if (fHasBroadcast)
  185. flags |= 1 << 0;
  186. if (fHasOptionalGui)
  187. flags |= 1 << 1;
  188. if (fHasServerControl)
  189. flags |= 1 << 2;
  190. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 1, flags, 0.0f, smName);
  191. }
  192. std::free(msgURL);
  193. }
  194. else
  195. {
  196. carla_stdout("Got unknown NSM reply method '%s'", method);
  197. }
  198. return 0;
  199. }
  200. int handleOpen(const char* const projectPath, const char* const displayName, const char* const clientNameId)
  201. {
  202. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  203. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  204. carla_stdout("CarlaNSM::handleOpen(\"%s\", \"%s\", \"%s\")", projectPath, displayName, clientNameId);
  205. if (gStandalone.engineCallback != nullptr)
  206. {
  207. fReadyActionOpen = false;
  208. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 2, 0, 0.0f, projectPath);
  209. for (; ! fReadyActionOpen;)
  210. carla_msleep(10);
  211. }
  212. else
  213. {
  214. using namespace juce;
  215. if (carla_is_engine_running())
  216. carla_engine_close();
  217. carla_engine_init("JACK", clientNameId);
  218. fProjectPath = projectPath;
  219. fProjectPath += ".carxp";
  220. const String jfilename = String(CharPointer_UTF8(fProjectPath));
  221. if (File(jfilename).existsAsFile())
  222. carla_load_project(fProjectPath);
  223. }
  224. fClientNameId = clientNameId;
  225. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/open", "OK");
  226. // Broadcast ourselves
  227. if (fHasBroadcast)
  228. {
  229. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/nsm/server/broadcast", "sssss",
  230. "/non/hello", fServerURL, "Carla", CARLA_VERSION_STRING, fClientNameId.buffer());
  231. }
  232. return 0;
  233. }
  234. int handleSave()
  235. {
  236. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  237. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  238. carla_stdout("CarlaNSM::handleSave()");
  239. if (gStandalone.engineCallback != nullptr)
  240. {
  241. fReadyActionSave = false;
  242. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 3, 0, 0.0f, nullptr);
  243. for (; ! fReadyActionSave;)
  244. carla_msleep(10);
  245. }
  246. else
  247. {
  248. CARLA_SAFE_ASSERT_RETURN(fProjectPath.isNotEmpty(), 0);
  249. carla_save_project(fProjectPath);
  250. }
  251. lo_send_from(fReplyAddress, fServer, LO_TT_IMMEDIATE, "/reply", "ss", "/nsm/client/save", "OK");
  252. return 0;
  253. }
  254. int handleSessionIsLoaded()
  255. {
  256. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  257. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  258. carla_stdout("CarlaNSM::handleSessionIsLoaded()");
  259. if (gStandalone.engineCallback != nullptr)
  260. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 4, 0, 0.0f, nullptr);
  261. return 0;
  262. }
  263. int handleShowOptionalGui()
  264. {
  265. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  266. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  267. carla_stdout("CarlaNSM::handleShowOptionalGui()");
  268. if (gStandalone.engineCallback != nullptr)
  269. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 5, 0, 0.0f, nullptr);
  270. return 0;
  271. }
  272. int handleHideOptionalGui()
  273. {
  274. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  275. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  276. carla_stdout("CarlaNSM::handleHideOptionalGui()");
  277. if (gStandalone.engineCallback != nullptr)
  278. gStandalone.engineCallback(gStandalone.engineCallbackPtr, CB::ENGINE_CALLBACK_NSM, 0, 6, 0, 0.0f, nullptr);
  279. return 0;
  280. }
  281. int handleBroadcast(const char* const path, const char* const types, lo_arg** const argv, const int argc,
  282. const lo_message msg)
  283. {
  284. CARLA_SAFE_ASSERT_RETURN(fReplyAddress != nullptr, 1);
  285. CARLA_SAFE_ASSERT_RETURN(fServer != nullptr, 1);
  286. CARLA_SAFE_ASSERT_RETURN(argc >= 0, 0);
  287. carla_stdout("CarlaNSM::handleBroadcast(%s, %s, %p, %i)", path, types, argv, argc);
  288. #if 0
  289. if (std::strcmp(path, "/non/hello") == 0)
  290. {
  291. CARLA_SAFE_ASSERT_RETURN(argc == 4, 0);
  292. CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 0);
  293. const char* const url = &argv[0]->s;
  294. //const char* const name = &argv[1]->s;
  295. //const char* const version = &argv[2]->s;
  296. //const char* const clientId = &argv[3]->s;
  297. const lo_address targetAddress(lo_address_new_from_url(url));
  298. CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);
  299. lo_send_from(targetAddress, fServer, LO_TT_IMMEDIATE, "/signal/hello", "ss",
  300. fClientNameId.buffer(), fServerURL);
  301. lo_address_free(targetAddress);
  302. return 0;
  303. }
  304. if (std::strcmp(path, "/signal/hello") == 0)
  305. {
  306. //const char* const name = &argv[0]->s;
  307. const char* const url = &argv[1]->s;
  308. const lo_address targetAddress(lo_address_new_from_url(url));
  309. CARLA_SAFE_ASSERT_RETURN(targetAddress != nullptr, 0);
  310. lo_send_from(targetAddress, fServer, LO_TT_IMMEDIATE, "/signal/hello", "ss",
  311. fClientNameId.buffer(), fServerURL);
  312. lo_address_free(targetAddress);
  313. return 0;
  314. }
  315. if (std::strcmp(path, "/signal/list") == 0)
  316. {
  317. carla_stdout("CarlaNSM::handleBroadcast - got list");
  318. CARLA_SAFE_ASSERT_RETURN(carla_is_engine_running(), 0);
  319. const char* prefix = nullptr;
  320. if (argc > 0)
  321. prefix = &argv[0]->s;
  322. const lo_address msgAddress(lo_message_get_source(msg));
  323. CARLA_SAFE_ASSERT_RETURN(msgAddress != nullptr, 0);
  324. for (uint32_t i = 0, pluginCount = carla_get_current_plugin_count(); i < pluginCount; ++i)
  325. {
  326. const CarlaPluginInfo* const pluginInfo(carla_get_plugin_info(i));
  327. CARLA_SAFE_ASSERT_CONTINUE(pluginInfo != nullptr);
  328. /*const*/ CarlaString pluginNameId(fClientNameId + "/" + CarlaString(pluginInfo->name).replace('/','_') + "/");
  329. for (uint32_t j=0, paramCount = carla_get_parameter_count(i); j < paramCount; ++j)
  330. {
  331. const CarlaParameterInfo* const paramInfo(carla_get_parameter_info(i, j));
  332. CARLA_SAFE_ASSERT_CONTINUE(paramInfo != nullptr);
  333. const ParameterData* const paramData(carla_get_parameter_data(i, j));
  334. CARLA_SAFE_ASSERT_CONTINUE(paramData != nullptr);
  335. const ParameterRanges* const paramRanges(carla_get_parameter_ranges(i, j));
  336. CARLA_SAFE_ASSERT_CONTINUE(paramRanges != nullptr);
  337. if (paramData->type != CB::PARAMETER_INPUT /*&& paramData->type != CB::PARAMETER_OUTPUT*/)
  338. continue;
  339. if ((paramData->hints & CB::PARAMETER_IS_ENABLED) == 0)
  340. continue;
  341. if ((paramData->hints & CB::PARAMETER_IS_AUTOMABLE) == 0)
  342. continue;
  343. if (paramData->hints & CB::PARAMETER_IS_READ_ONLY)
  344. continue;
  345. const char* const dir = paramData->type == CB::PARAMETER_INPUT ? "in" : "out";
  346. const CarlaString paramNameId = pluginNameId + CarlaString(paramInfo->name).replace('/','_');
  347. const float defNorm = paramRanges->getNormalizedValue(paramRanges->def);
  348. if (prefix == nullptr || std::strncmp(paramNameId, prefix, std::strlen(prefix)) == 0)
  349. {
  350. lo_send_from(msgAddress, fServer, LO_TT_IMMEDIATE, "/reply", "sssfff",
  351. path, paramNameId.buffer(), dir, 0.0f, 1.0f, defNorm);
  352. }
  353. }
  354. }
  355. lo_send_from(msgAddress, fServer, LO_TT_IMMEDIATE, "/reply", "s", path);
  356. //return 0;
  357. }
  358. for (int i=0; i<argc; ++i)
  359. if (types[i] == 's')
  360. carla_stdout("%i: %s", i+1, &argv[i]->s);
  361. #endif
  362. return 0;
  363. // unused
  364. (void)msg;
  365. }
  366. private:
  367. lo_address fReplyAddress;
  368. lo_server fServer;
  369. lo_server_thread fServerThread;
  370. char* fServerURL;
  371. CarlaString fClientNameId;
  372. CarlaString fProjectPath;
  373. bool fHasBroadcast;
  374. bool fHasOptionalGui;
  375. bool fHasServerControl;
  376. bool fStarted;
  377. volatile bool fReadyActionOpen;
  378. volatile bool fReadyActionSave;
  379. #define handlePtr ((CarlaNSM*)data)
  380. static void _osc_error_handler(int num, const char* msg, const char* path)
  381. {
  382. carla_stderr2("CarlaNSM::_osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
  383. }
  384. static int _error_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
  385. {
  386. CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
  387. CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sis") == 0, 1);
  388. const char* const method = &argv[0]->s;
  389. const int code = argv[1]->i;
  390. const char* const message = &argv[2]->s;
  391. return handlePtr->handleError(method, code, message);
  392. }
  393. static int _reply_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
  394. {
  395. CARLA_SAFE_ASSERT_RETURN(argc == 4, 1);
  396. CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "ssss") == 0, 1);
  397. const char* const method = &argv[0]->s;
  398. const char* const message = &argv[1]->s;
  399. const char* const smName = &argv[2]->s;
  400. const char* const features = &argv[3]->s;
  401. return handlePtr->handleReply(method, message, smName, features, msg);
  402. }
  403. static int _open_handler(const char*, const char* types, lo_arg** argv, int argc, lo_message, void* data)
  404. {
  405. CARLA_SAFE_ASSERT_RETURN(argc == 3, 1);
  406. CARLA_SAFE_ASSERT_RETURN(std::strcmp(types, "sss") == 0, 1);
  407. const char* const projectPath = &argv[0]->s;
  408. const char* const displayName = &argv[1]->s;
  409. const char* const clientNameId = &argv[2]->s;
  410. return handlePtr->handleOpen(projectPath, displayName, clientNameId);
  411. }
  412. static int _save_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
  413. {
  414. CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);
  415. return handlePtr->handleSave();
  416. }
  417. static int _loaded_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
  418. {
  419. CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);
  420. return handlePtr->handleSessionIsLoaded();
  421. }
  422. static int _show_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
  423. {
  424. CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);
  425. return handlePtr->handleShowOptionalGui();
  426. }
  427. static int _hide_gui_handler(const char*, const char*, lo_arg**, int argc, lo_message, void* data)
  428. {
  429. CARLA_SAFE_ASSERT_RETURN(argc == 0, 1);
  430. return handlePtr->handleHideOptionalGui();
  431. }
  432. static int _broadcast_handler(const char* path, const char* types, lo_arg** argv, int argc, lo_message msg, void* data)
  433. {
  434. return handlePtr->handleBroadcast(path, types, argv, argc, msg);
  435. }
  436. #undef handlePtr
  437. CARLA_PREVENT_HEAP_ALLOCATION
  438. CARLA_DECLARE_NON_COPY_CLASS(CarlaNSM)
  439. };
  440. #endif // HAVE_LIBLO
  441. // -------------------------------------------------------------------------------------------------------------------
  442. CARLA_EXPORT
  443. bool carla_nsm_init(int pid, const char* executableName);
  444. bool carla_nsm_init(int pid, const char* executableName)
  445. {
  446. #ifdef HAVE_LIBLO
  447. return CarlaNSM::getInstance().announce(pid, executableName);
  448. #else
  449. return false;
  450. // unused
  451. (void)pid; (void)executableName;
  452. #endif
  453. }
  454. CARLA_EXPORT
  455. void carla_nsm_ready(int action);
  456. void carla_nsm_ready(int action)
  457. {
  458. #ifdef HAVE_LIBLO
  459. CarlaNSM::getInstance().ready(action);
  460. #endif
  461. }
  462. // -------------------------------------------------------------------------------------------------------------------