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.

616 lines
15KB

  1. /*
  2. * Carla Bridge Plugin
  3. * Copyright (C) 2012-2013 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 GPL.txt file
  16. */
  17. #ifdef BRIDGE_PLUGIN
  18. #include "CarlaBridgeClient.hpp"
  19. #include "CarlaBridgeToolkit.hpp"
  20. #include "CarlaStandalone.hpp"
  21. #include "CarlaEngine.hpp"
  22. #include "CarlaPlugin.hpp"
  23. #include <QtCore/QDir>
  24. #include <QtCore/QFile>
  25. #include <QtCore/QTextStream>
  26. #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
  27. # include <QtWidgets/QApplication>
  28. #else
  29. # include <QtGui/QApplication>
  30. #endif
  31. #ifdef CARLA_OS_UNIX
  32. # include <signal.h>
  33. #endif
  34. // -------------------------------------------------------------------------
  35. static bool gCloseNow = false;
  36. static bool gSaveNow = false;
  37. #ifdef CARLA_OS_WIN
  38. BOOL WINAPI closeSignalHandler(DWORD dwCtrlType)
  39. {
  40. if (dwCtrlType == CTRL_C_EVENT)
  41. {
  42. gCloseNow = true;
  43. return TRUE;
  44. }
  45. return FALSE;
  46. }
  47. #else
  48. void closeSignalHandler(int)
  49. {
  50. gCloseNow = true;
  51. }
  52. void saveSignalHandler(int)
  53. {
  54. gSaveNow = true;
  55. }
  56. #endif
  57. void initSignalHandler()
  58. {
  59. #ifdef CARLA_OS_WIN
  60. SetConsoleCtrlHandler(closeSignalHandler, TRUE);
  61. #elif defined(CARLA_OS_LINUX) || defined(CARLA_OS_HAIKU)
  62. struct sigaction sint;
  63. struct sigaction sterm;
  64. struct sigaction susr1;
  65. sint.sa_handler = closeSignalHandler;
  66. sint.sa_flags = SA_RESTART;
  67. sint.sa_restorer = nullptr;
  68. sigemptyset(&sint.sa_mask);
  69. sigaction(SIGINT, &sint, nullptr);
  70. sterm.sa_handler = closeSignalHandler;
  71. sterm.sa_flags = SA_RESTART;
  72. sterm.sa_restorer = nullptr;
  73. sigemptyset(&sterm.sa_mask);
  74. sigaction(SIGTERM, &sterm, nullptr);
  75. susr1.sa_handler = saveSignalHandler;
  76. susr1.sa_flags = SA_RESTART;
  77. susr1.sa_restorer = nullptr;
  78. sigemptyset(&susr1.sa_mask);
  79. sigaction(SIGUSR1, &susr1, nullptr);
  80. #endif
  81. }
  82. // -------------------------------------------------------------------------
  83. // Helpers
  84. extern CarlaBackend::CarlaEngine* carla_get_standalone_engine();
  85. CARLA_BACKEND_START_NAMESPACE
  86. extern const char* findDSSIGUI(const char* const filename, const char* const label);
  87. CARLA_BACKEND_END_NAMESPACE
  88. CARLA_BRIDGE_START_NAMESPACE
  89. #if 0
  90. } // Fix editor indentation
  91. #endif
  92. // -------------------------------------------------------------------------
  93. class CarlaPluginClient : public CarlaBridgeClient,
  94. public QObject
  95. {
  96. public:
  97. CarlaPluginClient(const char* const name)
  98. : CarlaBridgeClient(nullptr),
  99. QObject(nullptr),
  100. fEngine(nullptr),
  101. fPlugin(nullptr),
  102. fTimerId(0)
  103. {
  104. CARLA_ASSERT(name != nullptr);
  105. carla_debug("CarlaPluginClient::CarlaPluginClient()");
  106. carla_engine_init("JACK", name);
  107. carla_set_engine_callback(callback, this);
  108. }
  109. ~CarlaPluginClient()
  110. {
  111. CARLA_ASSERT(fTimerId == 0);
  112. carla_debug("CarlaPluginClient::~CarlaPluginClient()");
  113. carla_set_engine_about_to_close();
  114. carla_engine_close();
  115. }
  116. void ready()
  117. {
  118. CARLA_ASSERT(fTimerId == 0);
  119. fEngine = carla_get_standalone_engine();
  120. fPlugin = fEngine->getPlugin(0);
  121. fTimerId = startTimer(50);
  122. }
  123. void idle()
  124. {
  125. if (fEngine != nullptr)
  126. fEngine->idle();
  127. CarlaBridgeClient::oscIdle();
  128. if (gSaveNow)
  129. {
  130. // TODO
  131. gSaveNow = false;
  132. }
  133. if (gCloseNow)
  134. {
  135. if (fTimerId != 0)
  136. {
  137. killTimer(fTimerId);
  138. fTimerId = 0;
  139. }
  140. if (QApplication* const app = qApp)
  141. {
  142. if (! app->closingDown())
  143. app->quit();
  144. }
  145. }
  146. }
  147. void exec()
  148. {
  149. while (! gCloseNow)
  150. {
  151. idle();
  152. carla_msleep(50);
  153. }
  154. }
  155. // ---------------------------------------------------------------------
  156. // plugin management
  157. void saveNow()
  158. {
  159. qDebug("CarlaPluginClient::saveNow()");
  160. CARLA_ASSERT(fEngine != nullptr);
  161. CARLA_ASSERT(fPlugin != nullptr);
  162. if (fPlugin == nullptr || fEngine == nullptr)
  163. return;
  164. fPlugin->prepareForSave();
  165. for (uint32_t i=0; i < fPlugin->customDataCount(); i++)
  166. {
  167. const CarlaBackend::CustomData& cdata = fPlugin->customData(i);
  168. fEngine->osc_send_bridge_set_custom_data(cdata.type, cdata.key, cdata.value);
  169. }
  170. if (fPlugin->options() & CarlaBackend::PLUGIN_OPTION_USE_CHUNKS)
  171. {
  172. void* data = nullptr;
  173. int32_t dataSize = fPlugin->chunkData(&data);
  174. if (data && dataSize >= 4)
  175. {
  176. QString filePath;
  177. filePath = QDir::tempPath();
  178. #ifdef Q_OS_WIN
  179. filePath += "\\.CarlaChunk_";
  180. #else
  181. filePath += "/.CarlaChunk_";
  182. #endif
  183. filePath += fPlugin->name();
  184. QFile file(filePath);
  185. if (file.open(QIODevice::WriteOnly))
  186. {
  187. QByteArray chunk((const char*)data, dataSize);
  188. file.write(chunk);
  189. file.close();
  190. fEngine->osc_send_bridge_set_chunk_data(filePath.toUtf8().constData());
  191. }
  192. }
  193. }
  194. //engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_SAVED, "");
  195. }
  196. void setCustomData(const char* const type, const char* const key, const char* const value)
  197. {
  198. carla_debug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  199. CARLA_ASSERT(fPlugin != nullptr);
  200. if (fPlugin != nullptr)
  201. fPlugin->setCustomData(type, key, value, true);
  202. }
  203. void setChunkData(const char* const filePath)
  204. {
  205. carla_debug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  206. CARLA_ASSERT(fPlugin != nullptr);
  207. if (fPlugin == nullptr)
  208. return;
  209. QString chunkFilePath(filePath);
  210. #ifdef CARLA_OS_WIN
  211. if (chunkFilePath.startsWith("/"))
  212. {
  213. // running under Wine, posix host
  214. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  215. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  216. }
  217. #endif
  218. QFile chunkFile(chunkFilePath);
  219. if (fPlugin != nullptr && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  220. {
  221. QTextStream in(&chunkFile);
  222. QString stringData(in.readAll());
  223. chunkFile.close();
  224. chunkFile.remove();
  225. fPlugin->setChunkData(stringData.toUtf8().constData());
  226. }
  227. }
  228. // ---------------------------------------------------------------------
  229. // processing
  230. void setParameter(const int32_t rindex, const float value)
  231. {
  232. carla_debug("CarlaPluginClient::setParameter(%i, %f)", rindex, value);
  233. CARLA_ASSERT(fPlugin != nullptr);
  234. if (fPlugin != nullptr)
  235. fPlugin->setParameterValueByRIndex(rindex, value, true, true, false);
  236. }
  237. void setProgram(const uint32_t index)
  238. {
  239. carla_debug("CarlaPluginClient::setProgram(%i)", index);
  240. CARLA_ASSERT(fPlugin != nullptr);
  241. if (fPlugin != nullptr)
  242. fPlugin->setProgram(index, true, true, false);
  243. }
  244. void setMidiProgram(const uint32_t index)
  245. {
  246. carla_debug("CarlaPluginClient::setMidiProgram(%i)", index);
  247. CARLA_ASSERT(fPlugin != nullptr);
  248. if (fPlugin != nullptr)
  249. fPlugin->setMidiProgram(index, true, true, false);
  250. }
  251. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  252. {
  253. carla_debug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo);
  254. CARLA_ASSERT(fPlugin != nullptr);
  255. CARLA_ASSERT(velo > 0);
  256. if (fPlugin != nullptr)
  257. fPlugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  258. }
  259. void noteOff(const uint8_t channel, const uint8_t note)
  260. {
  261. carla_debug("CarlaPluginClient::noteOff(%i, %i)", channel, note);
  262. CARLA_ASSERT(fPlugin != nullptr);
  263. if (fPlugin != nullptr)
  264. fPlugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  265. }
  266. protected:
  267. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const float value3, const char* const valueStr)
  268. {
  269. CARLA_BACKEND_USE_NAMESPACE;
  270. // TODO
  271. switch (action)
  272. {
  273. case CALLBACK_SHOW_GUI:
  274. if (value1 != 1 && ! isOscControlRegistered())
  275. #if 1
  276. {
  277. static int count = 0;
  278. count++;
  279. if (count == 2)
  280. gCloseNow = true;
  281. }
  282. #else
  283. gCloseNow = true;
  284. #endif
  285. break;
  286. default:
  287. break;
  288. }
  289. return;
  290. (void)value2;
  291. (void)value3;
  292. (void)valueStr;
  293. }
  294. private:
  295. CarlaBackend::CarlaEngine* fEngine;
  296. CarlaBackend::CarlaPlugin* fPlugin;
  297. int fTimerId;
  298. void timerEvent(QTimerEvent* const event)
  299. {
  300. if (event->timerId() == fTimerId)
  301. idle();
  302. QObject::timerEvent(event);
  303. }
  304. static void callback(void* ptr, CarlaBackend::CallbackType action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr)
  305. {
  306. return ((CarlaPluginClient*)ptr)->handleCallback(action, value1, value2, value3, valueStr);
  307. // unused
  308. (void)pluginId;
  309. }
  310. };
  311. // -------------------------------------------------------------------------
  312. int CarlaBridgeOsc::handleMsgShow()
  313. {
  314. carla_debug("CarlaBridgeOsc::handleMsgShow()");
  315. CARLA_ASSERT(kClient != nullptr);
  316. if (kClient == nullptr)
  317. return 1;
  318. carla_show_gui(0, true);
  319. return 0;
  320. }
  321. int CarlaBridgeOsc::handleMsgHide()
  322. {
  323. carla_debug("CarlaBridgeOsc::handleMsgHide()");
  324. CARLA_ASSERT(kClient != nullptr);
  325. if (kClient == nullptr)
  326. return 1;
  327. carla_show_gui(0, false);
  328. return 0;
  329. }
  330. int CarlaBridgeOsc::handleMsgQuit()
  331. {
  332. carla_debug("CarlaBridgeOsc::handleMsgQuit()");
  333. CARLA_ASSERT(kClient != nullptr);
  334. if (kClient == nullptr)
  335. return 1;
  336. gCloseNow = true;
  337. return 0;
  338. }
  339. // -------------------------------------------------------------------------
  340. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  341. {
  342. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  343. CARLA_ASSERT(kClient != nullptr);
  344. if (kClient == nullptr)
  345. return 1;
  346. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  347. plugClient->saveNow();
  348. return 0;
  349. }
  350. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  351. {
  352. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  353. CARLA_ASSERT(kClient != nullptr);
  354. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  355. if (kClient == nullptr)
  356. return 1;
  357. const char* const chunkFile = (const char*)&argv[0]->s;
  358. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  359. plugClient->setChunkData(chunkFile);
  360. return 0;
  361. }
  362. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  363. {
  364. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  365. CARLA_ASSERT(kClient != nullptr);
  366. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  367. if (kClient == nullptr)
  368. return 1;
  369. const char* const type = (const char*)&argv[0]->s;
  370. const char* const key = (const char*)&argv[1]->s;
  371. const char* const value = (const char*)&argv[2]->s;
  372. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  373. plugClient->setCustomData(type, key, value);
  374. return 0;
  375. }
  376. // -------------------------------------------------------------------------
  377. CARLA_BRIDGE_END_NAMESPACE
  378. int main(int argc, char* argv[])
  379. {
  380. CARLA_BRIDGE_USE_NAMESPACE
  381. #if 1
  382. QApplication app(argc, argv, true);
  383. app.setQuitOnLastWindowClosed(false);
  384. // Init Plugin client
  385. CarlaPluginClient client("zyn-2-test");
  386. // Listen for ctrl+c or sigint/sigterm events
  387. initSignalHandler();
  388. if (!carla_add_plugin(CarlaBackend::BINARY_NATIVE, CarlaBackend::PLUGIN_INTERNAL, nullptr, "zyn1", "zynaddsubfx", nullptr))
  389. {
  390. carla_stderr(carla_get_last_error());
  391. return 1;
  392. }
  393. carla_add_plugin(CarlaBackend::BINARY_NATIVE, CarlaBackend::PLUGIN_INTERNAL, nullptr, "zyn2", "zynaddsubfx", nullptr);
  394. carla_set_active(0, true);
  395. carla_set_active(1, true);
  396. carla_show_gui(0, true);
  397. carla_show_gui(1, true);
  398. client.ready();
  399. app.exec();
  400. carla_remove_plugin(1);
  401. carla_remove_plugin(0);
  402. return 0;
  403. #else
  404. if (argc != 6)
  405. {
  406. carla_stdout("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  407. return 1;
  408. }
  409. const char* const oscUrl = argv[1];
  410. const char* const stype = argv[2];
  411. const char* const filename = argv[3];
  412. const char* name = argv[4];
  413. const char* const label = argv[5];
  414. const bool useOsc = std::strcmp(oscUrl, "null");
  415. if (std::strcmp(name, "(none)") == 0)
  416. name = nullptr;
  417. CarlaBackend::PluginType itype;
  418. if (std::strcmp(stype, "LADSPA") == 0)
  419. itype = CarlaBackend::PLUGIN_LADSPA;
  420. else if (std::strcmp(stype, "DSSI") == 0)
  421. itype = CarlaBackend::PLUGIN_DSSI;
  422. else if (std::strcmp(stype, "LV2") == 0)
  423. itype = CarlaBackend::PLUGIN_LV2;
  424. else if (std::strcmp(stype, "VST") == 0)
  425. itype = CarlaBackend::PLUGIN_VST;
  426. else if (std::strcmp(stype, "VST3") == 0)
  427. itype = CarlaBackend::PLUGIN_VST3;
  428. else
  429. {
  430. carla_stderr("Invalid plugin type '%s'", stype);
  431. return 1;
  432. }
  433. QApplication app(argc, argv, true);
  434. app.setQuitOnLastWindowClosed(false);
  435. // Init Plugin client
  436. CarlaPluginClient client(name ? name : label);
  437. // Init OSC
  438. if (useOsc)
  439. client.oscInit(oscUrl);
  440. // Listen for ctrl+c or sigint/sigterm events
  441. initSignalHandler();
  442. const void* extraStuff = nullptr;
  443. if (itype == CarlaBackend::PLUGIN_DSSI)
  444. extraStuff = CarlaBackend::findDSSIGUI(filename, label);
  445. // Init plugin
  446. int ret;
  447. if (carla_add_plugin(CarlaBackend::BINARY_NATIVE, itype, filename, name, label, extraStuff))
  448. {
  449. if (useOsc)
  450. {
  451. client.sendOscUpdate();
  452. client.sendOscBridgeUpdate();
  453. }
  454. else
  455. {
  456. carla_set_active(0, true);
  457. carla_show_gui(0, true);
  458. }
  459. client.ready();
  460. ret = app.exec();
  461. carla_remove_plugin(0);
  462. }
  463. else
  464. {
  465. const char* const lastError = carla_get_last_error();
  466. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  467. if (useOsc)
  468. client.sendOscBridgeError(lastError);
  469. ret = 1;
  470. }
  471. if (extraStuff != nullptr && itype == CarlaBackend::PLUGIN_DSSI)
  472. delete[] (const char*)extraStuff;
  473. // Close OSC
  474. if (useOsc)
  475. client.oscClose();
  476. return ret;
  477. #endif
  478. }
  479. #endif // BRIDGE_PLUGIN