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.

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