Collection of tools useful for audio production
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.

802 lines
20KB

  1. /*
  2. * Carla Plugin bridge code
  3. * Copyright (C) 2012 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * 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 COPYING file
  16. */
  17. #ifdef BRIDGE_PLUGIN
  18. #include "carla_bridge_client.hpp"
  19. #include "carla_bridge_toolkit.hpp"
  20. #include "carla_plugin.hpp"
  21. #include <QtCore/QDir>
  22. #include <QtCore/QFile>
  23. #include <QtCore/QTextStream>
  24. #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
  25. # include <QtWidgets/QApplication>
  26. #else
  27. # include <QtGui/QApplication>
  28. #endif
  29. #ifdef Q_OS_UNIX
  30. # include <signal.h>
  31. #endif
  32. // -------------------------------------------------------------------------
  33. static int qargc = 0;
  34. static char** qargv = nullptr;
  35. static bool qCloseNow = false;
  36. static bool qSaveNow = false;
  37. #if defined(Q_OS_HAIKU) || defined(Q_OS_UNIX)
  38. void closeSignalHandler(int)
  39. {
  40. qCloseNow = true;
  41. }
  42. void saveSignalHandler(int)
  43. {
  44. qSaveNow = true;
  45. }
  46. #elif defined(Q_OS_WIN)
  47. BOOL WINAPI closeSignalHandler(DWORD dwCtrlType)
  48. {
  49. if (dwCtrlType == CTRL_C_EVENT)
  50. {
  51. qCloseNow = true;
  52. return TRUE;
  53. }
  54. return FALSE;
  55. }
  56. #endif
  57. void initSignalHandler()
  58. {
  59. #if defined(Q_OS_HAIKU) || defined(Q_OS_UNIX)
  60. struct sigaction sint;
  61. struct sigaction sterm;
  62. struct sigaction susr1;
  63. sint.sa_handler = closeSignalHandler;
  64. sint.sa_flags = SA_RESTART;
  65. sint.sa_restorer = nullptr;
  66. sigemptyset(&sint.sa_mask);
  67. sigaction(SIGINT, &sint, nullptr);
  68. sterm.sa_handler = closeSignalHandler;
  69. sterm.sa_flags = SA_RESTART;
  70. sterm.sa_restorer = nullptr;
  71. sigemptyset(&sterm.sa_mask);
  72. sigaction(SIGTERM, &sterm, nullptr);
  73. susr1.sa_handler = saveSignalHandler;
  74. susr1.sa_flags = SA_RESTART;
  75. susr1.sa_restorer = nullptr;
  76. sigemptyset(&susr1.sa_mask);
  77. sigaction(SIGUSR1, &susr1, nullptr);
  78. #elif defined(Q_OS_WIN)
  79. SetConsoleCtrlHandler(closeSignalHandler, TRUE);
  80. #endif
  81. }
  82. CARLA_BRIDGE_START_NAMESPACE
  83. // -------------------------------------------------------------------------
  84. class CarlaBridgeToolkitPlugin : public CarlaBridgeToolkit,
  85. public CarlaBackend::CarlaPluginGUI::Callback
  86. {
  87. public:
  88. CarlaBridgeToolkitPlugin(CarlaBridgeClient* const client, const char* const uiTitle)
  89. : CarlaBridgeToolkit(client, uiTitle)
  90. {
  91. qDebug("CarlaBridgeToolkitPlugin::CarlaBridgeToolkitPlugin(%p, \"%s\")", client, uiTitle);
  92. app = nullptr;
  93. gui = nullptr;
  94. m_hasUI = false;
  95. m_uiQuit = false;
  96. m_uiShow = false;
  97. init();
  98. }
  99. ~CarlaBridgeToolkitPlugin()
  100. {
  101. qDebug("CarlaBridgeToolkitPlugin::~CarlaBridgeToolkitPlugin()");
  102. if (gui)
  103. {
  104. gui->close();
  105. delete gui;
  106. gui = nullptr;
  107. }
  108. if (app)
  109. {
  110. if (! app->closingDown())
  111. app->quit();
  112. delete app;
  113. app = nullptr;
  114. }
  115. }
  116. void init()
  117. {
  118. qDebug("CarlaBridgeToolkitPlugin::init()");
  119. CARLA_ASSERT(! app);
  120. CARLA_ASSERT(! gui);
  121. app = new QApplication(qargc, qargv);
  122. gui = new CarlaBackend::CarlaPluginGUI(nullptr, this);
  123. }
  124. void exec(const bool showGui)
  125. {
  126. qDebug("CarlaBridgeToolkitPlugin::exec(%s)", bool2str(showGui));
  127. CARLA_ASSERT(app);
  128. CARLA_ASSERT(gui);
  129. CARLA_ASSERT(client);
  130. if (showGui)
  131. {
  132. if (m_hasUI)
  133. show();
  134. }
  135. else
  136. {
  137. app->setQuitOnLastWindowClosed(false);
  138. client->sendOscUpdate();
  139. client->sendOscBridgeUpdate();
  140. }
  141. m_uiQuit = showGui;
  142. // Main loop
  143. app->exec();
  144. }
  145. void quit()
  146. {
  147. qDebug("CarlaBridgeToolkitPlugin::quit()");
  148. CARLA_ASSERT(app);
  149. if (app && ! app->closingDown())
  150. app->quit();
  151. }
  152. void show();
  153. void hide();
  154. void resize(const int width, const int height)
  155. {
  156. qDebug("CarlaBridgeToolkitPlugin::resize(%i, %i)", width, height);
  157. CARLA_ASSERT(gui);
  158. if (gui)
  159. gui->setNewSize(width, height);
  160. }
  161. GuiContainer* getContainer() const
  162. {
  163. CARLA_ASSERT(gui);
  164. if (gui)
  165. return gui->getContainer();
  166. return nullptr;
  167. }
  168. void* getContainerId()
  169. {
  170. CARLA_ASSERT(gui);
  171. if (gui)
  172. return (void*)gui->getWinId();
  173. return nullptr;
  174. }
  175. void setHasUI(const bool hasUI, const bool showUI)
  176. {
  177. m_hasUI = hasUI;
  178. m_uiShow = showUI;
  179. }
  180. protected:
  181. QApplication* app;
  182. CarlaBackend::CarlaPluginGUI* gui;
  183. void guiClosedCallback();
  184. private:
  185. bool m_hasUI;
  186. bool m_uiQuit;
  187. bool m_uiShow;
  188. };
  189. CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle)
  190. {
  191. return new CarlaBridgeToolkitPlugin(client, uiTitle);
  192. }
  193. // -------------------------------------------------------------------------
  194. class CarlaPluginClient : public CarlaBridgeClient,
  195. public QObject
  196. {
  197. public:
  198. CarlaPluginClient()
  199. : CarlaBridgeClient(""),
  200. QObject(nullptr)
  201. {
  202. qDebug("CarlaPluginClient::CarlaPluginClient()");
  203. msgTimerGUI = 0;
  204. msgTimerOSC = 0;
  205. engine = nullptr;
  206. plugin = nullptr;
  207. }
  208. ~CarlaPluginClient()
  209. {
  210. qDebug("CarlaPluginClient::~CarlaPluginClient()");
  211. CARLA_ASSERT(msgTimerGUI == 0);
  212. CARLA_ASSERT(msgTimerOSC == 0);
  213. }
  214. // ---------------------------------------------------------------------
  215. void init()
  216. {
  217. CARLA_ASSERT(plugin);
  218. msgTimerGUI = startTimer(50);
  219. msgTimerOSC = startTimer(25);
  220. if (! plugin)
  221. return;
  222. // create window if needed
  223. bool guiResizable;
  224. CarlaBackend::GuiType guiType;
  225. plugin->getGuiInfo(&guiType, &guiResizable);
  226. CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit;
  227. qWarning("----------------------------------------------------- trying..., %s", CarlaBackend::GuiType2Str(guiType));
  228. if (guiType == CarlaBackend::GUI_INTERNAL_QT4 || guiType == CarlaBackend::GUI_INTERNAL_COCOA || guiType == CarlaBackend::GUI_INTERNAL_HWND || guiType == CarlaBackend::GUI_INTERNAL_X11)
  229. {
  230. plugin->setGuiContainer(plugToolkit->getContainer());
  231. plugToolkit->setHasUI(true, true);
  232. }
  233. else
  234. {
  235. plugToolkit->setHasUI(guiType != CarlaBackend::GUI_NONE, false);
  236. }
  237. }
  238. void quit()
  239. {
  240. engine = nullptr;
  241. plugin = nullptr;
  242. if (msgTimerGUI != 0)
  243. {
  244. killTimer(msgTimerGUI);
  245. msgTimerGUI = 0;
  246. }
  247. if (msgTimerOSC != 0)
  248. {
  249. killTimer(msgTimerOSC);
  250. msgTimerOSC = 0;
  251. }
  252. }
  253. // ---------------------------------------------------------------------
  254. void setEngine(CarlaBackend::CarlaEngine* const engine)
  255. {
  256. qDebug("CarlaPluginClient::setEngine(%p)", engine);
  257. CARLA_ASSERT(engine);
  258. this->engine = engine;
  259. engine->setOscBridgeData(m_oscData);
  260. }
  261. void setPlugin(CarlaBackend::CarlaPlugin* const plugin)
  262. {
  263. qDebug("CarlaPluginClient::setPlugin(%p)", plugin);
  264. CARLA_ASSERT(plugin);
  265. this->plugin = plugin;
  266. }
  267. // ---------------------------------------------------------------------
  268. void guiClosed()
  269. {
  270. CARLA_ASSERT(engine);
  271. if (engine)
  272. engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, "");
  273. }
  274. void showPluginGui(const bool yesNo)
  275. {
  276. CARLA_ASSERT(plugin);
  277. if (plugin)
  278. plugin->showGui(yesNo);
  279. }
  280. // ---------------------------------------------------------------------
  281. static void callback(void* const ptr, CarlaBackend::CallbackType const action, const unsigned short, const int value1, const int value2, const double value3, const char* const valueStr)
  282. {
  283. CARLA_ASSERT(ptr);
  284. if (CarlaPluginClient* const _this_ = (CarlaPluginClient*)ptr)
  285. _this_->handleCallback(action, value1, value2, value3, valueStr);
  286. }
  287. // ---------------------------------------------------------------------
  288. // processing
  289. void setParameter(const int32_t rindex, const double value)
  290. {
  291. qDebug("CarlaPluginClient::setParameter(%i, %g)", rindex, value);
  292. CARLA_ASSERT(plugin);
  293. if (plugin)
  294. plugin->setParameterValueByRIndex(rindex, value, true, true, false);
  295. }
  296. void setProgram(const uint32_t index)
  297. {
  298. qDebug("CarlaPluginClient::setProgram(%i)", index);
  299. CARLA_ASSERT(engine);
  300. CARLA_ASSERT(plugin);
  301. CARLA_ASSERT(index < plugin->programCount());
  302. if (! (plugin && engine))
  303. return;
  304. if (index >= plugin->programCount())
  305. return;
  306. plugin->setProgram(index, true, true, false, true);
  307. double value;
  308. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  309. {
  310. value = plugin->getParameterValue(i);
  311. engine->osc_send_bridge_set_parameter_value(i, value);
  312. engine->osc_send_bridge_set_default_value(i, value);
  313. }
  314. }
  315. void setMidiProgram(const uint32_t index)
  316. {
  317. qDebug("CarlaPluginClient::setMidiProgram(%i)", index);
  318. CARLA_ASSERT(engine);
  319. CARLA_ASSERT(plugin);
  320. if (! (plugin && engine))
  321. return;
  322. plugin->setMidiProgram(index, true, true, false, true);
  323. double value;
  324. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  325. {
  326. value = plugin->getParameterValue(i);
  327. engine->osc_send_bridge_set_parameter_value(i, value);
  328. engine->osc_send_bridge_set_default_value(i, value);
  329. }
  330. }
  331. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  332. {
  333. qDebug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo);
  334. CARLA_ASSERT(plugin);
  335. CARLA_ASSERT(velo > 0);
  336. if (! plugin)
  337. return;
  338. plugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  339. }
  340. void noteOff(const uint8_t channel, const uint8_t note)
  341. {
  342. qDebug("CarlaPluginClient::noteOff(%i, %i)", channel, note);
  343. CARLA_ASSERT(plugin);
  344. if (! plugin)
  345. return;
  346. plugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  347. }
  348. // ---------------------------------------------------------------------
  349. // plugin
  350. void saveNow()
  351. {
  352. qDebug("CarlaPluginClient::saveNow()");
  353. CARLA_ASSERT(plugin);
  354. CARLA_ASSERT(engine);
  355. if (! (plugin && engine))
  356. return;
  357. plugin->prepareForSave();
  358. for (uint32_t i=0; i < plugin->customDataCount(); i++)
  359. {
  360. const CarlaBackend::CustomData* const cdata = plugin->customData(i);
  361. engine->osc_send_bridge_set_custom_data(cdata->type, cdata->key, cdata->value);
  362. }
  363. if (plugin->hints() & CarlaBackend::PLUGIN_USES_CHUNKS)
  364. {
  365. void* data = nullptr;
  366. int32_t dataSize = plugin->chunkData(&data);
  367. if (data && dataSize >= 4)
  368. {
  369. QString filePath;
  370. filePath = QDir::tempPath();
  371. #ifdef Q_OS_WIN
  372. filePath += "\\.CarlaChunk_";
  373. #else
  374. filePath += "/.CarlaChunk_";
  375. #endif
  376. filePath += plugin->name();
  377. QFile file(filePath);
  378. if (file.open(QIODevice::WriteOnly))
  379. {
  380. QByteArray chunk((const char*)data, dataSize);
  381. file.write(chunk);
  382. file.close();
  383. engine->osc_send_bridge_set_chunk_data(filePath.toUtf8().constData());
  384. }
  385. }
  386. }
  387. engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_SAVED, "");
  388. }
  389. void setCustomData(const char* const type, const char* const key, const char* const value)
  390. {
  391. qDebug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  392. CARLA_ASSERT(plugin);
  393. if (! plugin)
  394. return;
  395. plugin->setCustomData(type, key, value, true);
  396. }
  397. void setChunkData(const char* const filePath)
  398. {
  399. qDebug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  400. CARLA_ASSERT(plugin);
  401. if (! plugin)
  402. return;
  403. QString chunkFilePath(filePath);
  404. #ifdef Q_OS_WIN
  405. if (chunkFilePath.startsWith("/"))
  406. {
  407. // running under Wine, posix host
  408. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  409. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  410. }
  411. #endif
  412. QFile chunkFile(chunkFilePath);
  413. if (plugin && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  414. {
  415. QTextStream in(&chunkFile);
  416. QString stringData(in.readAll());
  417. chunkFile.close();
  418. chunkFile.remove();
  419. plugin->setChunkData(stringData.toUtf8().constData());
  420. }
  421. }
  422. // ---------------------------------------------------------------------
  423. protected:
  424. int msgTimerGUI, msgTimerOSC;
  425. CarlaBackend::CarlaEngine* engine;
  426. CarlaBackend::CarlaPlugin* plugin;
  427. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3, const char* const valueStr)
  428. {
  429. qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g \"%s\")", CarlaBackend::CallbackType2Str(action), value1, value2, value3, valueStr);
  430. if (! engine)
  431. return;
  432. }
  433. void timerEvent(QTimerEvent* const event)
  434. {
  435. if (qCloseNow)
  436. return toolkitQuit();
  437. if (qSaveNow)
  438. {
  439. // TODO
  440. qSaveNow = false;
  441. }
  442. if (event->timerId() == msgTimerGUI)
  443. {
  444. if (plugin)
  445. plugin->idleGui();
  446. }
  447. else if (event->timerId() == msgTimerOSC)
  448. {
  449. if (isOscControlRegistered())
  450. oscIdle();
  451. }
  452. QObject::timerEvent(event);
  453. }
  454. };
  455. // -------------------------------------------------------------------------
  456. void CarlaBridgeToolkitPlugin::show()
  457. {
  458. qDebug("----------------------------------------------------------------------------------------------------------");
  459. qDebug("CarlaBridgeToolkitPlugin::show()");
  460. CARLA_ASSERT(gui);
  461. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  462. plugClient->showPluginGui(true);
  463. if (gui && m_uiShow)
  464. gui->setVisible(true);
  465. }
  466. void CarlaBridgeToolkitPlugin::hide()
  467. {
  468. qDebug("CarlaBridgeToolkitPlugin::hide()");
  469. CARLA_ASSERT(gui);
  470. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  471. if (gui && m_uiShow)
  472. gui->setVisible(false);
  473. plugClient->showPluginGui(false);
  474. }
  475. void CarlaBridgeToolkitPlugin::guiClosedCallback()
  476. {
  477. qDebug("CarlaBridgeToolkitPlugin::guiClosedCallback()");
  478. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  479. if (m_uiQuit)
  480. {
  481. plugClient->quit();
  482. quit();
  483. }
  484. else
  485. {
  486. plugClient->guiClosed();
  487. }
  488. }
  489. // -------------------------------------------------------------------------
  490. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  491. {
  492. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  493. if (! client)
  494. return 1;
  495. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  496. plugClient->saveNow();
  497. return 0;
  498. }
  499. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  500. {
  501. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  502. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  503. if (! client)
  504. return 1;
  505. const char* const chunkFile = (const char*)&argv[0]->s;
  506. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  507. plugClient->setChunkData(chunkFile);
  508. return 0;
  509. }
  510. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  511. {
  512. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  513. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  514. if (! client)
  515. return 1;
  516. const char* const type = (const char*)&argv[0]->s;
  517. const char* const key = (const char*)&argv[1]->s;
  518. const char* const value = (const char*)&argv[2]->s;
  519. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  520. plugClient->setCustomData(type, key, value);
  521. return 0;
  522. }
  523. // -------------------------------------------------------------------------
  524. CARLA_BRIDGE_END_NAMESPACE
  525. int main(int argc, char* argv[])
  526. {
  527. CARLA_BRIDGE_USE_NAMESPACE
  528. if (argc != 6)
  529. {
  530. qWarning("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  531. return 1;
  532. }
  533. const char* const oscUrl = argv[1];
  534. const char* const stype = argv[2];
  535. const char* const filename = argv[3];
  536. const char* name = argv[4];
  537. const char* const label = argv[5];
  538. const bool useOsc = strcmp(oscUrl, "null");
  539. if (strcmp(name, "(none)") == 0)
  540. name = nullptr;
  541. CarlaBackend::PluginType itype;
  542. if (strcmp(stype, "LADSPA") == 0)
  543. itype = CarlaBackend::PLUGIN_LADSPA;
  544. else if (strcmp(stype, "DSSI") == 0)
  545. itype = CarlaBackend::PLUGIN_DSSI;
  546. else if (strcmp(stype, "LV2") == 0)
  547. itype = CarlaBackend::PLUGIN_LV2;
  548. else if (strcmp(stype, "VST") == 0)
  549. itype = CarlaBackend::PLUGIN_VST;
  550. else
  551. {
  552. qWarning("Invalid plugin type '%s'", stype);
  553. return 1;
  554. }
  555. // Init Plugin client
  556. CarlaPluginClient client;
  557. // Init OSC
  558. if (useOsc && ! client.oscInit(oscUrl))
  559. {
  560. return 1;
  561. }
  562. // Listen for ctrl+c or sigint/sigterm events
  563. initSignalHandler();
  564. // Init backend engine
  565. CarlaBackend::CarlaEngine* engine = CarlaBackend::CarlaEngine::newDriverByName("JACK");
  566. engine->setCallback(client.callback, &client);
  567. client.setEngine(engine);
  568. // Init engine
  569. CarlaString engName(name ? name : label);
  570. engName += " (master)";
  571. engName.toBasic();
  572. engName.truncate(engine->maxClientNameSize());
  573. if (! engine->init(engName))
  574. {
  575. if (const char* const lastError = engine->getLastError())
  576. {
  577. qWarning("Bridge engine failed to start, error was:\n%s", lastError);
  578. client.sendOscBridgeError(lastError);
  579. }
  580. engine->close();
  581. delete engine;
  582. return 2;
  583. }
  584. void* extraStuff = nullptr;
  585. #if 1 // TESTING
  586. static const char* const dssiGUI = "/usr/lib/dssi/calf/calf_gtk";
  587. extraStuff = (void*)dssiGUI;
  588. #endif
  589. // Init plugin
  590. short id = engine->addPlugin(itype, filename, name, label, extraStuff);
  591. int ret;
  592. if (id >= 0 && id < CarlaBackend::MAX_PLUGINS)
  593. {
  594. CarlaBackend::CarlaPlugin* const plugin = engine->getPlugin(id);
  595. client.setPlugin(plugin);
  596. if (! useOsc)
  597. plugin->setActive(true, false, false);
  598. client.init();
  599. client.toolkitExec(!useOsc);
  600. client.quit();
  601. ret = 0;
  602. }
  603. else
  604. {
  605. const char* const lastError = engine->getLastError();
  606. qWarning("Plugin failed to load, error was:\n%s", lastError);
  607. if (useOsc)
  608. client.sendOscBridgeError(lastError);
  609. ret = 1;
  610. }
  611. engine->aboutToClose();
  612. engine->removeAllPlugins();
  613. engine->close();
  614. delete engine;
  615. // Close OSC
  616. if (useOsc)
  617. {
  618. client.oscClose();
  619. }
  620. return ret;
  621. }
  622. #endif // BRIDGE_PLUGIN