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.

393 lines
13KB

  1. /*
  2. * Carla Plugin
  3. * Copyright (C) 2011-2014 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. #if 0
  18. #include "CarlaPlugin.hpp"
  19. #include "CarlaPluginThread.hpp"
  20. #include "CarlaEngine.hpp"
  21. #include "juce_core.h"
  22. using juce::String;
  23. using juce::StringArray;
  24. CARLA_BACKEND_START_NAMESPACE
  25. // -----------------------------------------------------------------------
  26. #ifdef DEBUG
  27. static inline
  28. const char* PluginThreadMode2str(const CarlaPluginThread::Mode mode) noexcept
  29. {
  30. switch (mode)
  31. {
  32. case CarlaPluginThread::PLUGIN_THREAD_NULL:
  33. return "PLUGIN_THREAD_NULL";
  34. case CarlaPluginThread::PLUGIN_THREAD_DSSI_GUI:
  35. return "PLUGIN_THREAD_DSSI_GUI";
  36. case CarlaPluginThread::PLUGIN_THREAD_LV2_GUI:
  37. return "PLUGIN_THREAD_LV2_GUI";
  38. case CarlaPluginThread::PLUGIN_THREAD_VST2_GUI:
  39. return "PLUGIN_THREAD_VST2_GUI";
  40. case CarlaPluginThread::PLUGIN_THREAD_BRIDGE:
  41. return "PLUGIN_THREAD_BRIDGE";
  42. }
  43. carla_stderr("CarlaPluginThread::PluginThreadMode2str(%i) - invalid mode", mode);
  44. return nullptr;
  45. }
  46. #endif
  47. // -----------------------------------------------------------------------
  48. CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine, CarlaBackend::CarlaPlugin* const plugin, const Mode mode) noexcept
  49. : CarlaThread("CarlaPluginThread"),
  50. fEngine(engine),
  51. fPlugin(plugin),
  52. fMode(mode),
  53. fBinary(),
  54. fLabel(),
  55. fExtra1(),
  56. fExtra2(),
  57. fProcess(nullptr),
  58. leakDetector_CarlaPluginThread()
  59. {
  60. carla_debug("CarlaPluginThread::CarlaPluginThread(%p, %p, %s)", engine, plugin, PluginThreadMode2str(mode));
  61. }
  62. CarlaPluginThread::~CarlaPluginThread() noexcept
  63. {
  64. carla_debug("CarlaPluginThread::~CarlaPluginThread()");
  65. if (fProcess != nullptr)
  66. {
  67. //fProcess.release();
  68. //try {
  69. //delete fProcess;
  70. //} CARLA_SAFE_EXCEPTION("~CarlaPluginThread(): delete ChildProcess");
  71. fProcess = nullptr;
  72. }
  73. }
  74. void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode) noexcept
  75. {
  76. CARLA_SAFE_ASSERT(! isThreadRunning());
  77. carla_debug("CarlaPluginThread::setMode(%s)", PluginThreadMode2str(mode));
  78. fMode = mode;
  79. }
  80. void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra1, const char* const extra2) noexcept
  81. {
  82. CARLA_SAFE_ASSERT(! isThreadRunning());
  83. carla_debug("CarlaPluginThread::setOscData(\"%s\", \"%s\", \"%s\", \"%s\")", binary, label, extra1, extra2);
  84. fBinary = binary;
  85. fLabel = label;
  86. fExtra1 = extra1;
  87. fExtra2 = extra2;
  88. }
  89. uintptr_t CarlaPluginThread::getPid() const
  90. {
  91. CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr, 0);
  92. return (uintptr_t)fProcess->getPID();
  93. }
  94. void CarlaPluginThread::run()
  95. {
  96. carla_debug("CarlaPluginThread::run()");
  97. if (fProcess == nullptr)
  98. {
  99. fProcess = new ChildProcess;
  100. //fProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  101. }
  102. else if (fProcess->isRunning())
  103. {
  104. carla_stderr("CarlaPluginThread::run() - already running, giving up...");
  105. switch (fMode)
  106. {
  107. case PLUGIN_THREAD_NULL:
  108. case PLUGIN_THREAD_BRIDGE:
  109. break;
  110. case PLUGIN_THREAD_DSSI_GUI:
  111. case PLUGIN_THREAD_LV2_GUI:
  112. case PLUGIN_THREAD_VST2_GUI:
  113. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  114. fProcess->kill();
  115. fProcess = nullptr;
  116. return;
  117. }
  118. }
  119. String name(fPlugin->getName());
  120. String filename(fPlugin->getFilename());
  121. if (name.isEmpty())
  122. name = "(none)";
  123. if (filename.isEmpty())
  124. filename = "\"\"";
  125. if (fLabel.isEmpty())
  126. fLabel = "\"\"";
  127. StringArray arguments;
  128. #ifndef CARLA_OS_WIN
  129. if (fBinary.endsWith(".exe"))
  130. arguments.add("wine");
  131. #endif
  132. arguments.add(fBinary.buffer());
  133. // use a global mutex to ensure bridge environment is correct
  134. static CarlaMutex sEnvMutex;
  135. char strBuf[STR_MAX+1];
  136. strBuf[STR_MAX] = '\0';
  137. const EngineOptions& options(fEngine->getOptions());
  138. sEnvMutex.lock();
  139. carla_setenv("CARLA_CLIENT_NAME", name.toRawUTF8());
  140. std::snprintf(strBuf, STR_MAX, "%f", fEngine->getSampleRate());
  141. carla_setenv("CARLA_SAMPLE_RATE", strBuf);
  142. carla_setenv("ENGINE_OPTION_FORCE_STEREO", bool2str(options.forceStereo));
  143. carla_setenv("ENGINE_OPTION_PREFER_PLUGIN_BRIDGES", bool2str(options.preferPluginBridges));
  144. carla_setenv("ENGINE_OPTION_PREFER_UI_BRIDGES", bool2str(options.preferUiBridges));
  145. carla_setenv("ENGINE_OPTION_UIS_ALWAYS_ON_TOP", bool2str(options.uisAlwaysOnTop));
  146. std::snprintf(strBuf, STR_MAX, "%u", options.maxParameters);
  147. carla_setenv("ENGINE_OPTION_MAX_PARAMETERS", strBuf);
  148. std::snprintf(strBuf, STR_MAX, "%u", options.uiBridgesTimeout);
  149. carla_setenv("ENGINE_OPTION_UI_BRIDGES_TIMEOUT",strBuf);
  150. if (options.pathLADSPA != nullptr)
  151. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", options.pathLADSPA);
  152. else
  153. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LADSPA", "");
  154. if (options.pathDSSI != nullptr)
  155. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", options.pathDSSI);
  156. else
  157. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_DSSI", "");
  158. if (options.pathLV2 != nullptr)
  159. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", options.pathLV2);
  160. else
  161. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_LV2", "");
  162. if (options.pathVST2 != nullptr)
  163. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", options.pathVST2);
  164. else
  165. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST2", "");
  166. if (options.pathVST3 != nullptr)
  167. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", options.pathVST3);
  168. else
  169. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_VST3", "");
  170. if (options.pathAU != nullptr)
  171. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", options.pathAU);
  172. else
  173. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_AU", "");
  174. if (options.pathGIG != nullptr)
  175. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", options.pathGIG);
  176. else
  177. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_GIG", "");
  178. if (options.pathSF2 != nullptr)
  179. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", options.pathSF2);
  180. else
  181. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SF2", "");
  182. if (options.pathSFZ != nullptr)
  183. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", options.pathSFZ);
  184. else
  185. carla_setenv("ENGINE_OPTION_PLUGIN_PATH_SFZ", "");
  186. if (options.binaryDir != nullptr)
  187. carla_setenv("ENGINE_OPTION_PATH_BINARIES", options.binaryDir);
  188. else
  189. carla_setenv("ENGINE_OPTION_PATH_BINARIES", "");
  190. if (options.resourceDir != nullptr)
  191. carla_setenv("ENGINE_OPTION_PATH_RESOURCES", options.resourceDir);
  192. else
  193. carla_setenv("ENGINE_OPTION_PATH_RESOURCES", "");
  194. carla_setenv("ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR", bool2str(options.preventBadBehaviour));
  195. std::snprintf(strBuf, STR_MAX, P_UINTPTR, options.frontendWinId);
  196. carla_setenv("ENGINE_OPTION_FRONTEND_WIN_ID", strBuf);
  197. #ifdef CARLA_OS_LINUX
  198. const char* const oldPreload(std::getenv("LD_PRELOAD"));
  199. ::unsetenv("LD_PRELOAD");
  200. #endif
  201. switch (fMode)
  202. {
  203. case PLUGIN_THREAD_NULL:
  204. break;
  205. case PLUGIN_THREAD_DSSI_GUI:
  206. /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId()));
  207. /* filename */ arguments.add(filename);
  208. /* label */ arguments.add(fLabel.buffer());
  209. /* ui-title */ arguments.add(name + String(" (GUI)"));
  210. break;
  211. case PLUGIN_THREAD_LV2_GUI:
  212. /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId()));
  213. /* URI */ arguments.add(fLabel.buffer());
  214. /* UI URI */ arguments.add(fExtra1.buffer());
  215. /* UI Bundle */ arguments.add(fExtra2.buffer());
  216. /* UI Title */ arguments.add(name + String(" (GUI)"));
  217. break;
  218. case PLUGIN_THREAD_VST2_GUI:
  219. /* osc-url */ arguments.add(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId()));
  220. /* filename */ arguments.add(filename);
  221. /* ui-title */ arguments.add(name + String(" (GUI)"));
  222. break;
  223. case PLUGIN_THREAD_BRIDGE:
  224. /* stype */ arguments.add(fExtra1.buffer());
  225. /* filename */ arguments.add(filename);
  226. /* label */ arguments.add(fLabel.buffer());
  227. /* uniqueId */ arguments.add(String(static_cast<juce::int64>(fPlugin->getUniqueId())));
  228. carla_setenv("ENGINE_BRIDGE_OSC_URL", String(String(fEngine->getOscServerPathUDP()) + String("/") + String(fPlugin->getId())).toRawUTF8());
  229. carla_setenv("ENGINE_BRIDGE_SHM_IDS", fExtra2.buffer());
  230. carla_setenv("WINEDEBUG", "-all");
  231. break;
  232. }
  233. carla_stdout("starting app..");
  234. fProcess->start(arguments);
  235. #ifdef CARLA_OS_LINUX
  236. if (oldPreload != nullptr)
  237. ::setenv("LD_PRELOAD", oldPreload, 1);
  238. #endif
  239. sEnvMutex.unlock();
  240. switch (fMode)
  241. {
  242. case PLUGIN_THREAD_NULL:
  243. break;
  244. case PLUGIN_THREAD_DSSI_GUI:
  245. case PLUGIN_THREAD_LV2_GUI:
  246. case PLUGIN_THREAD_VST2_GUI:
  247. if (fPlugin->waitForOscGuiShow())
  248. {
  249. while (fProcess->isRunning() && ! shouldThreadExit())
  250. carla_sleep(1);
  251. // we only get here if UI was closed or thread asked to exit
  252. if (fProcess->isRunning() && shouldThreadExit())
  253. {
  254. fProcess->waitForProcessToFinish(static_cast<int>(fEngine->getOptions().uiBridgesTimeout));
  255. if (fProcess->isRunning())
  256. {
  257. carla_stdout("CarlaPluginThread::run() - UI refused to close, force kill now");
  258. fProcess->kill();
  259. }
  260. else
  261. {
  262. carla_stdout("CarlaPluginThread::run() - UI auto-closed successfully");
  263. }
  264. }
  265. else if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/)
  266. carla_stderr("CarlaPluginThread::run() - UI crashed while running");
  267. else
  268. carla_stdout("CarlaPluginThread::run() - UI closed cleanly");
  269. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  270. }
  271. else
  272. {
  273. fProcess->kill();
  274. carla_stdout("CarlaPluginThread::run() - GUI timeout");
  275. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  276. }
  277. break;
  278. case PLUGIN_THREAD_BRIDGE:
  279. //fProcess->waitForFinished(-1);
  280. while (fProcess->isRunning() && ! shouldThreadExit())
  281. carla_sleep(1);
  282. // we only get here if bridge crashed or thread asked to exit
  283. if (fProcess->isRunning() && shouldThreadExit())
  284. {
  285. fProcess->waitForProcessToFinish(2000);
  286. if (fProcess->isRunning())
  287. {
  288. carla_stdout("CarlaPluginThread::run() - bridge refused to close, force kill now");
  289. fProcess->kill();
  290. }
  291. else
  292. {
  293. carla_stdout("CarlaPluginThread::run() - bridge auto-closed successfully");
  294. }
  295. }
  296. else
  297. {
  298. // forced quit, may have crashed
  299. if (fProcess->getExitCode() != 0 /*|| fProcess->exitStatus() == QProcess::CrashExit*/)
  300. {
  301. carla_stderr("CarlaPluginThread::run() - bridge crashed");
  302. CarlaString errorString("Plugin '" + CarlaString(fPlugin->getName()) + "' has crashed!\n"
  303. "Saving now will lose its current settings.\n"
  304. "Please remove this plugin, and not rely on it from this point.");
  305. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_ERROR, fPlugin->getId(), 0, 0, 0.0f, errorString);
  306. }
  307. }
  308. break;
  309. }
  310. carla_stdout("app finished");
  311. fProcess = nullptr;
  312. }
  313. // -----------------------------------------------------------------------
  314. CARLA_BACKEND_END_NAMESPACE
  315. #endif