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.

621 lines
18KB

  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 doc/GPL.txt file.
  16. */
  17. #include "CarlaBridgeClient.hpp"
  18. #include "CarlaEngine.hpp"
  19. #include "CarlaPlugin.hpp"
  20. #include "CarlaHost.hpp"
  21. #include "CarlaBackendUtils.hpp"
  22. #include "CarlaBridgeUtils.hpp"
  23. #include "juce_core.h"
  24. #ifdef CARLA_OS_UNIX
  25. # include <signal.h>
  26. #endif
  27. using juce::File;
  28. // -------------------------------------------------------------------------
  29. static volatile bool gCloseNow = false;
  30. static volatile bool gSaveNow = false;
  31. #ifdef CARLA_OS_WIN
  32. BOOL WINAPI winSignalHandler(DWORD dwCtrlType)
  33. {
  34. if (dwCtrlType == CTRL_C_EVENT)
  35. {
  36. gCloseNow = true;
  37. return TRUE;
  38. }
  39. return FALSE;
  40. }
  41. #else
  42. static void closeSignalHandler(int)
  43. {
  44. gCloseNow = true;
  45. }
  46. static void saveSignalHandler(int)
  47. {
  48. gSaveNow = true;
  49. }
  50. #endif
  51. void initSignalHandler()
  52. {
  53. #ifdef CARLA_OS_WIN
  54. SetConsoleCtrlHandler(winSignalHandler, TRUE);
  55. #elif defined(CARLA_OS_LINUX) || defined(CARLA_OS_HAIKU)
  56. struct sigaction sint;
  57. struct sigaction sterm;
  58. struct sigaction susr1;
  59. sint.sa_handler = closeSignalHandler;
  60. sint.sa_flags = SA_RESTART;
  61. sint.sa_restorer = nullptr;
  62. sigemptyset(&sint.sa_mask);
  63. sigaction(SIGINT, &sint, nullptr);
  64. sterm.sa_handler = closeSignalHandler;
  65. sterm.sa_flags = SA_RESTART;
  66. sterm.sa_restorer = nullptr;
  67. sigemptyset(&sterm.sa_mask);
  68. sigaction(SIGTERM, &sterm, nullptr);
  69. susr1.sa_handler = saveSignalHandler;
  70. susr1.sa_flags = SA_RESTART;
  71. susr1.sa_restorer = nullptr;
  72. sigemptyset(&susr1.sa_mask);
  73. sigaction(SIGUSR1, &susr1, nullptr);
  74. #endif
  75. }
  76. // -------------------------------------------------------------------------
  77. CARLA_BRIDGE_START_NAMESPACE
  78. #if 0
  79. } // Fix editor indentation
  80. #endif
  81. // -------------------------------------------------------------------------
  82. class CarlaPluginClient : public CarlaBridgeClient
  83. {
  84. public:
  85. CarlaPluginClient(const bool useBridge, const char* const driverName, const char* audioBaseName, const char* controlBaseName)
  86. : CarlaBridgeClient(nullptr),
  87. fEngine(nullptr),
  88. fPlugin(nullptr)
  89. {
  90. CARLA_ASSERT(driverName != nullptr && driverName[0] != '\0');
  91. carla_debug("CarlaPluginClient::CarlaPluginClient(%s, \"%s\", %s, %s)", bool2str(useBridge), driverName, audioBaseName, controlBaseName);
  92. carla_set_engine_callback(callback, this);
  93. File curDir(File::getSpecialLocation(File::currentApplicationFile).getParentDirectory());
  94. if (curDir.getChildFile("resources").exists())
  95. carla_set_engine_option(CarlaBackend::OPTION_PATH_RESOURCES, 0, curDir.getChildFile("resources").getFullPathName().toRawUTF8());
  96. else if (curDir.getChildFile("../../modules/carla_native/resources").exists())
  97. carla_set_engine_option(CarlaBackend::OPTION_PATH_RESOURCES, 0, curDir.getChildFile("../../modules/carla_native/resources").getFullPathName().toRawUTF8());
  98. else
  99. carla_set_engine_option(CarlaBackend::OPTION_PATH_RESOURCES, 0, curDir.getChildFile("../modules/carla_native/resources").getFullPathName().toRawUTF8());
  100. if (useBridge)
  101. carla_engine_init_bridge(audioBaseName, controlBaseName, driverName);
  102. else
  103. carla_engine_init("JACK", driverName);
  104. fEngine = carla_get_standalone_engine();
  105. }
  106. ~CarlaPluginClient() override
  107. {
  108. carla_debug("CarlaPluginClient::~CarlaPluginClient()");
  109. carla_engine_close();
  110. }
  111. bool isOk() const noexcept
  112. {
  113. return (fEngine != nullptr);
  114. }
  115. void oscInit(const char* const url)
  116. {
  117. CarlaBridgeClient::oscInit(url);
  118. fEngine->setOscBridgeData(&fOscData);
  119. }
  120. void ready(const bool doSaveLoad)
  121. {
  122. fPlugin = fEngine->getPlugin(0);
  123. if (doSaveLoad)
  124. {
  125. fProjFileName = fPlugin->getName();
  126. fProjFileName += ".carxs";
  127. if (! File::isAbsolutePath((const char*)fProjFileName))
  128. fProjFileName = File::getCurrentWorkingDirectory().getChildFile((const char*)fProjFileName).getFullPathName().toRawUTF8();
  129. if (! fPlugin->loadStateFromFile(fProjFileName))
  130. carla_stderr("Plugin preset load failed, error was:\n%s", fEngine->getLastError());
  131. }
  132. }
  133. void idle()
  134. {
  135. CARLA_SAFE_ASSERT_RETURN(fEngine != nullptr,);
  136. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  137. carla_engine_idle();
  138. CarlaBridgeClient::oscIdle();
  139. if (gSaveNow)
  140. {
  141. gSaveNow = false;
  142. if (fProjFileName.isNotEmpty())
  143. {
  144. if (! fPlugin->saveStateToFile(fProjFileName))
  145. carla_stderr("Plugin preset save failed, error was:\n%s", fEngine->getLastError());
  146. }
  147. }
  148. if (gCloseNow)
  149. {
  150. //gCloseNow = false;
  151. // close something?
  152. }
  153. }
  154. void exec()
  155. {
  156. while (! gCloseNow)
  157. {
  158. idle();
  159. carla_msleep(30);
  160. }
  161. }
  162. // ---------------------------------------------------------------------
  163. // plugin management
  164. void saveNow()
  165. {
  166. CARLA_SAFE_ASSERT_RETURN(fEngine != nullptr,);
  167. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  168. carla_debug("CarlaPluginClient::saveNow()");
  169. fPlugin->prepareForSave();
  170. for (uint32_t i=0; i < fPlugin->getCustomDataCount(); ++i)
  171. {
  172. const CarlaBackend::CustomData& cdata(fPlugin->getCustomData(i));
  173. fEngine->oscSend_bridge_set_custom_data(cdata.type, cdata.key, cdata.value);
  174. }
  175. if (fPlugin->getOptions() & CarlaBackend::PLUGIN_OPTION_USE_CHUNKS)
  176. {
  177. void* data = nullptr;
  178. int32_t dataSize = fPlugin->getChunkData(&data);
  179. if (data && dataSize >= 4)
  180. {
  181. #if 0
  182. QString filePath;
  183. filePath = QDir::tempPath();
  184. #ifdef Q_OS_WIN
  185. filePath += "\\.CarlaChunk_";
  186. #else
  187. filePath += "/.CarlaChunk_";
  188. #endif
  189. filePath += fPlugin->getName();
  190. QFile file(filePath);
  191. if (file.open(QIODevice::WriteOnly))
  192. {
  193. QByteArray chunk((const char*)data, dataSize);
  194. file.write(chunk);
  195. file.close();
  196. fEngine->oscSend_bridge_set_chunk_data(filePath.toUtf8().constData());
  197. }
  198. #endif
  199. }
  200. }
  201. fEngine->oscSend_bridge_configure(CARLA_BRIDGE_MSG_SAVED, "");
  202. }
  203. void setParameterMidiChannel(const int32_t index, const int32_t channel)
  204. {
  205. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  206. carla_debug("CarlaPluginClient::setParameterMidiChannel(%i, %i)", index, channel);
  207. fPlugin->setParameterMidiChannel(index, channel, false, false);
  208. }
  209. void setParameterMidiCC(const int32_t index, const int32_t cc)
  210. {
  211. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  212. carla_debug("CarlaPluginClient::setParameterMidiCC(%i, %i)", index, cc);
  213. fPlugin->setParameterMidiCC(index, cc, false, false);
  214. }
  215. void setCustomData(const char* const type, const char* const key, const char* const value)
  216. {
  217. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  218. carla_debug("CarlaPluginClient::setCustomData(\"%s\", \"%s\", \"%s\")", type, key, value);
  219. fPlugin->setCustomData(type, key, value, true);
  220. }
  221. void setChunkData(const char* const filePath)
  222. {
  223. CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
  224. carla_debug("CarlaPluginClient::setChunkData(\"%s\")", filePath);
  225. #if 0
  226. QString chunkFilePath(filePath);
  227. #ifdef CARLA_OS_WIN
  228. if (chunkFilePath.startsWith("/"))
  229. {
  230. // running under Wine, posix host
  231. chunkFilePath = chunkFilePath.replace(0, 1, "Z:/");
  232. chunkFilePath = QDir::toNativeSeparators(chunkFilePath);
  233. }
  234. #endif
  235. QFile chunkFile(chunkFilePath);
  236. if (fPlugin != nullptr && chunkFile.open(QIODevice::ReadOnly | QIODevice::Text))
  237. {
  238. QTextStream in(&chunkFile);
  239. QString stringData(in.readAll());
  240. chunkFile.close();
  241. chunkFile.remove();
  242. fPlugin->setChunkData(stringData.toUtf8().constData());
  243. }
  244. #endif
  245. }
  246. // ---------------------------------------------------------------------
  247. protected:
  248. void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const float value3, const char* const valueStr)
  249. {
  250. CARLA_BACKEND_USE_NAMESPACE;
  251. // TODO
  252. switch (action)
  253. {
  254. case CALLBACK_PARAMETER_VALUE_CHANGED:
  255. if (isOscControlRegistered())
  256. sendOscControl(value1, value3);
  257. break;
  258. case CALLBACK_SHOW_GUI:
  259. if (! isOscControlRegistered())
  260. {
  261. if (value1 != 1)
  262. gCloseNow = true;
  263. }
  264. else
  265. {
  266. // show-gui button
  267. fEngine->oscSend_bridge_configure(CARLA_BRIDGE_MSG_HIDE_GUI, "");
  268. }
  269. break;
  270. default:
  271. break;
  272. }
  273. return;
  274. (void)value2;
  275. (void)value3;
  276. (void)valueStr;
  277. }
  278. private:
  279. CarlaBackend::CarlaEngine* fEngine;
  280. CarlaBackend::CarlaPlugin* fPlugin;
  281. CarlaString fProjFileName;
  282. static void callback(void* ptr, CarlaBackend::CallbackType action, unsigned int pluginId, int value1, int value2, float value3, const char* valueStr)
  283. {
  284. carla_debug("CarlaPluginClient::callback(%p, %i:%s, %i, %i, %i, %f, \"%s\")", ptr, action, CarlaBackend::CallbackType2Str(action), pluginId, value1, value2, value3, valueStr);
  285. CARLA_SAFE_ASSERT_RETURN(ptr != nullptr,);
  286. CARLA_SAFE_ASSERT_RETURN(pluginId == 0,);
  287. return ((CarlaPluginClient*)ptr)->handleCallback(action, value1, value2, value3, valueStr);
  288. }
  289. };
  290. // -------------------------------------------------------------------------
  291. int CarlaBridgeOsc::handleMsgShow()
  292. {
  293. carla_debug("CarlaBridgeOsc::handleMsgShow()");
  294. carla_show_gui(0, true);
  295. return 0;
  296. }
  297. int CarlaBridgeOsc::handleMsgHide()
  298. {
  299. carla_debug("CarlaBridgeOsc::handleMsgHide()");
  300. carla_show_gui(0, false);
  301. return 0;
  302. }
  303. int CarlaBridgeOsc::handleMsgQuit()
  304. {
  305. carla_debug("CarlaBridgeOsc::handleMsgQuit()");
  306. gCloseNow = true;
  307. return 0;
  308. }
  309. // -------------------------------------------------------------------------
  310. int CarlaBridgeOsc::handleMsgPluginSaveNow()
  311. {
  312. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  313. carla_debug("CarlaBridgeOsc::handleMsgPluginSaveNow()");
  314. CarlaPluginClient* const plugClient((CarlaPluginClient*)fClient);
  315. plugClient->saveNow();
  316. return 0;
  317. }
  318. int CarlaBridgeOsc::handleMsgPluginSetParameterMidiChannel(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  319. {
  320. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii");
  321. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  322. carla_debug("CarlaBridgeOsc::handleMsgPluginSetParameterMidiChannel()");
  323. const int32_t index = argv[0]->i;
  324. const int32_t channel = argv[1]->i;
  325. CarlaPluginClient* const plugClient((CarlaPluginClient*)fClient);
  326. plugClient->setParameterMidiChannel(index, channel);
  327. return 0;
  328. }
  329. int CarlaBridgeOsc::handleMsgPluginSetParameterMidiCC(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  330. {
  331. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii");
  332. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  333. carla_debug("CarlaBridgeOsc::handleMsgPluginSetParameterMidiCC()");
  334. const int32_t index = argv[0]->i;
  335. const int32_t cc = argv[1]->i;
  336. CarlaPluginClient* const plugClient((CarlaPluginClient*)fClient);
  337. plugClient->setParameterMidiCC(index, cc);
  338. return 0;
  339. }
  340. int CarlaBridgeOsc::handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  341. {
  342. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "s");
  343. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  344. carla_debug("CarlaBridgeOsc::handleMsgPluginSetChunk()");
  345. const char* const chunkFile = (const char*)&argv[0]->s;
  346. CarlaPluginClient* const plugClient((CarlaPluginClient*)fClient);
  347. plugClient->setChunkData(chunkFile);
  348. return 0;
  349. }
  350. int CarlaBridgeOsc::handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS)
  351. {
  352. CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(3, "sss");
  353. CARLA_SAFE_ASSERT_RETURN(fClient != nullptr, 1);
  354. carla_debug("CarlaBridgeOsc::handleMsgPluginSetCustomData()");
  355. const char* const type = (const char*)&argv[0]->s;
  356. const char* const key = (const char*)&argv[1]->s;
  357. const char* const value = (const char*)&argv[2]->s;
  358. CarlaPluginClient* const plugClient((CarlaPluginClient*)fClient);
  359. plugClient->setCustomData(type, key, value);
  360. return 0;
  361. }
  362. // -------------------------------------------------------------------------
  363. CARLA_BRIDGE_END_NAMESPACE
  364. // -------------------------------------------------------------------------
  365. int main(int argc, char* argv[])
  366. {
  367. CARLA_BRIDGE_USE_NAMESPACE;
  368. // ---------------------------------------------------------------------
  369. // Check argument count
  370. if (argc != 6 && argc != 7)
  371. {
  372. carla_stdout("usage: %s <osc-url|\"null\"> <type> <filename> <name|\"(none)\"> <label>", argv[0]);
  373. return 1;
  374. }
  375. // ---------------------------------------------------------------------
  376. // Get args
  377. const char* const oscUrl = argv[1];
  378. const char* const stype = argv[2];
  379. const char* const filename = argv[3];
  380. const char* name = argv[4];
  381. const char* label = argv[5];
  382. // ---------------------------------------------------------------------
  383. // Setup args
  384. const bool useBridge = (argc == 7);
  385. const bool useOsc = (std::strcmp(oscUrl, "null") != 0 && std::strcmp(oscUrl, "(null)") != 0 && std::strcmp(oscUrl, "NULL") != 0);
  386. if (std::strcmp(name, "(none)") == 0)
  387. name = nullptr;
  388. if (std::strlen(label) == 0)
  389. label = nullptr;
  390. char bridgeBaseAudioName[6+1];
  391. char bridgeBaseControlName[6+1];
  392. if (useBridge)
  393. {
  394. CARLA_SAFE_ASSERT_RETURN(std::strlen(argv[6]) == 6*2, 1);
  395. std::strncpy(bridgeBaseAudioName, argv[6], 6);
  396. std::strncpy(bridgeBaseControlName, argv[6]+6, 6);
  397. bridgeBaseAudioName[6] = '\0';
  398. bridgeBaseControlName[6] = '\0';
  399. }
  400. else
  401. {
  402. bridgeBaseAudioName[0] = '\0';
  403. bridgeBaseControlName[0] = '\0';
  404. }
  405. // ---------------------------------------------------------------------
  406. // Check plugin type
  407. CarlaBackend::PluginType itype(CarlaBackend::getPluginTypeFromString(stype));
  408. if (itype == CarlaBackend::PLUGIN_NONE)
  409. {
  410. carla_stderr("Invalid plugin type '%s'", stype);
  411. return 1;
  412. }
  413. // ---------------------------------------------------------------------
  414. // Set client name
  415. CarlaString clientName((name != nullptr) ? name : label);
  416. if (clientName.isEmpty())
  417. clientName = File(filename).getFileNameWithoutExtension().toRawUTF8();
  418. // ---------------------------------------------------------------------
  419. // Set extraStuff
  420. const void* extraStuff = nullptr;
  421. if (itype == CarlaBackend::PLUGIN_GIG || itype == CarlaBackend::PLUGIN_SF2 || itype == CarlaBackend::PLUGIN_SFZ)
  422. {
  423. if (label == nullptr)
  424. label = clientName;
  425. if (std::strstr(label, " (16 outs)") == 0)
  426. extraStuff = (const void*)label; // dummy non-null pointer
  427. }
  428. // ---------------------------------------------------------------------
  429. // Init plugin client
  430. CarlaPluginClient client(useBridge, (const char*)clientName, bridgeBaseAudioName, bridgeBaseControlName);
  431. if (! client.isOk())
  432. {
  433. carla_stderr("Failed to init engine, error was:\n%s", carla_get_last_error());
  434. return 1;
  435. }
  436. // ---------------------------------------------------------------------
  437. // Init OSC
  438. if (useOsc)
  439. client.oscInit(oscUrl);
  440. // ---------------------------------------------------------------------
  441. // Listen for ctrl+c or sigint/sigterm events
  442. initSignalHandler();
  443. // ---------------------------------------------------------------------
  444. // Init plugin
  445. int ret;
  446. if (carla_add_plugin(CarlaBackend::BINARY_NATIVE, itype, filename, name, label, extraStuff))
  447. {
  448. if (useOsc)
  449. {
  450. client.sendOscUpdate();
  451. client.sendOscBridgeUpdate();
  452. }
  453. else
  454. {
  455. carla_set_active(0, true);
  456. if (const CarlaPluginInfo* const pluginInfo = carla_get_plugin_info(0))
  457. {
  458. if (pluginInfo->hints & CarlaBackend::PLUGIN_HAS_GUI)
  459. carla_show_gui(0, true);
  460. }
  461. }
  462. client.ready(!useOsc);
  463. client.exec();
  464. carla_set_engine_about_to_close();
  465. carla_remove_plugin(0);
  466. ret = 0;
  467. }
  468. else
  469. {
  470. const char* const lastError(carla_get_last_error());
  471. carla_stderr("Plugin failed to load, error was:\n%s", lastError);
  472. if (useOsc)
  473. client.sendOscBridgeError(lastError);
  474. ret = 1;
  475. }
  476. // ---------------------------------------------------------------------
  477. // Close OSC
  478. if (useOsc)
  479. client.oscClose();
  480. return ret;
  481. }