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.

695 lines
20KB

  1. /*
  2. * Carla Bridge Plugin
  3. * Copyright (C) 2012-2020 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. #ifndef BUILD_BRIDGE
  18. # error This file should not be compiled if not building bridge
  19. #endif
  20. #include "CarlaEngine.hpp"
  21. #include "CarlaHost.h"
  22. #include "CarlaBackendUtils.hpp"
  23. #include "CarlaJuceUtils.hpp"
  24. #include "CarlaMainLoop.hpp"
  25. #include "CarlaMIDI.h"
  26. #ifdef CARLA_OS_UNIX
  27. # include <signal.h>
  28. #endif
  29. #ifdef CARLA_OS_LINUX
  30. # include <sched.h>
  31. # define SCHED_RESET_ON_FORK 0x40000000
  32. #endif
  33. #ifdef CARLA_OS_WIN
  34. # include <pthread.h>
  35. # include <objbase.h>
  36. #endif
  37. #ifdef HAVE_X11
  38. # include <X11/Xlib.h>
  39. #endif
  40. #ifdef USING_JUCE
  41. # if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  42. # pragma GCC diagnostic push
  43. # pragma GCC diagnostic ignored "-Wconversion"
  44. # pragma GCC diagnostic ignored "-Weffc++"
  45. # pragma GCC diagnostic ignored "-Wsign-conversion"
  46. # pragma GCC diagnostic ignored "-Wundef"
  47. # pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
  48. # endif
  49. # include "AppConfig.h"
  50. # if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  51. # include "juce_gui_basics/juce_gui_basics.h"
  52. # else
  53. # include "juce_events/juce_events.h"
  54. # endif
  55. # if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
  56. # pragma GCC diagnostic pop
  57. # endif
  58. #endif
  59. #include "jackbridge/JackBridge.hpp"
  60. #include "water/files/File.h"
  61. using CarlaBackend::CarlaEngine;
  62. using CarlaBackend::EngineCallbackOpcode;
  63. using CarlaBackend::EngineCallbackOpcode2Str;
  64. using CarlaBackend::runMainLoopOnce;
  65. using water::CharPointer_UTF8;
  66. using water::File;
  67. using water::String;
  68. // -------------------------------------------------------------------------
  69. static bool gIsInitiated = false;
  70. static volatile bool gCloseNow = false;
  71. static volatile bool gSaveNow = false;
  72. #if defined(CARLA_OS_UNIX)
  73. static void closeSignalHandler(int) noexcept
  74. {
  75. gCloseNow = true;
  76. }
  77. static void saveSignalHandler(int) noexcept
  78. {
  79. gSaveNow = true;
  80. }
  81. #elif defined(CARLA_OS_WIN)
  82. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  83. {
  84. if (dwCtrlType == CTRL_C_EVENT)
  85. {
  86. gCloseNow = true;
  87. return TRUE;
  88. }
  89. return FALSE;
  90. }
  91. #endif
  92. static void initSignalHandler()
  93. {
  94. #if defined(CARLA_OS_UNIX)
  95. struct sigaction sig;
  96. carla_zeroStruct(sig);
  97. sig.sa_handler = closeSignalHandler;
  98. sig.sa_flags = SA_RESTART;
  99. sigemptyset(&sig.sa_mask);
  100. sigaction(SIGTERM, &sig, nullptr);
  101. sigaction(SIGINT, &sig, nullptr);
  102. sig.sa_handler = saveSignalHandler;
  103. sig.sa_flags = SA_RESTART;
  104. sigemptyset(&sig.sa_mask);
  105. sigaction(SIGUSR1, &sig, nullptr);
  106. #elif defined(CARLA_OS_WIN)
  107. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  108. #endif
  109. }
  110. // -------------------------------------------------------------------------
  111. static String gProjectFilename;
  112. static CarlaHostHandle gHostHandle;
  113. static void gIdle()
  114. {
  115. carla_engine_idle(gHostHandle);
  116. if (gSaveNow)
  117. {
  118. gSaveNow = false;
  119. if (gProjectFilename.isNotEmpty())
  120. {
  121. if (! carla_save_plugin_state(gHostHandle, 0, gProjectFilename.toRawUTF8()))
  122. carla_stderr("Plugin preset save failed, error was:\n%s", carla_get_last_error(gHostHandle));
  123. }
  124. }
  125. }
  126. // -------------------------------------------------------------------------
  127. #if defined(USING_JUCE) && (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN))
  128. class CarlaJuceApp : public juce::JUCEApplication,
  129. private juce::Timer
  130. {
  131. public:
  132. CarlaJuceApp() {}
  133. ~CarlaJuceApp() {}
  134. void initialise(const juce::String&) override
  135. {
  136. startTimer(8);
  137. }
  138. void shutdown() override
  139. {
  140. gCloseNow = true;
  141. stopTimer();
  142. }
  143. const juce::String getApplicationName() override
  144. {
  145. return "CarlaPlugin";
  146. }
  147. const juce::String getApplicationVersion() override
  148. {
  149. return CARLA_VERSION_STRING;
  150. }
  151. void timerCallback() override
  152. {
  153. gIdle();
  154. if (gCloseNow)
  155. {
  156. quit();
  157. gCloseNow = false;
  158. }
  159. }
  160. };
  161. static juce::JUCEApplicationBase* juce_CreateApplication() { return new CarlaJuceApp(); }
  162. #endif
  163. // -------------------------------------------------------------------------
  164. class CarlaBridgePlugin
  165. {
  166. public:
  167. CarlaBridgePlugin(const bool useBridge, const char* const clientName, const char* const audioPoolBaseName,
  168. const char* const rtClientBaseName, const char* const nonRtClientBaseName, const char* const nonRtServerBaseName)
  169. : fEngine(nullptr),
  170. #ifdef USING_JUCE
  171. fJuceInitialiser(),
  172. #endif
  173. fUsingBridge(false),
  174. fUsingExec(false)
  175. {
  176. CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0');
  177. carla_debug("CarlaBridgePlugin::CarlaBridgePlugin(%s, \"%s\", %s, %s, %s, %s)",
  178. bool2str(useBridge), clientName, audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName);
  179. carla_set_engine_callback(gHostHandle, callback, this);
  180. if (useBridge)
  181. {
  182. carla_engine_init_bridge(gHostHandle,
  183. audioPoolBaseName,
  184. rtClientBaseName,
  185. nonRtClientBaseName,
  186. nonRtServerBaseName,
  187. clientName);
  188. }
  189. else if (std::getenv("CARLA_BRIDGE_DUMMY") != nullptr)
  190. {
  191. carla_engine_init(gHostHandle, "Dummy", clientName);
  192. }
  193. else
  194. {
  195. carla_engine_init(gHostHandle, "JACK", clientName);
  196. }
  197. fEngine = carla_get_engine_from_handle(gHostHandle);
  198. }
  199. ~CarlaBridgePlugin()
  200. {
  201. carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()");
  202. if (! fUsingExec)
  203. carla_engine_close(gHostHandle);
  204. }
  205. bool isOk() const noexcept
  206. {
  207. return (fEngine != nullptr);
  208. }
  209. // ---------------------------------------------------------------------
  210. void exec(const bool useBridge)
  211. {
  212. fUsingBridge = useBridge;
  213. fUsingExec = true;
  214. if (! useBridge)
  215. {
  216. const CarlaPluginInfo* const pInfo = carla_get_plugin_info(gHostHandle, 0);
  217. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr,);
  218. gProjectFilename = CharPointer_UTF8(pInfo->name);
  219. gProjectFilename += ".carxs";
  220. if (! File::isAbsolutePath(gProjectFilename))
  221. gProjectFilename = File::getCurrentWorkingDirectory().getChildFile(gProjectFilename).getFullPathName();
  222. if (File(gProjectFilename).existsAsFile())
  223. {
  224. if (carla_load_plugin_state(gHostHandle, 0, gProjectFilename.toRawUTF8()))
  225. carla_stdout("Plugin state loaded successfully");
  226. else
  227. carla_stderr("Plugin state load failed, error was:\n%s", carla_get_last_error(gHostHandle));
  228. }
  229. else
  230. {
  231. carla_stdout("Previous plugin state in '%s' is non-existent, will start from default state",
  232. gProjectFilename.toRawUTF8());
  233. }
  234. }
  235. gIsInitiated = true;
  236. #if defined(USING_JUCE) && (defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN))
  237. # ifndef CARLA_OS_WIN
  238. static const int argc = 0;
  239. static const char* argv[] = {};
  240. # endif
  241. juce::JUCEApplicationBase::createInstance = &juce_CreateApplication;
  242. juce::JUCEApplicationBase::main(JUCE_MAIN_FUNCTION_ARGS);
  243. #else
  244. for (; runMainLoopOnce() && ! gCloseNow;)
  245. {
  246. gIdle();
  247. # if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  248. // MacOS and Win32 have event-loops to run, so minimize sleep time
  249. carla_msleep(1);
  250. # else
  251. carla_msleep(5);
  252. # endif
  253. }
  254. #endif
  255. carla_engine_close(gHostHandle);
  256. }
  257. // ---------------------------------------------------------------------
  258. protected:
  259. void handleCallback(const EngineCallbackOpcode action,
  260. const int value1,
  261. const int, const int, const float, const char* const)
  262. {
  263. CARLA_BACKEND_USE_NAMESPACE;
  264. switch (action)
  265. {
  266. case ENGINE_CALLBACK_ENGINE_STOPPED:
  267. case ENGINE_CALLBACK_PLUGIN_REMOVED:
  268. case ENGINE_CALLBACK_QUIT:
  269. gCloseNow = true;
  270. break;
  271. case ENGINE_CALLBACK_UI_STATE_CHANGED:
  272. if (gIsInitiated && value1 != 1 && ! fUsingBridge)
  273. gCloseNow = true;
  274. break;
  275. default:
  276. break;
  277. }
  278. }
  279. private:
  280. const CarlaEngine* fEngine;
  281. #ifdef USING_JUCE
  282. const juce::ScopedJuceInitialiser_GUI fJuceInitialiser;
  283. #endif
  284. bool fUsingBridge;
  285. bool fUsingExec;
  286. static void callback(void* ptr, EngineCallbackOpcode action, unsigned int pluginId,
  287. int value1, int value2, int value3,
  288. float valuef, const char* valueStr)
  289. {
  290. carla_debug("CarlaBridgePlugin::callback(%p, %i:%s, %i, %i, %i, %i, %f, \"%s\")",
  291. ptr, action, EngineCallbackOpcode2Str(action),
  292. pluginId, value1, value2, value3, static_cast<double>(valuef), valueStr);
  293. // ptr must not be null
  294. CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
  295. #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
  296. // pluginId must be 0 (first), except for patchbay things
  297. if (action < CarlaBackend::ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED ||
  298. action > CarlaBackend::ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED)
  299. #endif
  300. {
  301. CARLA_SAFE_ASSERT_UINT_RETURN(pluginId == 0, pluginId,);
  302. }
  303. return ((CarlaBridgePlugin*)ptr)->handleCallback(action, value1, value2, value3, valuef, valueStr);
  304. }
  305. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgePlugin)
  306. };
  307. // -------------------------------------------------------------------------
  308. int main(int argc, char* argv[])
  309. {
  310. // ---------------------------------------------------------------------
  311. // Check argument count
  312. if (argc != 4 && argc != 5)
  313. {
  314. carla_stdout("usage: %s <type> <filename> <label> [uniqueId]", argv[0]);
  315. return 1;
  316. }
  317. #if defined(CARLA_OS_WIN) && ! defined(BUILDING_CARLA_FOR_WINDOWS)
  318. // ---------------------------------------------------------------------
  319. // Test if bridge is working
  320. if (! jackbridge_is_ok())
  321. {
  322. carla_stderr("A JACK or Wine library is missing, cannot continue");
  323. return 1;
  324. }
  325. #endif
  326. // ---------------------------------------------------------------------
  327. // Get args
  328. const char* const stype = argv[1];
  329. const char* filename = argv[2];
  330. const char* label = argv[3];
  331. const int64_t uniqueId = (argc == 5) ? static_cast<int64_t>(std::atoll(argv[4])) : 0;
  332. if (filename[0] == '\0' || std::strcmp(filename, "(none)") == 0)
  333. filename = nullptr;
  334. if (label[0] == '\0' || std::strcmp(label, "(none)") == 0)
  335. label = nullptr;
  336. // ---------------------------------------------------------------------
  337. // Check binary type
  338. CarlaBackend::BinaryType btype = CarlaBackend::BINARY_NATIVE;
  339. if (const char* const binaryTypeStr = std::getenv("CARLA_BRIDGE_PLUGIN_BINARY_TYPE"))
  340. btype = CarlaBackend::getBinaryTypeFromString(binaryTypeStr);
  341. if (btype == CarlaBackend::BINARY_NONE)
  342. {
  343. carla_stderr("Invalid binary type '%i'", btype);
  344. return 1;
  345. }
  346. // ---------------------------------------------------------------------
  347. // Check plugin type
  348. CarlaBackend::PluginType itype(CarlaBackend::getPluginTypeFromString(stype));
  349. if (itype == CarlaBackend::PLUGIN_NONE)
  350. {
  351. carla_stderr("Invalid plugin type '%s'", stype);
  352. return 1;
  353. }
  354. // ---------------------------------------------------------------------
  355. // Set file
  356. const File file(filename != nullptr ? filename : "");
  357. // ---------------------------------------------------------------------
  358. // Set name
  359. const char* name(std::getenv("CARLA_CLIENT_NAME"));
  360. if (name != nullptr && (name[0] == '\0' || std::strcmp(name, "(none)") == 0))
  361. name = nullptr;
  362. // ---------------------------------------------------------------------
  363. // Setup options
  364. const char* const shmIds(std::getenv("ENGINE_BRIDGE_SHM_IDS"));
  365. const bool useBridge = (shmIds != nullptr);
  366. // ---------------------------------------------------------------------
  367. // Setup bridge ids
  368. char audioPoolBaseName[6+1];
  369. char rtClientBaseName[6+1];
  370. char nonRtClientBaseName[6+1];
  371. char nonRtServerBaseName[6+1];
  372. if (useBridge)
  373. {
  374. CARLA_SAFE_ASSERT_RETURN(std::strlen(shmIds) == 6*4, 1);
  375. std::strncpy(audioPoolBaseName, shmIds+6*0, 6);
  376. std::strncpy(rtClientBaseName, shmIds+6*1, 6);
  377. std::strncpy(nonRtClientBaseName, shmIds+6*2, 6);
  378. std::strncpy(nonRtServerBaseName, shmIds+6*3, 6);
  379. audioPoolBaseName[6] = '\0';
  380. rtClientBaseName[6] = '\0';
  381. nonRtClientBaseName[6] = '\0';
  382. nonRtServerBaseName[6] = '\0';
  383. jackbridge_parent_deathsig(false);
  384. }
  385. else
  386. {
  387. audioPoolBaseName[0] = '\0';
  388. rtClientBaseName[0] = '\0';
  389. nonRtClientBaseName[0] = '\0';
  390. nonRtServerBaseName[0] = '\0';
  391. jackbridge_init();
  392. }
  393. // ---------------------------------------------------------------------
  394. // Set client name
  395. CarlaString clientName;
  396. if (name != nullptr)
  397. {
  398. clientName = name;
  399. }
  400. else if (itype == CarlaBackend::PLUGIN_LV2)
  401. {
  402. // LV2 requires URI
  403. CARLA_SAFE_ASSERT_RETURN(label != nullptr && label[0] != '\0', 1);
  404. // LV2 URI is not usable as client name, create a usable name from URI
  405. CarlaString label2(label);
  406. // truncate until last valid char
  407. for (std::size_t i=label2.length()-1; i != 0; --i)
  408. {
  409. if (! std::isalnum(label2[i]))
  410. continue;
  411. label2.truncate(i+1);
  412. break;
  413. }
  414. // get last used separator
  415. bool found;
  416. std::size_t septmp, sep = 0;
  417. septmp = label2.rfind('#', &found)+1;
  418. if (found && septmp > sep)
  419. sep = septmp;
  420. septmp = label2.rfind('/', &found)+1;
  421. if (found && septmp > sep)
  422. sep = septmp;
  423. septmp = label2.rfind('=', &found)+1;
  424. if (found && septmp > sep)
  425. sep = septmp;
  426. septmp = label2.rfind(':', &found)+1;
  427. if (found && septmp > sep)
  428. sep = septmp;
  429. // make name starting from the separator and first valid char
  430. const char* name2 = label2.buffer() + sep;
  431. for (; *name2 != '\0' && ! std::isalnum(*name2); ++name2) {}
  432. if (*name2 != '\0')
  433. clientName = name2;
  434. }
  435. else if (label != nullptr)
  436. {
  437. clientName = label;
  438. }
  439. else
  440. {
  441. clientName = file.getFileNameWithoutExtension().toRawUTF8();
  442. }
  443. // if we still have no client name by now, use a dummy one
  444. if (clientName.isEmpty())
  445. clientName = "carla-plugin";
  446. // just to be safe
  447. clientName.toBasic();
  448. // ---------------------------------------------------------------------
  449. // Set extraStuff
  450. const void* extraStuff = nullptr;
  451. if (itype == CarlaBackend::PLUGIN_SF2)
  452. {
  453. if (label == nullptr)
  454. label = clientName;
  455. if (std::strstr(label, " (16 outs)") != nullptr)
  456. extraStuff = "true";
  457. }
  458. // ---------------------------------------------------------------------
  459. // Initialize OS features
  460. #ifdef CARLA_OS_WIN
  461. OleInitialize(nullptr);
  462. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  463. # ifndef __WINPTHREADS_VERSION
  464. // (non-portable) initialization of statically linked pthread library
  465. pthread_win32_process_attach_np();
  466. pthread_win32_thread_attach_np();
  467. # endif
  468. #endif
  469. #ifdef HAVE_X11
  470. if (std::getenv("DISPLAY") != nullptr)
  471. XInitThreads();
  472. #endif
  473. // ---------------------------------------------------------------------
  474. // Set ourselves with high priority
  475. #ifdef CARLA_OS_LINUX
  476. // reset scheduler to normal mode
  477. struct sched_param sparam;
  478. carla_zeroStruct(sparam);
  479. sched_setscheduler(0, SCHED_OTHER|SCHED_RESET_ON_FORK, &sparam);
  480. // try niceness first, if it fails, try SCHED_RR
  481. if (nice(-5) < 0)
  482. {
  483. sparam.sched_priority = (sched_get_priority_max(SCHED_RR) + sched_get_priority_min(SCHED_RR*7)) / 8;
  484. if (sparam.sched_priority > 0)
  485. {
  486. if (sched_setscheduler(0, SCHED_RR|SCHED_RESET_ON_FORK, &sparam) < 0)
  487. {
  488. CarlaString error(std::strerror(errno));
  489. carla_stderr("Failed to set high priority, error %i: %s", errno, error.buffer());
  490. }
  491. }
  492. }
  493. #endif
  494. #ifdef CARLA_OS_WIN
  495. if (! SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS))
  496. carla_stderr("Failed to set high priority.");
  497. #endif
  498. // ---------------------------------------------------------------------
  499. // Listen for ctrl+c or sigint/sigterm events
  500. initSignalHandler();
  501. // ---------------------------------------------------------------------
  502. // Init plugin bridge
  503. int ret;
  504. {
  505. gHostHandle = carla_standalone_host_init();
  506. CarlaBridgePlugin bridge(useBridge, clientName,
  507. audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName);
  508. if (! bridge.isOk())
  509. {
  510. carla_stderr("Failed to init engine, error was:\n%s", carla_get_last_error(gHostHandle));
  511. return 1;
  512. }
  513. // -----------------------------------------------------------------
  514. // Init plugin
  515. if (carla_add_plugin(gHostHandle,
  516. btype, itype,
  517. file.getFullPathName().toRawUTF8(), name, label, uniqueId, extraStuff, 0x0))
  518. {
  519. ret = 0;
  520. if (! useBridge)
  521. {
  522. carla_set_active(gHostHandle, 0, true);
  523. if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(gHostHandle, 0))
  524. {
  525. if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI)
  526. {
  527. #ifdef HAVE_X11
  528. if (std::getenv("DISPLAY") != nullptr)
  529. #endif
  530. carla_show_custom_ui(gHostHandle, 0, true);
  531. }
  532. }
  533. }
  534. bridge.exec(useBridge);
  535. }
  536. else
  537. {
  538. ret = 1;
  539. const char* const lastError(carla_get_last_error(gHostHandle));
  540. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  541. if (useBridge)
  542. {
  543. // do a single idle so that we can send error message to server
  544. gIdle();
  545. #ifdef CARLA_OS_UNIX
  546. // kill ourselves now if we can't load plugin in bridge mode
  547. ::kill(::getpid(), SIGKILL);
  548. #endif
  549. }
  550. }
  551. }
  552. #ifdef CARLA_OS_WIN
  553. #ifndef __WINPTHREADS_VERSION
  554. pthread_win32_thread_detach_np();
  555. pthread_win32_process_detach_np();
  556. #endif
  557. CoUninitialize();
  558. OleUninitialize();
  559. #endif
  560. return ret;
  561. }