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.

477 lines
13KB

  1. /*
  2. * Carla Bridge Plugin
  3. * Copyright (C) 2012-2014 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 "CarlaEngine.hpp"
  18. #include "CarlaHost.h"
  19. #include "CarlaBackendUtils.hpp"
  20. #include "CarlaOscUtils.hpp"
  21. #include "CarlaMIDI.h"
  22. #ifdef CARLA_OS_UNIX
  23. # include <signal.h>
  24. #endif
  25. #include "juce_core.h"
  26. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  27. # include "juce_gui_basics.h"
  28. using juce::JUCEApplication;
  29. using juce::JUCEApplicationBase;
  30. using juce::Timer;
  31. #endif
  32. using CarlaBackend::CarlaEngine;
  33. using CarlaBackend::EngineCallbackOpcode;
  34. using CarlaBackend::EngineCallbackOpcode2Str;
  35. using juce::File;
  36. using juce::String;
  37. // -------------------------------------------------------------------------
  38. static bool gIsInitiated = false;
  39. static volatile bool gCloseNow = false;
  40. static volatile bool gSaveNow = false;
  41. #ifdef CARLA_OS_WIN
  42. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  43. {
  44. if (dwCtrlType == CTRL_C_EVENT)
  45. {
  46. gCloseNow = true;
  47. return TRUE;
  48. }
  49. return FALSE;
  50. }
  51. #elif defined(CARLA_OS_LINUX)
  52. static void closeSignalHandler(int) noexcept
  53. {
  54. gCloseNow = true;
  55. }
  56. static void saveSignalHandler(int) noexcept
  57. {
  58. gSaveNow = true;
  59. }
  60. #endif
  61. static void initSignalHandler()
  62. {
  63. #ifdef CARLA_OS_WIN
  64. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  65. #elif defined(CARLA_OS_LINUX)
  66. struct sigaction sint;
  67. struct sigaction sterm;
  68. struct sigaction susr1;
  69. sint.sa_handler = closeSignalHandler;
  70. sint.sa_flags = SA_RESTART;
  71. sint.sa_restorer = nullptr;
  72. sigemptyset(&sint.sa_mask);
  73. sigaction(SIGINT, &sint, nullptr);
  74. sterm.sa_handler = closeSignalHandler;
  75. sterm.sa_flags = SA_RESTART;
  76. sterm.sa_restorer = nullptr;
  77. sigemptyset(&sterm.sa_mask);
  78. sigaction(SIGTERM, &sterm, nullptr);
  79. susr1.sa_handler = saveSignalHandler;
  80. susr1.sa_flags = SA_RESTART;
  81. susr1.sa_restorer = nullptr;
  82. sigemptyset(&susr1.sa_mask);
  83. sigaction(SIGUSR1, &susr1, nullptr);
  84. #endif
  85. }
  86. // -------------------------------------------------------------------------
  87. static CarlaString gProjectFilename;
  88. static void gIdle()
  89. {
  90. carla_engine_idle();
  91. if (gSaveNow)
  92. {
  93. gSaveNow = false;
  94. if (gProjectFilename.isNotEmpty())
  95. {
  96. if (! carla_save_plugin_state(0, gProjectFilename))
  97. carla_stderr("Plugin preset save failed, error was:\n%s", carla_get_last_error());
  98. }
  99. }
  100. }
  101. // -------------------------------------------------------------------------
  102. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  103. class CarlaJuceApp : public JUCEApplication,
  104. private Timer
  105. {
  106. public:
  107. CarlaJuceApp() {}
  108. ~CarlaJuceApp() {}
  109. void initialise(const String&) override
  110. {
  111. startTimer(15);
  112. }
  113. void shutdown() override
  114. {
  115. gCloseNow = true;
  116. stopTimer();
  117. }
  118. const String getApplicationName() override
  119. {
  120. return "CarlaPlugin";
  121. }
  122. const String getApplicationVersion() override
  123. {
  124. return CARLA_VERSION_STRING;
  125. }
  126. void timerCallback() override
  127. {
  128. gIdle();
  129. if (gCloseNow)
  130. {
  131. quit();
  132. gCloseNow = false;
  133. }
  134. }
  135. };
  136. static JUCEApplicationBase* juce_CreateApplication() { return new CarlaJuceApp(); }
  137. #endif
  138. // -------------------------------------------------------------------------
  139. class CarlaBridgePlugin
  140. {
  141. public:
  142. CarlaBridgePlugin(const bool useBridge, const char* const clientName, const char* const audioPoolBaseName,
  143. const char* const rtClientBaseName, const char* const nonRtClientBaseName, const char* const nonRtServerBaseName)
  144. : fEngine(nullptr),
  145. fProjFilename(),
  146. fOscControlData(),
  147. fOscServerPath(),
  148. fOscServerThread(nullptr),
  149. leakDetector_CarlaBridgePlugin()
  150. {
  151. CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0');
  152. carla_debug("CarlaBridgePlugin::CarlaBridgePlugin(%s, \"%s\", %s, %s, %s, %s)", bool2str(useBridge), clientName, audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName);
  153. carla_set_engine_callback(callback, this);
  154. if (useBridge)
  155. carla_engine_init_bridge(audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName, clientName);
  156. else
  157. carla_engine_init("JACK", clientName);
  158. fEngine = carla_get_engine();
  159. }
  160. ~CarlaBridgePlugin()
  161. {
  162. carla_debug("CarlaBridgePlugin::~CarlaBridgePlugin()");
  163. carla_engine_close();
  164. }
  165. bool isOk() const noexcept
  166. {
  167. return (fEngine != nullptr);
  168. }
  169. // ---------------------------------------------------------------------
  170. /*
  171. void sendOscUpdate() const noexcept
  172. {
  173. if (fOscControlData.target != nullptr)
  174. osc_send_update(fOscControlData, fOscServerPath);
  175. }
  176. void sendOscBridgeUpdate() const noexcept
  177. {
  178. if (fOscControlData.target != nullptr)
  179. osc_send_bridge_ready(fOscControlData, fOscControlData.path);
  180. }
  181. void sendOscBridgeError(const char* const error) const noexcept
  182. {
  183. if (fOscControlData.target != nullptr)
  184. osc_send_bridge_error(fOscControlData, error);
  185. }
  186. */
  187. // ---------------------------------------------------------------------
  188. void exec(const bool useOsc, int argc, char* argv[])
  189. {
  190. if (! useOsc)
  191. {
  192. const CarlaPluginInfo* const pInfo(carla_get_plugin_info(0));
  193. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr,);
  194. fProjFilename = pInfo->name;
  195. fProjFilename += ".carxs";
  196. if (! File::isAbsolutePath(fProjFilename))
  197. fProjFilename = File::getCurrentWorkingDirectory().getChildFile(fProjFilename).getFullPathName();
  198. if (File(fProjFilename).existsAsFile() && ! carla_load_plugin_state(0, fProjFilename.toRawUTF8()))
  199. carla_stderr("Plugin preset load failed, error was:\n%s", carla_get_last_error());
  200. }
  201. gIsInitiated = true;
  202. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  203. JUCEApplicationBase::createInstance = &juce_CreateApplication;
  204. JUCEApplicationBase::main(JUCE_MAIN_FUNCTION_ARGS);
  205. #else
  206. for (; ! gCloseNow;)
  207. {
  208. gIdle();
  209. carla_msleep(15);
  210. }
  211. #endif
  212. carla_set_engine_about_to_close();
  213. carla_remove_plugin(0);
  214. // may be unused
  215. return; (void)argc; (void)argv;
  216. }
  217. // ---------------------------------------------------------------------
  218. protected:
  219. void handleCallback(const EngineCallbackOpcode action, const int value1, const int value2, const float value3, const char* const valueStr)
  220. {
  221. CARLA_BACKEND_USE_NAMESPACE;
  222. // TODO
  223. switch (action)
  224. {
  225. case ENGINE_CALLBACK_ENGINE_STOPPED:
  226. case ENGINE_CALLBACK_PLUGIN_REMOVED:
  227. case ENGINE_CALLBACK_QUIT:
  228. gCloseNow = true;
  229. break;
  230. case ENGINE_CALLBACK_UI_STATE_CHANGED:
  231. if (gIsInitiated && value1 != 1 && fOscControlData.target == nullptr)
  232. gCloseNow = true;
  233. break;
  234. default:
  235. break;
  236. }
  237. return;
  238. (void)value2;
  239. (void)value3;
  240. (void)valueStr;
  241. }
  242. private:
  243. const CarlaEngine* fEngine;
  244. String fProjFilename;
  245. CarlaOscData fOscControlData;
  246. CarlaString fOscServerPath;
  247. lo_server_thread fOscServerThread;
  248. static void callback(void* ptr, EngineCallbackOpcode action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr)
  249. {
  250. carla_debug("CarlaBridgePlugin::callback(%p, %i:%s, %i, %i, %i, %f, \"%s\")", ptr, action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
  251. CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
  252. CARLA_SAFE_ASSERT_RETURN(pluginId == 0,);
  253. return ((CarlaBridgePlugin*)ptr)->handleCallback(action, value1, value2, value3, valueStr);
  254. }
  255. static void osc_error_handler(int num, const char* msg, const char* path)
  256. {
  257. carla_stderr("CarlaBridgePlugin::osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
  258. }
  259. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgePlugin)
  260. };
  261. // -------------------------------------------------------------------------
  262. int main(int argc, char* argv[])
  263. {
  264. // ---------------------------------------------------------------------
  265. // Check argument count
  266. if (argc != 4 && argc != 5)
  267. {
  268. carla_stdout("usage: %s <type> <filename> <label> [uniqueId]", argv[0]);
  269. return 1;
  270. }
  271. // ---------------------------------------------------------------------
  272. // Get args
  273. const char* const stype = argv[1];
  274. const char* const filename = argv[2];
  275. const char* label = argv[3];
  276. const int64_t uniqueId = (argc == 5) ? static_cast<int64_t>(std::atoll(argv[4])) : 0;
  277. // ---------------------------------------------------------------------
  278. // Check plugin type
  279. CarlaBackend::PluginType itype(CarlaBackend::getPluginTypeFromString(stype));
  280. if (itype == CarlaBackend::PLUGIN_NONE)
  281. {
  282. carla_stderr("Invalid plugin type '%s'", stype);
  283. return 1;
  284. }
  285. // ---------------------------------------------------------------------
  286. // Set name
  287. const char* name(std::getenv("CARLA_CLIENT_NAME"));
  288. if (name != nullptr && (name[0] == '\0' || std::strcmp(name, "(none)") == 0))
  289. name = nullptr;
  290. // ---------------------------------------------------------------------
  291. // Setup options
  292. const char* const shmIds(std::getenv("ENGINE_BRIDGE_SHM_IDS"));
  293. const bool useBridge = (shmIds != nullptr);
  294. // ---------------------------------------------------------------------
  295. // Setup bridge ids
  296. char audioPoolBaseName[6+1];
  297. char rtClientBaseName[6+1];
  298. char nonRtClientBaseName[6+1];
  299. char nonRtServerBaseName[6+1];
  300. if (useBridge)
  301. {
  302. CARLA_SAFE_ASSERT_RETURN(std::strlen(shmIds) == 6*4, 1);
  303. std::strncpy(audioPoolBaseName, shmIds+6*0, 6);
  304. std::strncpy(rtClientBaseName, shmIds+6*1, 6);
  305. std::strncpy(nonRtClientBaseName, shmIds+6*2, 6);
  306. std::strncpy(nonRtServerBaseName, shmIds+6*3, 6);
  307. audioPoolBaseName[6] = '\0';
  308. rtClientBaseName[6] = '\0';
  309. nonRtClientBaseName[6] = '\0';
  310. nonRtServerBaseName[6] = '\0';
  311. }
  312. else
  313. {
  314. audioPoolBaseName[0] = '\0';
  315. rtClientBaseName[0] = '\0';
  316. nonRtClientBaseName[0] = '\0';
  317. nonRtServerBaseName[0] = '\0';
  318. }
  319. // ---------------------------------------------------------------------
  320. // Set client name
  321. CarlaString clientName(name != nullptr ? name : label);
  322. if (clientName.isEmpty())
  323. clientName = juce::File(filename).getFileNameWithoutExtension().toRawUTF8();
  324. // ---------------------------------------------------------------------
  325. // Set extraStuff
  326. const void* extraStuff = nullptr;
  327. if (itype == CarlaBackend::PLUGIN_GIG || itype == CarlaBackend::PLUGIN_SF2)
  328. {
  329. if (label == nullptr)
  330. label = clientName;
  331. if (std::strstr(label, " (16 outs)") != nullptr)
  332. extraStuff = "true";
  333. }
  334. // ---------------------------------------------------------------------
  335. // Init plugin bridge
  336. CarlaBridgePlugin bridge(useBridge, clientName, audioPoolBaseName, rtClientBaseName, nonRtClientBaseName, nonRtServerBaseName);
  337. if (! bridge.isOk())
  338. {
  339. carla_stderr("Failed to init engine, error was:\n%s", carla_get_last_error());
  340. return 1;
  341. }
  342. // ---------------------------------------------------------------------
  343. // Listen for ctrl+c or sigint/sigterm events
  344. initSignalHandler();
  345. // ---------------------------------------------------------------------
  346. // Init plugin
  347. int ret;
  348. if (carla_add_plugin(CarlaBackend::BINARY_NATIVE, itype, filename, name, label, uniqueId, extraStuff))
  349. {
  350. ret = 0;
  351. if (useBridge)
  352. {
  353. //bridge.sendOscUpdate();
  354. //bridge.sendOscBridgeUpdate();
  355. }
  356. else
  357. {
  358. carla_set_active(0, true);
  359. if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(0))
  360. {
  361. if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI)
  362. carla_show_custom_ui(0, true);
  363. }
  364. }
  365. bridge.exec(useBridge, argc, argv);
  366. }
  367. else
  368. {
  369. ret = 1;
  370. const char* const lastError(carla_get_last_error());
  371. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  372. //if (useBridge)
  373. // bridge.sendOscBridgeError(lastError);
  374. }
  375. return ret;
  376. }