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.

565 lines
14KB

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