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.

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