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.

760 lines
19KB

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