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.

644 lines
18KB

  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 "CarlaBridgeClient.hpp"
  18. #include "CarlaEngine.hpp"
  19. #include "CarlaHost.h"
  20. #include "CarlaBackendUtils.hpp"
  21. #include "CarlaBridgeUtils.hpp"
  22. #include "CarlaMIDI.h"
  23. #ifdef CARLA_OS_UNIX
  24. # include <signal.h>
  25. #endif
  26. #include "juce_core.h"
  27. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  28. # include "juce_gui_basics.h"
  29. using juce::JUCEApplication;
  30. using juce::JUCEApplicationBase;
  31. using juce::Timer;
  32. #endif
  33. using CarlaBackend::CarlaEngine;
  34. using CarlaBackend::EngineCallbackOpcode;
  35. using CarlaBackend::EngineCallbackOpcode2Str;
  36. using juce::File;
  37. using juce::String;
  38. // -------------------------------------------------------------------------
  39. static bool gIsInitiated = false;
  40. static volatile bool gCloseNow = false;
  41. static volatile bool gSaveNow = false;
  42. #ifdef CARLA_OS_WIN
  43. static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept
  44. {
  45. if (dwCtrlType == CTRL_C_EVENT)
  46. {
  47. gCloseNow = true;
  48. return TRUE;
  49. }
  50. return FALSE;
  51. }
  52. #elif defined(CARLA_OS_LINUX)
  53. static void closeSignalHandler(int) noexcept
  54. {
  55. gCloseNow = true;
  56. }
  57. static void saveSignalHandler(int) noexcept
  58. {
  59. gSaveNow = true;
  60. }
  61. #endif
  62. static void initSignalHandler()
  63. {
  64. #ifdef CARLA_OS_WIN
  65. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  66. #elif defined(CARLA_OS_LINUX)
  67. struct sigaction sint;
  68. struct sigaction sterm;
  69. struct sigaction susr1;
  70. sint.sa_handler = closeSignalHandler;
  71. sint.sa_flags = SA_RESTART;
  72. sint.sa_restorer = nullptr;
  73. sigemptyset(&sint.sa_mask);
  74. sigaction(SIGINT, &sint, nullptr);
  75. sterm.sa_handler = closeSignalHandler;
  76. sterm.sa_flags = SA_RESTART;
  77. sterm.sa_restorer = nullptr;
  78. sigemptyset(&sterm.sa_mask);
  79. sigaction(SIGTERM, &sterm, nullptr);
  80. susr1.sa_handler = saveSignalHandler;
  81. susr1.sa_flags = SA_RESTART;
  82. susr1.sa_restorer = nullptr;
  83. sigemptyset(&susr1.sa_mask);
  84. sigaction(SIGUSR1, &susr1, nullptr);
  85. #endif
  86. }
  87. // -------------------------------------------------------------------------
  88. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  89. static CarlaBridge::CarlaBridgeClient* gBridgeClient = nullptr;
  90. class CarlaJuceApp : public JUCEApplication,
  91. private Timer
  92. {
  93. public:
  94. CarlaJuceApp() {}
  95. ~CarlaJuceApp() {}
  96. void initialise(const String&) override
  97. {
  98. startTimer(30);
  99. }
  100. void shutdown() override
  101. {
  102. gCloseNow = true;
  103. stopTimer();
  104. }
  105. const String getApplicationName() override
  106. {
  107. return "CarlaPlugin";
  108. }
  109. const String getApplicationVersion() override
  110. {
  111. return CARLA_VERSION_STRING;
  112. }
  113. void timerCallback() override
  114. {
  115. carla_engine_idle();
  116. if (gBridgeClient != nullptr)
  117. gBridgeClient->oscIdle();
  118. if (gCloseNow)
  119. {
  120. quit();
  121. gCloseNow = false;
  122. }
  123. }
  124. };
  125. static JUCEApplicationBase* juce_CreateApplication() { return new CarlaJuceApp(); }
  126. #endif
  127. // -------------------------------------------------------------------------
  128. CARLA_BRIDGE_START_NAMESPACE
  129. // -------------------------------------------------------------------------
  130. class CarlaPluginClient : public CarlaBridgeClient
  131. {
  132. public:
  133. CarlaPluginClient(const bool useBridge, const char* const clientName, const char* const audioBaseName, const char* const controlBaseName, const char* const timeBaseName)
  134. : CarlaBridgeClient(nullptr),
  135. fEngine(nullptr)
  136. {
  137. CARLA_ASSERT(clientName != nullptr && clientName[0] != '\0');
  138. carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s, %s)", bool2str(useBridge), clientName, audioBaseName, controlBaseName, timeBaseName);
  139. carla_set_engine_callback(callback, this);
  140. if (useBridge)
  141. carla_engine_init_bridge(audioBaseName, controlBaseName, timeBaseName, clientName);
  142. else
  143. carla_engine_init("JACK", clientName);
  144. fEngine = carla_get_engine();
  145. }
  146. ~CarlaPluginClient() override
  147. {
  148. carla_debug("CarlaPluginClient::~CarlaPluginClient()");
  149. carla_engine_close();
  150. }
  151. bool isOk() const noexcept
  152. {
  153. return (fEngine != nullptr);
  154. }
  155. void oscInit(const char* const url)
  156. {
  157. CarlaBridgeClient::oscInit(url);
  158. fEngine->setOscBridgeData(&fOscData);
  159. }
  160. void exec(const bool useOsc, int argc, char* argv[])
  161. {
  162. if (! useOsc)
  163. {
  164. const CarlaPluginInfo* const pInfo(carla_get_plugin_info(0));
  165. CARLA_SAFE_ASSERT_RETURN(pInfo != nullptr,);
  166. fProjFilename = pInfo->name;
  167. fProjFilename += ".carxs";
  168. if (! File::isAbsolutePath(fProjFilename))
  169. fProjFilename = File::getCurrentWorkingDirectory().getChildFile(fProjFilename).getFullPathName();
  170. if (File(fProjFilename).existsAsFile() && ! carla_load_plugin_state(0, fProjFilename.toRawUTF8()))
  171. carla_stderr("Plugin preset load failed, error was:\n%s", carla_get_last_error());
  172. }
  173. gIsInitiated = true;
  174. #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
  175. gBridgeClient = this;
  176. JUCEApplicationBase::createInstance = &juce_CreateApplication;
  177. JUCEApplicationBase::main(JUCE_MAIN_FUNCTION_ARGS);
  178. gBridgeClient = nullptr;
  179. #else
  180. for (; ! gCloseNow;)
  181. {
  182. idle();
  183. carla_msleep(25);
  184. }
  185. #endif
  186. carla_set_engine_about_to_close();
  187. carla_remove_plugin(0);
  188. // may be unused
  189. return; (void)argc; (void)argv;
  190. }
  191. void idle()
  192. {
  193. CARLA_SAFE_ASSERT_RETURN(fEngine != nullptr,);
  194. carla_engine_idle();
  195. CarlaBridgeClient::oscIdle();
  196. if (gSaveNow)
  197. {
  198. gSaveNow = false;
  199. if (fProjFilename.isNotEmpty())
  200. {
  201. if (! carla_save_plugin_state(0, fProjFilename.toRawUTF8()))
  202. carla_stderr("Plugin preset save failed, error was:\n%s", carla_get_last_error());
  203. }
  204. }
  205. }
  206. // ---------------------------------------------------------------------
  207. // plugin management
  208. void saveNow()
  209. {
  210. CARLA_SAFE_ASSERT_RETURN(fEngine != nullptr,);
  211. carla_debug("CarlaPluginClient::saveNow()");
  212. carla_prepare_for_save(0);
  213. for (uint32_t i=0, count=carla_get_custom_data_count(0); i<count; ++i)
  214. {
  215. const CarlaBackend::CustomData* const cdata(carla_get_custom_data(0, i));
  216. CARLA_SAFE_ASSERT_CONTINUE(cdata != nullptr);
  217. fEngine->oscSend_bridge_set_custom_data(cdata->type, cdata->key, cdata->value);
  218. }
  219. //if (fPlugin->getOptionsEnabled() & CarlaBackend::PLUGIN_OPTION_USE_CHUNKS)
  220. {
  221. //if (const char* const chunkData = carla_get_chunk_data(0))
  222. {
  223. #if 0
  224. QString filePath;
  225. filePath = QDir::tempPath();
  226. #ifdef Q_OS_WIN
  227. filePath += "\\.CarlaChunk_";
  228. #else
  229. filePath += "/.CarlaChunk_";
  230. #endif
  231. filePath += fPlugin->getName();
  232. QFile file(filePath);
  233. if (file.open(QIODevice::WriteOnly))
  234. {
  235. QByteArray chunk((const char*)data, dataSize);
  236. file.write(chunk);
  237. file.close();
  238. fEngine->oscSend_bridge_set_chunk_data(filePath.toUtf8().constData());
  239. }
  240. #endif
  241. }
  242. }
  243. fEngine->oscSend_bridge_configure(CARLA_BRIDGE_MSG_SAVED, "");
  244. }
  245. // ---------------------------------------------------------------------
  246. protected:
  247. void handleCallback(const EngineCallbackOpcode action, const int value1, const int value2, const float value3, const char* const valueStr)
  248. {
  249. CARLA_BACKEND_USE_NAMESPACE;
  250. // TODO
  251. switch (action)
  252. {
  253. case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
  254. if (isOscControlRegistered())
  255. {
  256. CARLA_SAFE_ASSERT_RETURN(value1 >= 0,);
  257. fEngine->oscSend_bridge_parameter_value(static_cast<uint32_t>(value1), value3);
  258. }
  259. break;
  260. case ENGINE_CALLBACK_UI_STATE_CHANGED:
  261. if (! isOscControlRegistered())
  262. {
  263. if (value1 != 1 && gIsInitiated)
  264. gCloseNow = true;
  265. }
  266. else
  267. {
  268. // show-gui button
  269. fEngine->oscSend_bridge_configure(CARLA_BRIDGE_MSG_HIDE_GUI, "");
  270. }
  271. break;
  272. default:
  273. break;
  274. }
  275. return;
  276. (void)value2;
  277. (void)value3;
  278. (void)valueStr;
  279. }
  280. private:
  281. const CarlaEngine* fEngine;
  282. String fProjFilename;
  283. static void callback(void* ptr, EngineCallbackOpcode action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr)
  284. {
  285. carla_debug("CarlaPluginClient::callback(%p, %i:%s, %i, %i, %i, %f, \"%s\")", ptr, action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
  286. CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
  287. CARLA_SAFE_ASSERT_RETURN(pluginId == 0,);
  288. return ((CarlaPluginClient*)ptr)->handleCallback(action, value1, value2, value3, valueStr);
  289. }
  290. };
  291. // -------------------------------------------------------------------------
  292. int CarlaBridgeOsc::handleMsgShow()
  293. {
  294. carla_debug("CarlaBridgeOsc::handleMsgShow()");
  295. if (carla_get_plugin_info(0)->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI)
  296. carla_show_custom_ui(0, true);
  297. return 0;
  298. }
  299. int CarlaBridgeOsc::handleMsgHide()
  300. {
  301. carla_debug("CarlaBridgeOsc::handleMsgHide()");
  302. if (carla_get_plugin_info(0)->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI)
  303. carla_show_custom_ui(0, false);
  304. return 0;
  305. }
  306. int CarlaBridgeOsc::handleMsgQuit()
  307. {
  308. carla_debug("CarlaBridgeOsc::handleMsgQuit()");
  309. gCloseNow = true;
  310. return 0;
  311. }
  312. // -------------------------------------------------------------------------
  313. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  314. {
  315. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  316. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  317. ((CarlaPluginClient*)fClient)->saveNow();
  318. return 0;
  319. }
  320. int CarlaBridgeOsc::handleMsgPluginSetParameterMidiChannel(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  321. {
  322. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii");
  323. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  324. carla_debug("CarlaBridgeOsc::handleMsgPluginSetParameterMidiChannel()");
  325. const int32_t index = argv[0]->i;
  326. const int32_t channel = argv[1]->i;
  327. CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
  328. CARLA_SAFE_ASSERT_RETURN(channel >= 0 && channel < MAX_MIDI_CHANNELS, 0);
  329. carla_set_parameter_midi_channel(0, static_cast<uint32_t>(index), static_cast<uint8_t>(channel));
  330. return 0;
  331. }
  332. int CarlaBridgeOsc::handleMsgPluginSetParameterMidiCC(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  333. {
  334. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii");
  335. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  336. carla_debug("CarlaBridgeOsc::handleMsgPluginSetParameterMidiCC()");
  337. const int32_t index = argv[0]->i;
  338. const int32_t cc = argv[1]->i;
  339. CARLA_SAFE_ASSERT_RETURN(index >= 0, 0);
  340. CARLA_SAFE_ASSERT_RETURN(cc >= 1 && cc < 0x5F, 0);
  341. carla_set_parameter_midi_cc(0, static_cast<uint32_t>(index), static_cast<int16_t>(cc));
  342. return 0;
  343. }
  344. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  345. {
  346. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  347. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  348. carla_debug("CarlaBridgeOsc::handleMsgPluginSetCustomData()");
  349. const char* const type = (const char*)&argv[0]->s;
  350. const char* const key = (const char*)&argv[1]->s;
  351. const char* const value = (const char*)&argv[2]->s;
  352. carla_set_custom_data(0, type, key, value);
  353. return 0;
  354. }
  355. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  356. {
  357. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  358. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  359. carla_debug("CarlaBridgeOsc::handleMsgPluginSetChunk()");
  360. const char* const chunkFilePathTry = (const char*)&argv[0]->s;
  361. CARLA_SAFE_ASSERT_RETURN(chunkFilePathTry != nullptr && chunkFilePathTry[0] != '\0', 0);
  362. String chunkFilePath(chunkFilePathTry);
  363. #ifdef CARLA_OS_WIN
  364. if (chunkFilePath.startsWith("/"))
  365. {
  366. // running under Wine, posix host
  367. chunkFilePath = chunkFilePath.replaceSection(0, 1, "Z:\\");
  368. chunkFilePath = chunkFilePath.replace("/", "\\");
  369. }
  370. #endif
  371. File chunkFile(chunkFilePath);
  372. CARLA_SAFE_ASSERT_RETURN(chunkFile.existsAsFile(), 0);
  373. String chunkData(chunkFile.loadFileAsString());
  374. chunkFile.deleteFile();
  375. CARLA_SAFE_ASSERT_RETURN(chunkData.isNotEmpty(), 0);
  376. carla_set_chunk_data(0, chunkData.toRawUTF8());
  377. return 0;
  378. }
  379. CARLA_BRIDGE_END_NAMESPACE
  380. // -------------------------------------------------------------------------
  381. int main(int argc, char* argv[])
  382. {
  383. CARLA_BRIDGE_USE_NAMESPACE;
  384. // ---------------------------------------------------------------------
  385. // Check argument count
  386. if (argc != 7)
  387. {
  388. carla_stdout("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label> <uniqueId>", argv[0]);
  389. return 1;
  390. }
  391. // ---------------------------------------------------------------------
  392. // Get args
  393. const char* const oscUrl = argv[1];
  394. const char* const stype = argv[2];
  395. const char* const filename = argv[3];
  396. const char* name = argv[4];
  397. const char* label = argv[5];
  398. const int64_t uniqueId = static_cast<int64_t>(std::atoll(argv[6]));
  399. // ---------------------------------------------------------------------
  400. // Check plugin type
  401. CarlaBackend::PluginType itype(CarlaBackend::getPluginTypeFromString(stype));
  402. if (itype == CarlaBackend::PLUGIN_NONE)
  403. {
  404. carla_stderr("Invalid plugin type '%s'", stype);
  405. return 1;
  406. }
  407. // ---------------------------------------------------------------------
  408. // Set name as null if invalid
  409. if (std::strlen(name) == 0 || std::strcmp(name, "(none)") == 0)
  410. name = nullptr;
  411. // ---------------------------------------------------------------------
  412. // Setup options
  413. const char* const shmIds(std::getenv("ENGINE_BRIDGE_SHM_IDS"));
  414. const bool useBridge = (shmIds != nullptr);
  415. const bool useOsc = (std::strcmp(oscUrl, "null") != 0 && std::strcmp(oscUrl, "(null)") != 0 && std::strcmp(oscUrl, "NULL") != 0);
  416. // ---------------------------------------------------------------------
  417. // Setup bridge ids
  418. char bridgeBaseAudioName[6+1];
  419. char bridgeBaseControlName[6+1];
  420. char bridgeBaseTimeName[6+1];
  421. if (useBridge)
  422. {
  423. CARLA_SAFE_ASSERT_RETURN(std::strlen(shmIds) == 6*3, 1);
  424. std::strncpy(bridgeBaseAudioName, shmIds, 6);
  425. std::strncpy(bridgeBaseControlName, shmIds+6, 6);
  426. std::strncpy(bridgeBaseTimeName, shmIds+12, 6);
  427. bridgeBaseAudioName[6] = '\0';
  428. bridgeBaseControlName[6] = '\0';
  429. bridgeBaseTimeName[6] = '\0';
  430. }
  431. else
  432. {
  433. bridgeBaseAudioName[0] = '\0';
  434. bridgeBaseControlName[0] = '\0';
  435. bridgeBaseTimeName[0] = '\0';
  436. }
  437. // ---------------------------------------------------------------------
  438. // Set client name
  439. CarlaString clientName((name != nullptr) ? name : label);
  440. if (clientName.isEmpty())
  441. clientName = juce::File(filename).getFileNameWithoutExtension().toRawUTF8();
  442. // ---------------------------------------------------------------------
  443. // Set extraStuff
  444. const void* extraStuff = nullptr;
  445. if (itype == CarlaBackend::PLUGIN_GIG || itype == CarlaBackend::PLUGIN_SF2)
  446. {
  447. if (label == nullptr)
  448. label = clientName;
  449. if (std::strstr(label, " (16 outs)") == 0)
  450. extraStuff = "true";
  451. }
  452. // ---------------------------------------------------------------------
  453. // Init plugin client
  454. CarlaPluginClient client(useBridge, clientName, bridgeBaseAudioName, bridgeBaseControlName, bridgeBaseTimeName);
  455. if (! client.isOk())
  456. {
  457. carla_stderr("Failed to init engine, error was:\n%s", carla_get_last_error());
  458. return 1;
  459. }
  460. // ---------------------------------------------------------------------
  461. // Init OSC
  462. if (useOsc)
  463. client.oscInit(oscUrl);
  464. // ---------------------------------------------------------------------
  465. // Listen for ctrl+c or sigint/sigterm events
  466. initSignalHandler();
  467. // ---------------------------------------------------------------------
  468. // Init plugin
  469. int ret;
  470. if (carla_add_plugin(CarlaBackend::BINARY_NATIVE, itype, filename, name, label, uniqueId, extraStuff))
  471. {
  472. ret = 0;
  473. if (useOsc)
  474. {
  475. client.sendOscUpdate();
  476. client.sendOscBridgeUpdate();
  477. }
  478. else
  479. {
  480. carla_set_active(0, true);
  481. if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(0))
  482. {
  483. if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_CUSTOM_UI)
  484. carla_show_custom_ui(0, true);
  485. }
  486. }
  487. client.exec(useOsc, argc, argv);
  488. }
  489. else
  490. {
  491. ret = 1;
  492. const char* const lastError(carla_get_last_error());
  493. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  494. if (useOsc)
  495. client.sendOscBridgeError(lastError);
  496. }
  497. // ---------------------------------------------------------------------
  498. // Close OSC
  499. if (useOsc)
  500. client.oscClose();
  501. return ret;
  502. }