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.

592 lines
15KB

  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. #include "CarlaBridgeClient.hpp"
  18. #include "CarlaBridgeToolkit.hpp"
  19. #include "CarlaStandalone.hpp"
  20. #include "CarlaEngine.hpp"
  21. #include "CarlaPlugin.hpp"
  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 CARLA_OS_UNIX
  31. # include <signal.h>
  32. #endif
  33. // -------------------------------------------------------------------------
  34. static bool gCloseNow = false;
  35. static bool gSaveNow = false;
  36. #ifdef CARLA_OS_WIN
  37. BOOL WINAPI closeSignalHandler(DWORD dwCtrlType)
  38. {
  39. if (dwCtrlType == CTRL_C_EVENT)
  40. {
  41. gCloseNow = true;
  42. return TRUE;
  43. }
  44. return FALSE;
  45. }
  46. #else
  47. void closeSignalHandler(int)
  48. {
  49. gCloseNow = true;
  50. }
  51. void saveSignalHandler(int)
  52. {
  53. gSaveNow = true;
  54. }
  55. #endif
  56. void initSignalHandler()
  57. {
  58. #ifdef CARLA_OS_WIN
  59. SetConsoleCtrlHandler(closeSignalHandler, TRUE);
  60. #elif defined(CARLA_OS_LINUX) || defined(CARLA_OS_HAIKU)
  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. #endif
  80. }
  81. // -------------------------------------------------------------------------
  82. // Helpers
  83. extern CarlaBackend::CarlaEngine* carla_get_standalone_engine();
  84. CARLA_BACKEND_START_NAMESPACE
  85. extern const char* findDSSIGUI(const char* const filename, const char* const label);
  86. CARLA_BACKEND_END_NAMESPACE
  87. CARLA_BRIDGE_START_NAMESPACE
  88. #if 0
  89. } // Fix editor indentation
  90. #endif
  91. // -------------------------------------------------------------------------
  92. class CarlaPluginClient : public CarlaBridgeClient,
  93. public QObject
  94. {
  95. public:
  96. CarlaPluginClient(const bool useBridge, const char* const driverName, const char* audioBaseName, const char* controlBaseName)
  97. : CarlaBridgeClient(nullptr),
  98. QObject(nullptr),
  99. fEngine(nullptr),
  100. fPlugin(nullptr),
  101. fTimerId(0)
  102. {
  103. CARLA_ASSERT(driverName != nullptr);
  104. carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s)", bool2str(useBridge), driverName, audioBaseName, controlBaseName);
  105. if (useBridge)
  106. carla_engine_init_bridge(audioBaseName, controlBaseName, driverName);
  107. else
  108. carla_engine_init("JACK", driverName);
  109. carla_set_engine_callback(callback, this);
  110. }
  111. ~CarlaPluginClient()
  112. {
  113. CARLA_ASSERT(fTimerId == 0);
  114. carla_debug("CarlaPluginClient::~CarlaPluginClient()");
  115. carla_set_engine_about_to_close();
  116. carla_engine_close();
  117. }
  118. void oscInit(const char* const url)
  119. {
  120. CarlaBridgeClient::oscInit(url);
  121. fEngine = carla_get_standalone_engine();
  122. fEngine->setOscBridgeData(fOscData);
  123. }
  124. void ready()
  125. {
  126. CARLA_ASSERT(fTimerId == 0);
  127. fEngine = carla_get_standalone_engine();
  128. fPlugin = fEngine->getPlugin(0);
  129. fTimerId = startTimer(50);
  130. }
  131. void idle()
  132. {
  133. if (fEngine != nullptr)
  134. fEngine->idle();
  135. CarlaBridgeClient::oscIdle();
  136. if (gSaveNow)
  137. {
  138. // TODO
  139. gSaveNow = false;
  140. }
  141. if (gCloseNow)
  142. {
  143. if (fTimerId != 0)
  144. {
  145. killTimer(fTimerId);
  146. fTimerId = 0;
  147. }
  148. if (QApplication* const app = qApp)
  149. {
  150. if (! app->closingDown())
  151. app->quit();
  152. }
  153. }
  154. }
  155. void exec()
  156. {
  157. while (! gCloseNow)
  158. {
  159. idle();
  160. carla_msleep(50);
  161. }
  162. }
  163. // ---------------------------------------------------------------------
  164. // plugin management
  165. void saveNow()
  166. {
  167. qDebug("CarlaPluginClient::saveNow()");
  168. CARLA_ASSERT(fEngine != nullptr);
  169. CARLA_ASSERT(fPlugin != nullptr);
  170. if (fPlugin == nullptr || fEngine == nullptr)
  171. return;
  172. fPlugin->prepareForSave();
  173. for (uint32_t i=0; i < fPlugin->customDataCount(); i++)
  174. {
  175. const CarlaBackend::CustomData& cdata = fPlugin->customData(i);
  176. fEngine->osc_send_bridge_set_custom_data(cdata.type, cdata.key, cdata.value);
  177. }
  178. if (fPlugin->options() & CarlaBackend::PLUGIN_OPTION_USE_CHUNKS)
  179. {
  180. void* data = nullptr;
  181. int32_t dataSize = fPlugin->chunkData(&data);
  182. if (data && dataSize >= 4)
  183. {
  184. QString filePath;
  185. filePath = QDir::tempPath();
  186. #ifdef Q_OS_WIN
  187. filePath += "\\.CarlaChunk_";
  188. #else
  189. filePath += "/.CarlaChunk_";
  190. #endif
  191. filePath += fPlugin->name();
  192. QFile file(filePath);
  193. if (file.open(QIODevice::WriteOnly))
  194. {
  195. QByteArray chunk((const char*)data, dataSize);
  196. file.write(chunk);
  197. file.close();
  198. fEngine->osc_send_bridge_set_chunk_data(filePath.toUtf8().constData());
  199. }
  200. }
  201. }
  202. //engine->osc_send_bridge_configure(CarlaBackend::CARLA_BRIDGE_MSG_SAVED, "");
  203. }
  204. void setCustomData(const char* const type, const char* const key, const char* const value)
  205. {
  206. carla_debug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  207. CARLA_ASSERT(fPlugin != nullptr);
  208. if (fPlugin != nullptr)
  209. fPlugin->setCustomData(type, key, value, true);
  210. }
  211. void setChunkData(const char* const filePath)
  212. {
  213. carla_debug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  214. CARLA_ASSERT(fPlugin != nullptr);
  215. if (fPlugin == nullptr)
  216. return;
  217. QString chunkFilePath(filePath);
  218. #ifdef CARLA_OS_WIN
  219. if (chunkFilePath.startsWith("/"))
  220. {
  221. // running under Wine, posix host
  222. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  223. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  224. }
  225. #endif
  226. QFile chunkFile(chunkFilePath);
  227. if (fPlugin != nullptr && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  228. {
  229. QTextStream in(&chunkFile);
  230. QString stringData(in.readAll());
  231. chunkFile.close();
  232. chunkFile.remove();
  233. fPlugin->setChunkData(stringData.toUtf8().constData());
  234. }
  235. }
  236. // ---------------------------------------------------------------------
  237. // processing
  238. void setParameter(const int32_t rindex, const float value)
  239. {
  240. carla_debug("CarlaPluginClient::setParameter(%i, %f)", rindex, value);
  241. CARLA_ASSERT(fPlugin != nullptr);
  242. if (fPlugin != nullptr)
  243. fPlugin->setParameterValueByRealIndex(rindex, value, true, true, false);
  244. }
  245. void setProgram(const uint32_t index)
  246. {
  247. carla_debug("CarlaPluginClient::setProgram(%i)", index);
  248. CARLA_ASSERT(fPlugin != nullptr);
  249. if (fPlugin != nullptr)
  250. fPlugin->setProgram(index, true, true, false);
  251. }
  252. void setMidiProgram(const uint32_t index)
  253. {
  254. carla_debug("CarlaPluginClient::setMidiProgram(%i)", index);
  255. CARLA_ASSERT(fPlugin != nullptr);
  256. if (fPlugin != nullptr)
  257. fPlugin->setMidiProgram(index, true, true, false);
  258. }
  259. void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo)
  260. {
  261. carla_debug("CarlaPluginClient::noteOn(%i, %i, %i)", channel, note, velo);
  262. CARLA_ASSERT(fPlugin != nullptr);
  263. CARLA_ASSERT(velo > 0);
  264. if (fPlugin != nullptr)
  265. fPlugin->sendMidiSingleNote(channel, note, velo, true, true, false);
  266. }
  267. void noteOff(const uint8_t channel, const uint8_t note)
  268. {
  269. carla_debug("CarlaPluginClient::noteOff(%i, %i)", channel, note);
  270. CARLA_ASSERT(fPlugin != nullptr);
  271. if (fPlugin != nullptr)
  272. fPlugin->sendMidiSingleNote(channel, note, 0, true, true, false);
  273. }
  274. protected:
  275. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const float value3, const char* const valueStr)
  276. {
  277. CARLA_BACKEND_USE_NAMESPACE;
  278. // TODO
  279. switch (action)
  280. {
  281. case CALLBACK_SHOW_GUI:
  282. if (value1 != 1 && ! isOscControlRegistered())
  283. gCloseNow = true;
  284. break;
  285. default:
  286. break;
  287. }
  288. return;
  289. (void)value2;
  290. (void)value3;
  291. (void)valueStr;
  292. }
  293. private:
  294. CarlaBackend::CarlaEngine* fEngine;
  295. CarlaBackend::CarlaPlugin* fPlugin;
  296. int fTimerId;
  297. void timerEvent(QTimerEvent* const event)
  298. {
  299. if (event->timerId() == fTimerId)
  300. idle();
  301. QObject::timerEvent(event);
  302. }
  303. static void callback(void* ptr, CarlaBackend::CallbackType action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr)
  304. {
  305. return ((CarlaPluginClient*)ptr)->handleCallback(action, value1, value2, value3, valueStr);
  306. // unused
  307. (void)pluginId;
  308. }
  309. };
  310. // -------------------------------------------------------------------------
  311. int CarlaBridgeOsc::handleMsgShow()
  312. {
  313. carla_debug("CarlaBridgeOsc::handleMsgShow()");
  314. CARLA_ASSERT(kClient != nullptr);
  315. if (kClient == nullptr)
  316. return 1;
  317. carla_show_gui(0, true);
  318. return 0;
  319. }
  320. int CarlaBridgeOsc::handleMsgHide()
  321. {
  322. carla_debug("CarlaBridgeOsc::handleMsgHide()");
  323. CARLA_ASSERT(kClient != nullptr);
  324. if (kClient == nullptr)
  325. return 1;
  326. carla_show_gui(0, false);
  327. return 0;
  328. }
  329. int CarlaBridgeOsc::handleMsgQuit()
  330. {
  331. carla_debug("CarlaBridgeOsc::handleMsgQuit()");
  332. CARLA_ASSERT(kClient != nullptr);
  333. gCloseNow = true;
  334. return 0;
  335. }
  336. // -------------------------------------------------------------------------
  337. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  338. {
  339. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  340. CARLA_ASSERT(kClient != nullptr);
  341. if (kClient == nullptr)
  342. return 1;
  343. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  344. plugClient->saveNow();
  345. return 0;
  346. }
  347. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  348. {
  349. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  350. CARLA_ASSERT(kClient != nullptr);
  351. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  352. if (kClient == nullptr)
  353. return 1;
  354. const char* const chunkFile = (const char*)&argv[0]->s;
  355. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  356. plugClient->setChunkData(chunkFile);
  357. return 0;
  358. }
  359. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  360. {
  361. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  362. CARLA_ASSERT(kClient != nullptr);
  363. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  364. if (kClient == nullptr)
  365. return 1;
  366. const char* const type = (const char*)&argv[0]->s;
  367. const char* const key = (const char*)&argv[1]->s;
  368. const char* const value = (const char*)&argv[2]->s;
  369. CarlaPluginClient* const plugClient = (CarlaPluginClient*)kClient;
  370. plugClient->setCustomData(type, key, value);
  371. return 0;
  372. }
  373. // -------------------------------------------------------------------------
  374. CARLA_BRIDGE_END_NAMESPACE
  375. int main(int argc, char* argv[])
  376. {
  377. CARLA_BRIDGE_USE_NAMESPACE
  378. if (argc != 6 && argc != 7)
  379. {
  380. carla_stdout("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  381. return 1;
  382. }
  383. const char* const oscUrl = argv[1];
  384. const char* const stype = argv[2];
  385. const char* const filename = argv[3];
  386. const char* name = argv[4];
  387. const char* const label = argv[5];
  388. const bool useBridge = (argc == 7);
  389. const bool useOsc = std::strcmp(oscUrl, "null");
  390. if (std::strcmp(name, "(none)") == 0)
  391. name = nullptr;
  392. char bridgeBaseAudioName[6+1] = { 0 };
  393. char bridgeBaseControlName[6+1] = { 0 };
  394. if (useBridge)
  395. {
  396. std::strncpy(bridgeBaseAudioName, argv[6], 6);
  397. std::strncpy(bridgeBaseControlName, argv[6]+6, 6);
  398. }
  399. CarlaBackend::PluginType itype;
  400. if (std::strcmp(stype, "LADSPA") == 0)
  401. itype = CarlaBackend::PLUGIN_LADSPA;
  402. else if (std::strcmp(stype, "DSSI") == 0)
  403. itype = CarlaBackend::PLUGIN_DSSI;
  404. else if (std::strcmp(stype, "LV2") == 0)
  405. itype = CarlaBackend::PLUGIN_LV2;
  406. else if (std::strcmp(stype, "VST") == 0)
  407. itype = CarlaBackend::PLUGIN_VST;
  408. else if (std::strcmp(stype, "VST3") == 0)
  409. itype = CarlaBackend::PLUGIN_VST3;
  410. else
  411. {
  412. carla_stderr("Invalid plugin type '%s'", stype);
  413. return 1;
  414. }
  415. QApplication app(argc, argv, true);
  416. app.setQuitOnLastWindowClosed(false);
  417. // Init Plugin client
  418. CarlaPluginClient client(useBridge, (name != nullptr) ? name : label, bridgeBaseAudioName, bridgeBaseControlName);
  419. // Init OSC
  420. if (useOsc)
  421. client.oscInit(oscUrl);
  422. // Listen for ctrl+c or sigint/sigterm events
  423. initSignalHandler();
  424. const void* extraStuff = nullptr;
  425. if (itype == CarlaBackend::PLUGIN_DSSI)
  426. extraStuff = CarlaBackend::findDSSIGUI(filename, label);
  427. // Init plugin
  428. int ret;
  429. if (carla_add_plugin(CarlaBackend::BINARY_NATIVE, itype, filename, name, label, extraStuff))
  430. {
  431. if (useOsc)
  432. {
  433. client.sendOscUpdate();
  434. client.sendOscBridgeUpdate();
  435. // TESTING!!
  436. carla_set_active(0, true);
  437. carla_show_gui(0, true);
  438. }
  439. else
  440. {
  441. carla_set_active(0, true);
  442. carla_show_gui(0, true);
  443. }
  444. client.ready();
  445. ret = app.exec();
  446. carla_remove_plugin(0);
  447. }
  448. else
  449. {
  450. const char* const lastError = carla_get_last_error();
  451. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  452. if (useOsc)
  453. client.sendOscBridgeError(lastError);
  454. ret = 1;
  455. }
  456. if (extraStuff != nullptr && itype == CarlaBackend::PLUGIN_DSSI)
  457. delete[] (const char*)extraStuff;
  458. // Close OSC
  459. if (useOsc)
  460. client.oscClose();
  461. return ret;
  462. }