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.

1168 lines
30KB

  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_backend_utils.hpp"
  21. #include "carla_engine.hpp"
  22. #include "carla_plugin.hpp"
  23. #include "Shared.hpp"
  24. #include <set>
  25. #include <QtCore/QDir>
  26. #include <QtCore/QFile>
  27. #include <QtCore/QTextStream>
  28. #include <QtCore/QThread>
  29. #include <QtXml/QDomDocument>
  30. #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
  31. # include <QtWidgets/QApplication>
  32. #else
  33. # include <QtGui/QApplication>
  34. #endif
  35. #ifdef Q_OS_UNIX
  36. # include <signal.h>
  37. #endif
  38. // -------------------------------------------------------------------------
  39. static int qargc = 0;
  40. static char** qargv = nullptr;
  41. static bool qCloseNow = false;
  42. static bool qSaveNow = false;
  43. #if defined(Q_OS_HAIKU) || defined(Q_OS_UNIX)
  44. void closeSignalHandler(int)
  45. {
  46. qCloseNow = true;
  47. }
  48. void saveSignalHandler(int)
  49. {
  50. qSaveNow = true;
  51. }
  52. #elif defined(Q_OS_WIN)
  53. BOOL WINAPI closeSignalHandler(DWORD dwCtrlType)
  54. {
  55. if (dwCtrlType == CTRL_C_EVENT)
  56. {
  57. qCloseNow = true;
  58. return TRUE;
  59. }
  60. return FALSE;
  61. }
  62. #endif
  63. void initSignalHandler()
  64. {
  65. #if defined(Q_OS_HAIKU) || defined(Q_OS_UNIX)
  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. #elif defined(Q_OS_WIN)
  85. SetConsoleCtrlHandler(closeSignalHandler, TRUE);
  86. #endif
  87. }
  88. CARLA_BRIDGE_START_NAMESPACE
  89. // -------------------------------------------------------------------------
  90. class CarlaBridgeToolkitPlugin : public CarlaBridgeToolkit //,
  91. //public CarlaBackend::CarlaPluginGUI::Callback
  92. {
  93. public:
  94. CarlaBridgeToolkitPlugin(CarlaBridgeClient* const client, const char* const uiTitle)
  95. : CarlaBridgeToolkit(client, uiTitle)
  96. {
  97. qDebug("CarlaBridgeToolkitPlugin::CarlaBridgeToolkitPlugin(%p, \"%s\")", client, uiTitle);
  98. app = nullptr;
  99. //gui = nullptr;
  100. m_hasUI = false;
  101. m_uiQuit = false;
  102. m_uiShow = false;
  103. init();
  104. }
  105. ~CarlaBridgeToolkitPlugin()
  106. {
  107. qDebug("CarlaBridgeToolkitPlugin::~CarlaBridgeToolkitPlugin()");
  108. #if 0
  109. if (gui)
  110. {
  111. gui->close();
  112. delete gui;
  113. gui = nullptr;
  114. }
  115. #endif
  116. if (app)
  117. {
  118. if (! app->closingDown())
  119. app->quit();
  120. delete app;
  121. app = nullptr;
  122. }
  123. }
  124. void init()
  125. {
  126. qDebug("CarlaBridgeToolkitPlugin::init()");
  127. CARLA_ASSERT(! app);
  128. //CARLA_ASSERT(! gui);
  129. app = new QApplication(qargc, qargv);
  130. //gui = new CarlaBackend::CarlaPluginGUI(nullptr, this);
  131. }
  132. void exec(const bool showGui)
  133. {
  134. qDebug("CarlaBridgeToolkitPlugin::exec(%s)", bool2str(showGui));
  135. CARLA_ASSERT(app);
  136. //CARLA_ASSERT(gui);
  137. CARLA_ASSERT(client);
  138. if (showGui)
  139. {
  140. if (m_hasUI)
  141. show();
  142. }
  143. else
  144. {
  145. app->setQuitOnLastWindowClosed(false);
  146. client->sendOscUpdate();
  147. client->sendOscBridgeUpdate();
  148. }
  149. m_uiQuit = showGui;
  150. // Main loop
  151. app->exec();
  152. }
  153. void quit()
  154. {
  155. qDebug("CarlaBridgeToolkitPlugin::quit()");
  156. CARLA_ASSERT(app);
  157. if (app && ! app->closingDown())
  158. app->quit();
  159. }
  160. void show();
  161. void hide();
  162. void resize(const int width, const int height)
  163. {
  164. qDebug("CarlaBridgeToolkitPlugin::resize(%i, %i)", width, height);
  165. #if 0
  166. CARLA_ASSERT(gui);
  167. if (gui)
  168. gui->setNewSize(width, height);
  169. #endif
  170. }
  171. #if 0
  172. GuiContainer* getContainer() const
  173. {
  174. CARLA_ASSERT(gui);
  175. if (gui)
  176. return gui->getContainer();
  177. return nullptr;
  178. }
  179. void* getContainerId()
  180. {
  181. CARLA_ASSERT(gui);
  182. if (gui)
  183. return (void*)gui->getWinId();
  184. return nullptr;
  185. }
  186. #endif
  187. void setHasUI(const bool hasUI, const bool showUI)
  188. {
  189. m_hasUI = hasUI;
  190. m_uiShow = showUI;
  191. }
  192. void guiClosedCallback();
  193. protected:
  194. QApplication* app;
  195. //CarlaBackend::CarlaPluginGUI* gui;
  196. private:
  197. bool m_hasUI;
  198. bool m_uiQuit;
  199. bool m_uiShow;
  200. };
  201. CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle)
  202. {
  203. return new CarlaBridgeToolkitPlugin(client, uiTitle);
  204. }
  205. // -------------------------------------------------------------------------
  206. class CarlaPluginClient : public CarlaBridgeClient,
  207. public QObject
  208. {
  209. public:
  210. CarlaPluginClient()
  211. : CarlaBridgeClient(""),
  212. QObject(nullptr)
  213. {
  214. qDebug("CarlaPluginClient::CarlaPluginClient()");
  215. msgTimerGUI = 0;
  216. msgTimerOSC = 0;
  217. needsResize = 0;
  218. nextWidth = 0;
  219. nextHeight = 0;
  220. engine = nullptr;
  221. plugin = nullptr;
  222. }
  223. ~CarlaPluginClient()
  224. {
  225. qDebug("CarlaPluginClient::~CarlaPluginClient()");
  226. CARLA_ASSERT(msgTimerGUI == 0);
  227. CARLA_ASSERT(msgTimerOSC == 0);
  228. }
  229. // ---------------------------------------------------------------------
  230. void init()
  231. {
  232. CARLA_ASSERT(plugin);
  233. msgTimerGUI = startTimer(50);
  234. msgTimerOSC = startTimer(25);
  235. if (! plugin)
  236. return;
  237. #if 0
  238. bool guiResizable;
  239. CarlaBackend::GuiType guiType;
  240. plugin->getGuiInfo(&guiType, &guiResizable);
  241. CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit;
  242. if (guiType == CarlaBackend::GUI_INTERNAL_QT4 || guiType == CarlaBackend::GUI_INTERNAL_COCOA || guiType == CarlaBackend::GUI_INTERNAL_HWND || guiType == CarlaBackend::GUI_INTERNAL_X11)
  243. {
  244. plugin->setGuiContainer(plugToolkit->getContainer());
  245. plugToolkit->setHasUI(true, true);
  246. }
  247. else
  248. {
  249. plugToolkit->setHasUI(guiType != CarlaBackend::GUI_NONE, false);
  250. }
  251. #endif
  252. }
  253. void quit()
  254. {
  255. engine = nullptr;
  256. plugin = nullptr;
  257. if (msgTimerGUI != 0)
  258. {
  259. killTimer(msgTimerGUI);
  260. msgTimerGUI = 0;
  261. }
  262. if (msgTimerOSC != 0)
  263. {
  264. killTimer(msgTimerOSC);
  265. msgTimerOSC = 0;
  266. }
  267. }
  268. // ---------------------------------------------------------------------
  269. void setEngine(CarlaBackend::CarlaEngine* const engine)
  270. {
  271. qDebug("CarlaPluginClient::setEngine(%p)", engine);
  272. CARLA_ASSERT(engine);
  273. this->engine = engine;
  274. engine->setOscBridgeData(m_oscData);
  275. }
  276. void setPlugin(CarlaBackend::CarlaPlugin* const plugin)
  277. {
  278. qDebug("CarlaPluginClient::setPlugin(%p)", plugin);
  279. CARLA_ASSERT(plugin);
  280. this->plugin = plugin;
  281. // load carla plugin preset if possible
  282. if (const char* const pName = plugin->name())
  283. {
  284. QFile pFile(QDir::currentPath() + QDir::separator() + pName + ".carxs");
  285. qDebug("Trying to load plugin preset file '%s'", pFile.fileName().toUtf8().constData());
  286. if (! /*(*/pFile.exists() /*&& pFile.isReadable())*/)
  287. {
  288. qDebug("Plugin preset file doesn't exist or is not readable");
  289. return;
  290. }
  291. if (! pFile.open(QFile::ReadOnly))
  292. {
  293. qWarning("Plugin preset file read failed");
  294. return;
  295. }
  296. QDomDocument xml;
  297. xml.setContent(pFile.readAll());
  298. QDomElement xmlNode(xml.documentElement());
  299. if (xmlNode.tagName() == "CARLA-PRESET")
  300. {
  301. loadStateDict(getSaveStateDictFromXML(xmlNode));
  302. }
  303. else
  304. qWarning("Plugin preset file is not valid or corrupted");
  305. pFile.close();
  306. }
  307. }
  308. // ---------------------------------------------------------------------
  309. void loadStateDict(const CarlaSaveState* const content)
  310. {
  311. CARLA_ASSERT(content);
  312. if (! content)
  313. return;
  314. qDebug("Loading plugin state now...");
  315. #if 0
  316. // ---------------------------------------------------------------------
  317. // Part 1 - set custom data (except chunks)
  318. foreach (const CarlaStateCustomData& customData, content->customData)
  319. {
  320. if (customData.type != CUSTOM_DATA_CHUNK)
  321. {
  322. const char* const type = customData.type.toUtf8().constData();
  323. const char* const key = customData.key.toUtf8().constData();
  324. const char* const value = customData.value.toUtf8().constData();
  325. plugin->setCustomData(type, key, value, true);
  326. }
  327. }
  328. #endif
  329. // ---------------------------------------------------------------------
  330. // Part 2 - set program
  331. int32_t programId = -1;
  332. if (! content->currentProgramName.isEmpty())
  333. {
  334. const uint32_t programCount = plugin->programCount();
  335. char strBuf[STR_MAX] = { 0 };
  336. plugin->getProgramName(content->currentProgramIndex, strBuf);
  337. QString testProgramName(strBuf);
  338. // Program name matches
  339. if (content->currentProgramName == testProgramName)
  340. {
  341. programId = content->currentProgramIndex;
  342. }
  343. // index < count
  344. else if (content->currentProgramIndex < (int32_t)programCount)
  345. {
  346. programId = content->currentProgramIndex;
  347. }
  348. // index not valid, try to find by name
  349. else
  350. {
  351. for (uint32_t i=0; i < programCount; i++)
  352. {
  353. plugin->getProgramName(i, strBuf);
  354. testProgramName = QString(strBuf);
  355. if (content->currentProgramName == testProgramName)
  356. {
  357. programId = i;
  358. break;
  359. }
  360. }
  361. }
  362. }
  363. // set program now, if valid
  364. if (programId >= 0)
  365. {
  366. plugin->setProgram(programId, true, true, false, true);
  367. }
  368. // ---------------------------------------------------------------------
  369. // Part 3 - set midi program
  370. if (content->currentMidiBank >= 0 and content->currentMidiProgram >= 0)
  371. {
  372. const uint32_t midiProgramCount = plugin->midiProgramCount();
  373. for (uint32_t i=0; i < midiProgramCount; i++)
  374. {
  375. const MidiProgramData& midiProgramData = plugin->midiProgramData(i);
  376. if ((int32_t)midiProgramData.bank == content->currentMidiBank && (int32_t)midiProgramData.program == content->currentMidiProgram)
  377. {
  378. plugin->setMidiProgram(i, true, true, false, true);
  379. break;
  380. }
  381. }
  382. }
  383. // ---------------------------------------------------------------------
  384. // Part 4a - get plugin parameter symbols
  385. struct ParamSymbol {
  386. uint32_t index;
  387. QString symbol;
  388. ParamSymbol() {}
  389. ParamSymbol(uint32_t index_, const char* symbol_)
  390. : index(index_),
  391. symbol(symbol_) {}
  392. };
  393. QVector<ParamSymbol> paramSymbols;
  394. foreach (const CarlaStateParameter& parameter, content->parameters)
  395. {
  396. if (! parameter.symbol.isEmpty())
  397. {
  398. char strBuf[STR_MAX] = { 0 };
  399. plugin->getParameterSymbol(parameter.index, strBuf);
  400. if (strBuf[0] != 0)
  401. {
  402. ParamSymbol ps(parameter.index, strBuf);
  403. paramSymbols.append(ps);
  404. }
  405. }
  406. }
  407. // ---------------------------------------------------------------------
  408. // Part 4b - set parameter values (carefully)
  409. const double sampleRate = engine->getSampleRate();
  410. foreach (const CarlaStateParameter& parameter, content->parameters)
  411. {
  412. int32_t index = -1;
  413. if (content->type == "LADSPA")
  414. {
  415. // Try to set by symbol, otherwise use index
  416. if (! parameter.symbol.isEmpty())
  417. {
  418. bool breaked = false;
  419. foreach (const ParamSymbol& ps, paramSymbols)
  420. {
  421. if (parameter.symbol == ps.symbol)
  422. {
  423. index = ps.index;
  424. breaked = true;
  425. break;
  426. }
  427. }
  428. if (! breaked)
  429. index = parameter.index;
  430. }
  431. else
  432. index = parameter.index;
  433. }
  434. else if (content->type == "LV2")
  435. {
  436. // Symbol only
  437. if (! parameter.symbol.isEmpty())
  438. {
  439. bool breaked = false;
  440. foreach (const ParamSymbol& ps, paramSymbols)
  441. {
  442. if (parameter.symbol == ps.symbol)
  443. {
  444. index = ps.index;
  445. breaked = true;
  446. break;
  447. }
  448. }
  449. if (! breaked)
  450. qWarning("Failed to find LV2 parameter symbol for '%s')", parameter.symbol.toUtf8().constData());
  451. }
  452. else
  453. qWarning("LV2 Plugin parameter '%s' has no symbol", parameter.name.toUtf8().constData());
  454. }
  455. else
  456. {
  457. // Index only
  458. index = parameter.index;
  459. }
  460. // Now set parameter
  461. if (index >= 0)
  462. {
  463. const ParameterData& paramData = plugin->parameterData(index);
  464. double value = parameter.value;
  465. if (paramData.hints & PARAMETER_USES_SAMPLERATE)
  466. value *= sampleRate;
  467. plugin->setParameterValue(index, value, true, true, false);
  468. plugin->setParameterMidiCC(index, parameter.midiCC, true, false);
  469. plugin->setParameterMidiChannel(index, parameter.midiChannel-1, true, false);
  470. }
  471. else
  472. qWarning("Could not set parameter data for '%s')", parameter.name.toUtf8().constData());
  473. }
  474. #if 0
  475. // ---------------------------------------------------------------------
  476. // Part 5 - set chunk data
  477. foreach (const CarlaStateCustomData& customData, content->customData)
  478. {
  479. if (customData.type == CUSTOM_DATA_CHUNK)
  480. {
  481. const char* const type = customData.type.toUtf8().constData();
  482. const char* const key = customData.key.toUtf8().constData();
  483. const char* const value = customData.value.toUtf8().constData();
  484. plugin->setCustomData(type, key, value, true);
  485. }
  486. }
  487. #endif
  488. if (! content->chunk.isEmpty())
  489. plugin->setChunkData(content->chunk.toUtf8().constData());
  490. qDebug("Loading plugin state now finished");
  491. }
  492. void guiClosed()
  493. {
  494. CARLA_ASSERT(engine);
  495. //if (engine)
  496. // engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, "");
  497. }
  498. void showPluginGui(const bool yesNo)
  499. {
  500. CARLA_ASSERT(plugin);
  501. if (plugin)
  502. plugin->showGui(yesNo);
  503. }
  504. // ---------------------------------------------------------------------
  505. // processing
  506. void setParameter(const int32_t rindex, const float value)
  507. {
  508. qDebug("CarlaPluginClient::setParameter(%i, %g)", rindex, value);
  509. CARLA_ASSERT(plugin);
  510. if (plugin)
  511. plugin->setParameterValueByRIndex(rindex, value, true, true, false);
  512. }
  513. void setProgram(const uint32_t index)
  514. {
  515. qDebug("CarlaPluginClient::setProgram(%i)", index);
  516. CARLA_ASSERT(engine);
  517. CARLA_ASSERT(plugin);
  518. CARLA_ASSERT(index < plugin->programCount());
  519. if (! (plugin && engine))
  520. return;
  521. if (index >= plugin->programCount())
  522. return;
  523. plugin->setProgram(index, true, true, false, true);
  524. double value;
  525. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  526. {
  527. value = plugin->getParameterValue(i);
  528. engine->osc_send_bridge_set_parameter_value(i, value);
  529. engine->osc_send_bridge_set_default_value(i, value);
  530. }
  531. }
  532. void setMidiProgram(const uint32_t index)
  533. {
  534. qDebug("CarlaPluginClient::setMidiProgram(%i)", index);
  535. CARLA_ASSERT(engine);
  536. CARLA_ASSERT(plugin);
  537. if (! (plugin && engine))
  538. return;
  539. plugin->setMidiProgram(index, true, true, false, true);
  540. double value;
  541. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  542. {
  543. value = plugin->getParameterValue(i);
  544. engine->osc_send_bridge_set_parameter_value(i, value);
  545. engine->osc_send_bridge_set_default_value(i, value);
  546. }
  547. }
  548. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  549. {
  550. qDebug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo);
  551. CARLA_ASSERT(plugin);
  552. CARLA_ASSERT(velo > 0);
  553. if (! plugin)
  554. return;
  555. //plugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  556. }
  557. void noteOff(const uint8_t channel, const uint8_t note)
  558. {
  559. qDebug("CarlaPluginClient::noteOff(%i, %i)", channel, note);
  560. CARLA_ASSERT(plugin);
  561. if (! plugin)
  562. return;
  563. //plugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  564. }
  565. // ---------------------------------------------------------------------
  566. // plugin
  567. void saveNow()
  568. {
  569. qDebug("CarlaPluginClient::saveNow()");
  570. CARLA_ASSERT(plugin);
  571. CARLA_ASSERT(engine);
  572. if (! (plugin && engine))
  573. return;
  574. plugin->prepareForSave();
  575. for (uint32_t i=0; i < plugin->customDataCount(); i++)
  576. {
  577. const CarlaBackend::CustomData& cdata = plugin->customData(i);
  578. engine->osc_send_bridge_set_custom_data(cdata.type, cdata.key, cdata.value);
  579. }
  580. if (plugin->hints() & CarlaBackend::PLUGIN_USES_CHUNKS)
  581. {
  582. void* data = nullptr;
  583. int32_t dataSize = plugin->chunkData(&data);
  584. if (data && dataSize >= 4)
  585. {
  586. QString filePath;
  587. filePath = QDir::tempPath();
  588. #ifdef Q_OS_WIN
  589. filePath += "\\.CarlaChunk_";
  590. #else
  591. filePath += "/.CarlaChunk_";
  592. #endif
  593. filePath += plugin->name();
  594. QFile file(filePath);
  595. if (file.open(QIODevice::WriteOnly))
  596. {
  597. QByteArray chunk((const char*)data, dataSize);
  598. file.write(chunk);
  599. file.close();
  600. engine->osc_send_bridge_set_chunk_data(filePath.toUtf8().constData());
  601. }
  602. }
  603. }
  604. //engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_SAVED, "");
  605. }
  606. void setCustomData(const char* const type, const char* const key, const char* const value)
  607. {
  608. qDebug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  609. CARLA_ASSERT(plugin);
  610. if (! plugin)
  611. return;
  612. plugin->setCustomData(type, key, value, true);
  613. }
  614. void setChunkData(const char* const filePath)
  615. {
  616. qDebug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  617. CARLA_ASSERT(plugin);
  618. if (! plugin)
  619. return;
  620. QString chunkFilePath(filePath);
  621. #ifdef Q_OS_WIN
  622. if (chunkFilePath.startsWith("/"))
  623. {
  624. // running under Wine, posix host
  625. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  626. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  627. }
  628. #endif
  629. QFile chunkFile(chunkFilePath);
  630. if (plugin && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  631. {
  632. QTextStream in(&chunkFile);
  633. QString stringData(in.readAll());
  634. chunkFile.close();
  635. chunkFile.remove();
  636. plugin->setChunkData(stringData.toUtf8().constData());
  637. }
  638. }
  639. // ---------------------------------------------------------------------
  640. // callback
  641. static void callback(void* ptr, CarlaBackend::CallbackType action, unsigned int, int value1, int value2, float value3, const char* valueStr)
  642. {
  643. CARLA_ASSERT(ptr);
  644. if (CarlaPluginClient* const _this_ = (CarlaPluginClient*)ptr)
  645. _this_->handleCallback(action, value1, value2, value3, valueStr);
  646. }
  647. // ---------------------------------------------------------------------
  648. protected:
  649. int msgTimerGUI;
  650. int msgTimerOSC;
  651. bool needsResize;
  652. int nextWidth;
  653. int nextHeight;
  654. CarlaBackend::CarlaEngine* engine;
  655. CarlaBackend::CarlaPlugin* plugin;
  656. std::set<int32_t> parametersToUpdate;
  657. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3, const char* const valueStr)
  658. {
  659. CARLA_BACKEND_USE_NAMESPACE
  660. qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g \"%s\")", CallbackType2Str(action), value1, value2, value3, valueStr);
  661. if (! engine)
  662. return;
  663. switch (action)
  664. {
  665. case CALLBACK_DEBUG:
  666. break;
  667. case CALLBACK_PLUGIN_ADDED:
  668. break;
  669. case CALLBACK_PLUGIN_REMOVED:
  670. break;
  671. case CALLBACK_PARAMETER_VALUE_CHANGED:
  672. parametersToUpdate.insert(value1);
  673. break;
  674. case CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  675. // todo, unused?
  676. break;
  677. case CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  678. // todo, unused?
  679. break;
  680. case CALLBACK_PROGRAM_CHANGED:
  681. engine->osc_send_bridge_set_program(value1);
  682. break;
  683. case CALLBACK_MIDI_PROGRAM_CHANGED:
  684. engine->osc_send_bridge_set_midi_program(value1);
  685. break;
  686. case CALLBACK_NOTE_ON:
  687. // todo
  688. break;
  689. case CALLBACK_NOTE_OFF:
  690. // todo
  691. break;
  692. case CALLBACK_SHOW_GUI:
  693. if (value1 == 0)
  694. {
  695. CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit;
  696. plugToolkit->guiClosedCallback();
  697. }
  698. break;
  699. case CALLBACK_UPDATE:
  700. // todo
  701. break;
  702. case CALLBACK_RELOAD_INFO:
  703. // todo
  704. break;
  705. case CALLBACK_RELOAD_PARAMETERS:
  706. // todo
  707. break;
  708. case CALLBACK_RELOAD_PROGRAMS:
  709. // todo
  710. break;
  711. case CALLBACK_RELOAD_ALL:
  712. // todo
  713. break;
  714. case CALLBACK_NSM_ANNOUNCE:
  715. case CALLBACK_NSM_OPEN1:
  716. case CALLBACK_NSM_OPEN2:
  717. case CALLBACK_NSM_SAVE:
  718. break;
  719. case CALLBACK_ERROR:
  720. break;
  721. case CALLBACK_QUIT:
  722. m_toolkit->quit();
  723. break;
  724. }
  725. }
  726. void timerEvent(QTimerEvent* const event)
  727. {
  728. if (qCloseNow)
  729. return toolkitQuit();
  730. if (qSaveNow)
  731. {
  732. // TODO
  733. qSaveNow = false;
  734. }
  735. if (event->timerId() == msgTimerGUI)
  736. {
  737. if (plugin)
  738. plugin->idleGui();
  739. }
  740. else if (event->timerId() == msgTimerOSC)
  741. {
  742. if (isOscControlRegistered())
  743. oscIdle();
  744. }
  745. QObject::timerEvent(event);
  746. }
  747. };
  748. // -------------------------------------------------------------------------
  749. void CarlaBridgeToolkitPlugin::show()
  750. {
  751. qDebug("CarlaBridgeToolkitPlugin::show()");
  752. #if 0
  753. CARLA_ASSERT(gui);
  754. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  755. plugClient->showPluginGui(true);
  756. if (gui && m_uiShow)
  757. gui->setVisible(true);
  758. #endif
  759. }
  760. void CarlaBridgeToolkitPlugin::hide()
  761. {
  762. qDebug("CarlaBridgeToolkitPlugin::hide()");
  763. #if 0
  764. CARLA_ASSERT(gui);
  765. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  766. if (gui && m_uiShow)
  767. gui->setVisible(false);
  768. plugClient->showPluginGui(false);
  769. #endif
  770. }
  771. void CarlaBridgeToolkitPlugin::guiClosedCallback()
  772. {
  773. qDebug("CarlaBridgeToolkitPlugin::guiClosedCallback()");
  774. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  775. if (m_uiQuit)
  776. {
  777. plugClient->quit();
  778. quit();
  779. }
  780. else
  781. {
  782. plugClient->guiClosed();
  783. }
  784. }
  785. // -------------------------------------------------------------------------
  786. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  787. {
  788. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  789. if (! client)
  790. return 1;
  791. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  792. plugClient->saveNow();
  793. return 0;
  794. }
  795. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  796. {
  797. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  798. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  799. if (! client)
  800. return 1;
  801. const char* const chunkFile = (const char*)&argv[0]->s;
  802. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  803. plugClient->setChunkData(chunkFile);
  804. return 0;
  805. }
  806. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  807. {
  808. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  809. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  810. if (! client)
  811. return 1;
  812. const char* const type = (const char*)&argv[0]->s;
  813. const char* const key = (const char*)&argv[1]->s;
  814. const char* const value = (const char*)&argv[2]->s;
  815. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  816. plugClient->setCustomData(type, key, value);
  817. return 0;
  818. }
  819. // -------------------------------------------------------------------------
  820. CARLA_BRIDGE_END_NAMESPACE
  821. int main(int argc, char* argv[])
  822. {
  823. CARLA_BRIDGE_USE_NAMESPACE
  824. if (argc != 6)
  825. {
  826. qWarning("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  827. return 1;
  828. }
  829. const char* const oscUrl = argv[1];
  830. const char* const stype = argv[2];
  831. const char* const filename = argv[3];
  832. const char* name = argv[4];
  833. const char* const label = argv[5];
  834. const bool useOsc = strcmp(oscUrl, "null");
  835. if (strcmp(name, "(none)") == 0)
  836. name = nullptr;
  837. CarlaBackend::PluginType itype;
  838. if (strcmp(stype, "LADSPA") == 0)
  839. itype = CarlaBackend::PLUGIN_LADSPA;
  840. else if (strcmp(stype, "DSSI") == 0)
  841. itype = CarlaBackend::PLUGIN_DSSI;
  842. else if (strcmp(stype, "LV2") == 0)
  843. itype = CarlaBackend::PLUGIN_LV2;
  844. else if (strcmp(stype, "VST") == 0)
  845. itype = CarlaBackend::PLUGIN_VST;
  846. else
  847. {
  848. qWarning("Invalid plugin type '%s'", stype);
  849. return 1;
  850. }
  851. // Init Plugin client
  852. CarlaPluginClient client;
  853. // Init OSC
  854. if (useOsc && ! client.oscInit(oscUrl))
  855. {
  856. return 1;
  857. }
  858. // Listen for ctrl+c or sigint/sigterm events
  859. initSignalHandler();
  860. // Init backend engine
  861. CarlaBackend::CarlaEngine* engine = CarlaBackend::CarlaEngine::newDriverByName("JACK");
  862. engine->setCallback(client.callback, &client);
  863. client.setEngine(engine);
  864. // Init engine
  865. CarlaString engName(name ? name : label);
  866. engName += " (master)";
  867. engName.toBasic();
  868. engName.truncate(engine->maxClientNameSize());
  869. if (! engine->init((const char*)engName))
  870. {
  871. if (const char* const lastError = engine->getLastError())
  872. {
  873. qWarning("Bridge engine failed to start, error was:\n%s", lastError);
  874. client.sendOscBridgeError(lastError);
  875. }
  876. engine->close();
  877. delete engine;
  878. return 2;
  879. }
  880. void* extraStuff = nullptr;
  881. if (itype == CarlaBackend::PLUGIN_DSSI)
  882. extraStuff = findDSSIGUI(filename, name, label);
  883. // Init plugin
  884. int ret;
  885. if (engine->addPlugin(itype, filename, name, label, extraStuff))
  886. {
  887. CarlaBackend::CarlaPlugin* const plugin = engine->getPlugin(0);
  888. client.setPlugin(plugin);
  889. if (! useOsc)
  890. plugin->setActive(true, false, false);
  891. client.init();
  892. client.toolkitExec(!useOsc);
  893. client.quit();
  894. ret = 0;
  895. }
  896. else
  897. {
  898. const char* const lastError = engine->getLastError();
  899. qWarning("Plugin failed to load, error was:\n%s", lastError);
  900. if (useOsc)
  901. client.sendOscBridgeError(lastError);
  902. ret = 1;
  903. }
  904. if (extraStuff && itype == CarlaBackend::PLUGIN_DSSI)
  905. free((char*)extraStuff);
  906. engine->setAboutToClose();
  907. engine->removeAllPlugins();
  908. engine->close();
  909. delete engine;
  910. // Close OSC
  911. if (useOsc)
  912. {
  913. client.oscClose();
  914. }
  915. return ret;
  916. }
  917. #endif // BRIDGE_PLUGIN