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.

304 lines
10KB

  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 <QtCore/QDebug>
  21. #include <QtCore/QProcess>
  22. CARLA_BACKEND_START_NAMESPACE
  23. static inline
  24. const char* PluginThreadMode2str(const CarlaPluginThread::Mode mode)
  25. {
  26. switch (mode)
  27. {
  28. case CarlaPluginThread::PLUGIN_THREAD_NULL:
  29. return "PLUGIN_THREAD_NULL";
  30. case CarlaPluginThread::PLUGIN_THREAD_DSSI_GUI:
  31. return "PLUGIN_THREAD_DSSI_GUI";
  32. case CarlaPluginThread::PLUGIN_THREAD_LV2_GUI:
  33. return "PLUGIN_THREAD_LV2_GUI";
  34. case CarlaPluginThread::PLUGIN_THREAD_VST_GUI:
  35. return "PLUGIN_THREAD_VST_GUI";
  36. case CarlaPluginThread::PLUGIN_THREAD_BRIDGE:
  37. return "PLUGIN_THREAD_BRIDGE";
  38. }
  39. carla_stderr("CarlaPluginThread::PluginThreadMode2str(%i) - invalid mode", mode);
  40. return nullptr;
  41. }
  42. CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine, CarlaBackend::CarlaPlugin* const plugin, const Mode mode)
  43. : CarlaThread("CarlaPluginThread"),
  44. fEngine(engine),
  45. fPlugin(plugin),
  46. fMode(mode),
  47. fProcess(nullptr)
  48. {
  49. carla_debug("CarlaPluginThread::CarlaPluginThread(%p, %p, %s)", engine, plugin, PluginThreadMode2str(mode));
  50. }
  51. CarlaPluginThread::~CarlaPluginThread()
  52. {
  53. carla_debug("CarlaPluginThread::~CarlaPluginThread()");
  54. if (fProcess != nullptr)
  55. {
  56. delete fProcess;
  57. fProcess = nullptr;
  58. }
  59. }
  60. void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode)
  61. {
  62. CARLA_SAFE_ASSERT(! isThreadRunning());
  63. carla_debug("CarlaPluginThread::setMode(%s)", PluginThreadMode2str(mode));
  64. fMode = mode;
  65. }
  66. void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra1, const char* const extra2)
  67. {
  68. CARLA_SAFE_ASSERT(! isThreadRunning());
  69. carla_debug("CarlaPluginThread::setOscData(\"%s\", \"%s\", \"%s\", \"%s\")", binary, label, extra1, extra2);
  70. fBinary = binary;
  71. fLabel = label;
  72. fExtra1 = extra1;
  73. fExtra2 = extra2;
  74. }
  75. uintptr_t CarlaPluginThread::getPid() const
  76. {
  77. CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr, 0);
  78. return (uintptr_t)fProcess->pid();
  79. }
  80. void CarlaPluginThread::run()
  81. {
  82. carla_debug("CarlaPluginThread::run()");
  83. if (fProcess == nullptr)
  84. {
  85. fProcess = new QProcess(nullptr);
  86. fProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  87. }
  88. else if (fProcess->state() == QProcess::Running)
  89. {
  90. carla_stderr("CarlaPluginThread::run() - already running, giving up...");
  91. switch (fMode)
  92. {
  93. case PLUGIN_THREAD_NULL:
  94. break;
  95. case PLUGIN_THREAD_DSSI_GUI:
  96. case PLUGIN_THREAD_LV2_GUI:
  97. case PLUGIN_THREAD_VST_GUI:
  98. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  99. fProcess->terminate();
  100. return;
  101. case PLUGIN_THREAD_BRIDGE:
  102. break;
  103. }
  104. }
  105. QString name(fPlugin->getName());
  106. if (name.isEmpty())
  107. name = "(none)";
  108. QStringList arguments;
  109. QProcessEnvironment env(QProcessEnvironment::systemEnvironment());
  110. const EngineOptions& options(fEngine->getOptions());
  111. char strBuf[STR_MAX+1];
  112. env.insert("ENGINE_OPTION_UIS_ALWAYS_ON_TOP", options.uisAlwaysOnTop ? "true" : "false");
  113. if (options.maxParameters != 0)
  114. {
  115. std::sprintf(strBuf, "%u", options.maxParameters);
  116. env.insert("ENGINE_OPTION_MAX_PARAMETERS", strBuf);
  117. }
  118. if (options.uiBridgesTimeout != 0)
  119. {
  120. std::sprintf(strBuf, "%u", options.uiBridgesTimeout);
  121. env.insert("ENGINE_OPTION_UI_BRIDGES_TIMEOUT", strBuf);
  122. }
  123. if (options.frontendWinId != 0)
  124. {
  125. std::sprintf(strBuf, P_UINTPTR, options.frontendWinId);
  126. env.insert("ENGINE_OPTION_FRONTEND_WIN_ID", strBuf);
  127. }
  128. if (options.binaryDir != nullptr)
  129. env.insert("ENGINE_OPTION_PATH_BINARIES", options.binaryDir);
  130. if (options.resourceDir != nullptr)
  131. env.insert("ENGINE_OPTION_PATH_RESOURCES", options.resourceDir);
  132. switch (fMode)
  133. {
  134. case PLUGIN_THREAD_NULL:
  135. break;
  136. case PLUGIN_THREAD_DSSI_GUI:
  137. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  138. /* filename */ arguments << fPlugin->getFilename();
  139. /* label */ arguments << fLabel.buffer();
  140. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  141. break;
  142. case PLUGIN_THREAD_LV2_GUI:
  143. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  144. /* URI */ arguments << fLabel.buffer();
  145. /* ui-URI */ arguments << fExtra1.buffer();
  146. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  147. break;
  148. case PLUGIN_THREAD_VST_GUI:
  149. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  150. /* filename */ arguments << fPlugin->getFilename();
  151. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  152. break;
  153. case PLUGIN_THREAD_BRIDGE:
  154. env.insert("ENGINE_BRIDGE_SHM_IDS", fExtra2.buffer());
  155. env.insert("ENGINE_BRIDGE_CLIENT_NAME", name);
  156. env.insert("ENGINE_BRIDGE_OSC_URL", QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId()));
  157. if (fPlugin->getType() != PLUGIN_JACK)
  158. {
  159. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  160. /* stype */ arguments << fExtra1.buffer();
  161. /* filename */ arguments << fPlugin->getFilename();
  162. /* name */ arguments << name;
  163. /* label */ arguments << fLabel.buffer();
  164. /* uniqueId */ arguments << QString("%1").arg(fPlugin->getUniqueId());
  165. }
  166. else
  167. {
  168. env.insert("LD_LIBRARY_PATH", "/home/falktx/FOSS/GIT-mine/Carla/source/bridges/jackplugin/");
  169. carla_stdout("JACK app bridge here, filename: %s", fPlugin->getFilename());
  170. fBinary = fPlugin->getFilename();
  171. }
  172. break;
  173. }
  174. fProcess->setProcessEnvironment(env);
  175. carla_stdout("starting app..");
  176. qWarning() << arguments;
  177. fProcess->start(fBinary.buffer(), arguments);
  178. fProcess->waitForStarted();
  179. switch (fMode)
  180. {
  181. case PLUGIN_THREAD_NULL:
  182. break;
  183. case PLUGIN_THREAD_DSSI_GUI:
  184. case PLUGIN_THREAD_LV2_GUI:
  185. case PLUGIN_THREAD_VST_GUI:
  186. if (fPlugin->waitForOscGuiShow())
  187. {
  188. //fProcess->waitForFinished(-1);
  189. while (fProcess->state() != QProcess::NotRunning && ! shouldThreadExit())
  190. carla_sleep(1);
  191. // we only get here if UI was closed or thread asked to exit
  192. if (fProcess->state() != QProcess::NotRunning && shouldThreadExit())
  193. {
  194. fProcess->waitForFinished(static_cast<int>(fEngine->getOptions().uiBridgesTimeout));
  195. if (fProcess->state() == QProcess::Running)
  196. {
  197. carla_stdout("CarlaPluginThread::run() - UI refused to close, force kill now");
  198. fProcess->kill();
  199. }
  200. else
  201. {
  202. carla_stdout("CarlaPluginThread::run() - UI auto-closed successfully");
  203. }
  204. }
  205. else if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  206. carla_stderr("CarlaPluginThread::run() - UI crashed while running");
  207. else
  208. carla_stdout("CarlaPluginThread::run() - UI closed cleanly");
  209. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  210. }
  211. else
  212. {
  213. fProcess->close();
  214. CARLA_SAFE_ASSERT(fProcess->state() == QProcess::NotRunning);
  215. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  216. carla_stderr("CarlaPluginThread::run() - GUI crashed while opening");
  217. else
  218. carla_stdout("CarlaPluginThread::run() - GUI timeout");
  219. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  220. }
  221. break;
  222. case PLUGIN_THREAD_BRIDGE:
  223. //fProcess->waitForFinished(-1);
  224. while (fProcess->state() != QProcess::NotRunning && ! shouldThreadExit())
  225. carla_sleep(1);
  226. // we only get here if bridge crashed or thread asked to exit
  227. if (shouldThreadExit())
  228. {
  229. fProcess->waitForFinished(500);
  230. if (fProcess->state() == QProcess::Running)
  231. fProcess->close();
  232. }
  233. else
  234. {
  235. // forced quit, may have crashed
  236. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  237. {
  238. carla_stderr("CarlaPluginThread::run() - bridge crashed");
  239. CarlaString errorString("Plugin '" + CarlaString(fPlugin->getName()) + "' has crashed!\n"
  240. "Saving now will lose its current settings.\n"
  241. "Please remove this plugin, and not rely on it from this point.");
  242. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_ERROR, fPlugin->getId(), 0, 0, 0.0f, errorString);
  243. }
  244. }
  245. break;
  246. }
  247. carla_stdout("app finished");
  248. }
  249. CARLA_BACKEND_END_NAMESPACE