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.

1163 lines
30KB

  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 "carla_bridge_client.hpp"
  19. #include "carla_bridge_toolkit.hpp"
  20. #include "carla_backend_utils.hpp"
  21. #include "carla_state_utils.hpp"
  22. #include "carla_engine.hpp"
  23. #include "carla_plugin.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(CarlaBackend::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 CarlaBackend::SaveState& content)
  310. {
  311. qDebug("Loading plugin state now...");
  312. #if 0
  313. // ---------------------------------------------------------------------
  314. // Part 1 - set custom data (except chunks)
  315. foreach (const CarlaStateCustomData& customData, content->customData)
  316. {
  317. if (customData.type != CUSTOM_DATA_CHUNK)
  318. {
  319. const char* const type = customData.type.toUtf8().constData();
  320. const char* const key = customData.key.toUtf8().constData();
  321. const char* const value = customData.value.toUtf8().constData();
  322. plugin->setCustomData(type, key, value, true);
  323. }
  324. }
  325. // ---------------------------------------------------------------------
  326. // Part 2 - set program
  327. int32_t programId = -1;
  328. if (! content->currentProgramName.isEmpty())
  329. {
  330. const uint32_t programCount = plugin->programCount();
  331. char strBuf[STR_MAX] = { 0 };
  332. plugin->getProgramName(content->currentProgramIndex, strBuf);
  333. QString testProgramName(strBuf);
  334. // Program name matches
  335. if (content->currentProgramName == testProgramName)
  336. {
  337. programId = content->currentProgramIndex;
  338. }
  339. // index < count
  340. else if (content->currentProgramIndex < (int32_t)programCount)
  341. {
  342. programId = content->currentProgramIndex;
  343. }
  344. // index not valid, try to find by name
  345. else
  346. {
  347. for (uint32_t i=0; i < programCount; i++)
  348. {
  349. plugin->getProgramName(i, strBuf);
  350. testProgramName = QString(strBuf);
  351. if (content->currentProgramName == testProgramName)
  352. {
  353. programId = i;
  354. break;
  355. }
  356. }
  357. }
  358. }
  359. // set program now, if valid
  360. if (programId >= 0)
  361. {
  362. plugin->setProgram(programId, true, true, false, true);
  363. }
  364. // ---------------------------------------------------------------------
  365. // Part 3 - set midi program
  366. if (content->currentMidiBank >= 0 and content->currentMidiProgram >= 0)
  367. {
  368. const uint32_t midiProgramCount = plugin->midiProgramCount();
  369. for (uint32_t i=0; i < midiProgramCount; i++)
  370. {
  371. const MidiProgramData& midiProgramData = plugin->midiProgramData(i);
  372. if ((int32_t)midiProgramData.bank == content->currentMidiBank && (int32_t)midiProgramData.program == content->currentMidiProgram)
  373. {
  374. plugin->setMidiProgram(i, true, true, false, true);
  375. break;
  376. }
  377. }
  378. }
  379. // ---------------------------------------------------------------------
  380. // Part 4a - get plugin parameter symbols
  381. struct ParamSymbol {
  382. uint32_t index;
  383. QString symbol;
  384. ParamSymbol() {}
  385. ParamSymbol(uint32_t index_, const char* symbol_)
  386. : index(index_),
  387. symbol(symbol_) {}
  388. };
  389. QVector<ParamSymbol> paramSymbols;
  390. foreach (const CarlaStateParameter& parameter, content->parameters)
  391. {
  392. if (! parameter.symbol.isEmpty())
  393. {
  394. char strBuf[STR_MAX] = { 0 };
  395. plugin->getParameterSymbol(parameter.index, strBuf);
  396. if (strBuf[0] != 0)
  397. {
  398. ParamSymbol ps(parameter.index, strBuf);
  399. paramSymbols.append(ps);
  400. }
  401. }
  402. }
  403. // ---------------------------------------------------------------------
  404. // Part 4b - set parameter values (carefully)
  405. const double sampleRate = engine->getSampleRate();
  406. foreach (const CarlaStateParameter& parameter, content->parameters)
  407. {
  408. int32_t index = -1;
  409. if (content->type == "LADSPA")
  410. {
  411. // Try to set by symbol, otherwise use index
  412. if (! parameter.symbol.isEmpty())
  413. {
  414. bool breaked = false;
  415. foreach (const ParamSymbol& ps, paramSymbols)
  416. {
  417. if (parameter.symbol == ps.symbol)
  418. {
  419. index = ps.index;
  420. breaked = true;
  421. break;
  422. }
  423. }
  424. if (! breaked)
  425. index = parameter.index;
  426. }
  427. else
  428. index = parameter.index;
  429. }
  430. else if (content->type == "LV2")
  431. {
  432. // Symbol only
  433. if (! parameter.symbol.isEmpty())
  434. {
  435. bool breaked = false;
  436. foreach (const ParamSymbol& ps, paramSymbols)
  437. {
  438. if (parameter.symbol == ps.symbol)
  439. {
  440. index = ps.index;
  441. breaked = true;
  442. break;
  443. }
  444. }
  445. if (! breaked)
  446. qWarning("Failed to find LV2 parameter symbol for '%s')", parameter.symbol.toUtf8().constData());
  447. }
  448. else
  449. qWarning("LV2 Plugin parameter '%s' has no symbol", parameter.name.toUtf8().constData());
  450. }
  451. else
  452. {
  453. // Index only
  454. index = parameter.index;
  455. }
  456. // Now set parameter
  457. if (index >= 0)
  458. {
  459. const ParameterData& paramData = plugin->parameterData(index);
  460. double value = parameter.value;
  461. if (paramData.hints & PARAMETER_USES_SAMPLERATE)
  462. value *= sampleRate;
  463. plugin->setParameterValue(index, value, true, true, false);
  464. plugin->setParameterMidiCC(index, parameter.midiCC, true, false);
  465. plugin->setParameterMidiChannel(index, parameter.midiChannel-1, true, false);
  466. }
  467. else
  468. qWarning("Could not set parameter data for '%s')", parameter.name.toUtf8().constData());
  469. }
  470. #endif
  471. #if 0
  472. // ---------------------------------------------------------------------
  473. // Part 5 - set chunk data
  474. foreach (const CarlaStateCustomData& customData, content->customData)
  475. {
  476. if (customData.type == CUSTOM_DATA_CHUNK)
  477. {
  478. const char* const type = customData.type.toUtf8().constData();
  479. const char* const key = customData.key.toUtf8().constData();
  480. const char* const value = customData.value.toUtf8().constData();
  481. plugin->setCustomData(type, key, value, true);
  482. }
  483. }
  484. #endif
  485. //if (! content.chunk.isEmpty())
  486. // plugin->setChunkData(content.chunk);
  487. qDebug("Loading plugin state now finished");
  488. }
  489. void guiClosed()
  490. {
  491. CARLA_ASSERT(engine);
  492. //if (engine)
  493. // engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_HIDE_GUI, "");
  494. }
  495. void showPluginGui(const bool yesNo)
  496. {
  497. CARLA_ASSERT(plugin);
  498. if (plugin)
  499. plugin->showGui(yesNo);
  500. }
  501. // ---------------------------------------------------------------------
  502. // processing
  503. void setParameter(const int32_t rindex, const float value)
  504. {
  505. qDebug("CarlaPluginClient::setParameter(%i, %g)", rindex, value);
  506. CARLA_ASSERT(plugin);
  507. if (plugin)
  508. plugin->setParameterValueByRIndex(rindex, value, true, true, false);
  509. }
  510. void setProgram(const uint32_t index)
  511. {
  512. qDebug("CarlaPluginClient::setProgram(%i)", index);
  513. CARLA_ASSERT(engine);
  514. CARLA_ASSERT(plugin);
  515. CARLA_ASSERT(index < plugin->programCount());
  516. if (! (plugin && engine))
  517. return;
  518. if (index >= plugin->programCount())
  519. return;
  520. plugin->setProgram(index, true, true, false, true);
  521. double value;
  522. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  523. {
  524. value = plugin->getParameterValue(i);
  525. engine->osc_send_bridge_set_parameter_value(i, value);
  526. engine->osc_send_bridge_set_default_value(i, value);
  527. }
  528. }
  529. void setMidiProgram(const uint32_t index)
  530. {
  531. qDebug("CarlaPluginClient::setMidiProgram(%i)", index);
  532. CARLA_ASSERT(engine);
  533. CARLA_ASSERT(plugin);
  534. if (! (plugin && engine))
  535. return;
  536. plugin->setMidiProgram(index, true, true, false, true);
  537. double value;
  538. for (uint32_t i=0; i < plugin->parameterCount(); i++)
  539. {
  540. value = plugin->getParameterValue(i);
  541. engine->osc_send_bridge_set_parameter_value(i, value);
  542. engine->osc_send_bridge_set_default_value(i, value);
  543. }
  544. }
  545. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  546. {
  547. qDebug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo);
  548. CARLA_ASSERT(plugin);
  549. CARLA_ASSERT(velo > 0);
  550. if (! plugin)
  551. return;
  552. //plugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  553. }
  554. void noteOff(const uint8_t channel, const uint8_t note)
  555. {
  556. qDebug("CarlaPluginClient::noteOff(%i, %i)", channel, note);
  557. CARLA_ASSERT(plugin);
  558. if (! plugin)
  559. return;
  560. //plugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  561. }
  562. // ---------------------------------------------------------------------
  563. // plugin
  564. void saveNow()
  565. {
  566. qDebug("CarlaPluginClient::saveNow()");
  567. CARLA_ASSERT(plugin);
  568. CARLA_ASSERT(engine);
  569. if (! (plugin && engine))
  570. return;
  571. plugin->prepareForSave();
  572. for (uint32_t i=0; i < plugin->customDataCount(); i++)
  573. {
  574. const CarlaBackend::CustomData& cdata = plugin->customData(i);
  575. engine->osc_send_bridge_set_custom_data(cdata.type, cdata.key, cdata.value);
  576. }
  577. if (plugin->hints() & CarlaBackend::PLUGIN_USES_CHUNKS)
  578. {
  579. void* data = nullptr;
  580. int32_t dataSize = plugin->chunkData(&data);
  581. if (data && dataSize >= 4)
  582. {
  583. QString filePath;
  584. filePath = QDir::tempPath();
  585. #ifdef Q_OS_WIN
  586. filePath += "\\.CarlaChunk_";
  587. #else
  588. filePath += "/.CarlaChunk_";
  589. #endif
  590. filePath += plugin->name();
  591. QFile file(filePath);
  592. if (file.open(QIODevice::WriteOnly))
  593. {
  594. QByteArray chunk((const char*)data, dataSize);
  595. file.write(chunk);
  596. file.close();
  597. engine->osc_send_bridge_set_chunk_data(filePath.toUtf8().constData());
  598. }
  599. }
  600. }
  601. //engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_SAVED, "");
  602. }
  603. void setCustomData(const char* const type, const char* const key, const char* const value)
  604. {
  605. qDebug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  606. CARLA_ASSERT(plugin);
  607. if (! plugin)
  608. return;
  609. plugin->setCustomData(type, key, value, true);
  610. }
  611. void setChunkData(const char* const filePath)
  612. {
  613. qDebug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  614. CARLA_ASSERT(plugin);
  615. if (! plugin)
  616. return;
  617. QString chunkFilePath(filePath);
  618. #ifdef Q_OS_WIN
  619. if (chunkFilePath.startsWith("/"))
  620. {
  621. // running under Wine, posix host
  622. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  623. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  624. }
  625. #endif
  626. QFile chunkFile(chunkFilePath);
  627. if (plugin && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  628. {
  629. QTextStream in(&chunkFile);
  630. QString stringData(in.readAll());
  631. chunkFile.close();
  632. chunkFile.remove();
  633. plugin->setChunkData(stringData.toUtf8().constData());
  634. }
  635. }
  636. // ---------------------------------------------------------------------
  637. // callback
  638. static void callback(void* ptr, CarlaBackend::CallbackType action, unsigned int, int value1, int value2, float value3, const char* valueStr)
  639. {
  640. CARLA_ASSERT(ptr);
  641. if (CarlaPluginClient* const _this_ = (CarlaPluginClient*)ptr)
  642. _this_->handleCallback(action, value1, value2, value3, valueStr);
  643. }
  644. // ---------------------------------------------------------------------
  645. protected:
  646. int msgTimerGUI;
  647. int msgTimerOSC;
  648. bool needsResize;
  649. int nextWidth;
  650. int nextHeight;
  651. CarlaBackend::CarlaEngine* engine;
  652. CarlaBackend::CarlaPlugin* plugin;
  653. std::set<int32_t> parametersToUpdate;
  654. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3, const char* const valueStr)
  655. {
  656. CARLA_BACKEND_USE_NAMESPACE
  657. qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g \"%s\")", CallbackType2Str(action), value1, value2, value3, valueStr);
  658. if (! engine)
  659. return;
  660. switch (action)
  661. {
  662. case CALLBACK_DEBUG:
  663. break;
  664. case CALLBACK_PLUGIN_ADDED:
  665. break;
  666. case CALLBACK_PLUGIN_REMOVED:
  667. break;
  668. case CALLBACK_PARAMETER_VALUE_CHANGED:
  669. parametersToUpdate.insert(value1);
  670. break;
  671. case CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  672. // todo, unused?
  673. break;
  674. case CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  675. // todo, unused?
  676. break;
  677. case CALLBACK_PROGRAM_CHANGED:
  678. engine->osc_send_bridge_set_program(value1);
  679. break;
  680. case CALLBACK_MIDI_PROGRAM_CHANGED:
  681. engine->osc_send_bridge_set_midi_program(value1);
  682. break;
  683. case CALLBACK_NOTE_ON:
  684. // todo
  685. break;
  686. case CALLBACK_NOTE_OFF:
  687. // todo
  688. break;
  689. case CALLBACK_SHOW_GUI:
  690. if (value1 == 0)
  691. {
  692. CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit;
  693. plugToolkit->guiClosedCallback();
  694. }
  695. break;
  696. case CALLBACK_UPDATE:
  697. // todo
  698. break;
  699. case CALLBACK_RELOAD_INFO:
  700. // todo
  701. break;
  702. case CALLBACK_RELOAD_PARAMETERS:
  703. // todo
  704. break;
  705. case CALLBACK_RELOAD_PROGRAMS:
  706. // todo
  707. break;
  708. case CALLBACK_RELOAD_ALL:
  709. // todo
  710. break;
  711. case CALLBACK_NSM_ANNOUNCE:
  712. case CALLBACK_NSM_OPEN1:
  713. case CALLBACK_NSM_OPEN2:
  714. case CALLBACK_NSM_SAVE:
  715. break;
  716. case CALLBACK_ERROR:
  717. break;
  718. case CALLBACK_QUIT:
  719. m_toolkit->quit();
  720. break;
  721. }
  722. }
  723. void timerEvent(QTimerEvent* const event)
  724. {
  725. if (qCloseNow)
  726. return toolkitQuit();
  727. if (qSaveNow)
  728. {
  729. // TODO
  730. qSaveNow = false;
  731. }
  732. if (event->timerId() == msgTimerGUI)
  733. {
  734. if (plugin)
  735. plugin->idleGui();
  736. }
  737. else if (event->timerId() == msgTimerOSC)
  738. {
  739. if (isOscControlRegistered())
  740. oscIdle();
  741. }
  742. QObject::timerEvent(event);
  743. }
  744. };
  745. // -------------------------------------------------------------------------
  746. void CarlaBridgeToolkitPlugin::show()
  747. {
  748. qDebug("CarlaBridgeToolkitPlugin::show()");
  749. #if 0
  750. CARLA_ASSERT(gui);
  751. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  752. plugClient->showPluginGui(true);
  753. if (gui && m_uiShow)
  754. gui->setVisible(true);
  755. #endif
  756. }
  757. void CarlaBridgeToolkitPlugin::hide()
  758. {
  759. qDebug("CarlaBridgeToolkitPlugin::hide()");
  760. #if 0
  761. CARLA_ASSERT(gui);
  762. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  763. if (gui && m_uiShow)
  764. gui->setVisible(false);
  765. plugClient->showPluginGui(false);
  766. #endif
  767. }
  768. void CarlaBridgeToolkitPlugin::guiClosedCallback()
  769. {
  770. qDebug("CarlaBridgeToolkitPlugin::guiClosedCallback()");
  771. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  772. if (m_uiQuit)
  773. {
  774. plugClient->quit();
  775. quit();
  776. }
  777. else
  778. {
  779. plugClient->guiClosed();
  780. }
  781. }
  782. // -------------------------------------------------------------------------
  783. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  784. {
  785. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  786. if (! client)
  787. return 1;
  788. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  789. plugClient->saveNow();
  790. return 0;
  791. }
  792. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  793. {
  794. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  795. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  796. if (! client)
  797. return 1;
  798. const char* const chunkFile = (const char*)&argv[0]->s;
  799. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  800. plugClient->setChunkData(chunkFile);
  801. return 0;
  802. }
  803. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  804. {
  805. qDebug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  806. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  807. if (! client)
  808. return 1;
  809. const char* const type = (const char*)&argv[0]->s;
  810. const char* const key = (const char*)&argv[1]->s;
  811. const char* const value = (const char*)&argv[2]->s;
  812. CarlaPluginClient* const plugClient = (CarlaPluginClient*)client;
  813. plugClient->setCustomData(type, key, value);
  814. return 0;
  815. }
  816. // -------------------------------------------------------------------------
  817. CARLA_BRIDGE_END_NAMESPACE
  818. int main(int argc, char* argv[])
  819. {
  820. CARLA_BRIDGE_USE_NAMESPACE
  821. if (argc != 6)
  822. {
  823. qWarning("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  824. return 1;
  825. }
  826. const char* const oscUrl = argv[1];
  827. const char* const stype = argv[2];
  828. const char* const filename = argv[3];
  829. const char* name = argv[4];
  830. const char* const label = argv[5];
  831. const bool useOsc = strcmp(oscUrl, "null");
  832. if (strcmp(name, "(none)") == 0)
  833. name = nullptr;
  834. CarlaBackend::PluginType itype;
  835. if (strcmp(stype, "LADSPA") == 0)
  836. itype = CarlaBackend::PLUGIN_LADSPA;
  837. else if (strcmp(stype, "DSSI") == 0)
  838. itype = CarlaBackend::PLUGIN_DSSI;
  839. else if (strcmp(stype, "LV2") == 0)
  840. itype = CarlaBackend::PLUGIN_LV2;
  841. else if (strcmp(stype, "VST") == 0)
  842. itype = CarlaBackend::PLUGIN_VST;
  843. else
  844. {
  845. qWarning("Invalid plugin type '%s'", stype);
  846. return 1;
  847. }
  848. // Init Plugin client
  849. CarlaPluginClient client;
  850. // Init OSC
  851. if (useOsc && ! client.oscInit(oscUrl))
  852. {
  853. return 1;
  854. }
  855. // Listen for ctrl+c or sigint/sigterm events
  856. initSignalHandler();
  857. // Init backend engine
  858. CarlaBackend::CarlaEngine* engine = CarlaBackend::CarlaEngine::newDriverByName("JACK");
  859. engine->setCallback(client.callback, &client);
  860. client.setEngine(engine);
  861. // Init engine
  862. CarlaString engName(name ? name : label);
  863. engName += " (master)";
  864. engName.toBasic();
  865. engName.truncate(engine->maxClientNameSize());
  866. if (! engine->init((const char*)engName))
  867. {
  868. if (const char* const lastError = engine->getLastError())
  869. {
  870. qWarning("Bridge engine failed to start, error was:\n%s", lastError);
  871. client.sendOscBridgeError(lastError);
  872. }
  873. engine->close();
  874. delete engine;
  875. return 2;
  876. }
  877. void* extraStuff = nullptr;
  878. //if (itype == CarlaBackend::PLUGIN_DSSI)
  879. //extraStuff = findDSSIGUI(filename, name, label);
  880. // Init plugin
  881. int ret;
  882. if (engine->addPlugin(itype, filename, name, label, extraStuff))
  883. {
  884. CarlaBackend::CarlaPlugin* const plugin = engine->getPlugin(0);
  885. client.setPlugin(plugin);
  886. if (! useOsc)
  887. plugin->setActive(true, false, false);
  888. client.init();
  889. client.toolkitExec(!useOsc);
  890. client.quit();
  891. ret = 0;
  892. }
  893. else
  894. {
  895. const char* const lastError = engine->getLastError();
  896. qWarning("Plugin failed to load, error was:\n%s", lastError);
  897. if (useOsc)
  898. client.sendOscBridgeError(lastError);
  899. ret = 1;
  900. }
  901. if (extraStuff && itype == CarlaBackend::PLUGIN_DSSI)
  902. free((char*)extraStuff);
  903. engine->setAboutToClose();
  904. engine->removeAllPlugins();
  905. engine->close();
  906. delete engine;
  907. // Close OSC
  908. if (useOsc)
  909. {
  910. client.oscClose();
  911. }
  912. return ret;
  913. }
  914. #endif // BRIDGE_PLUGIN