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.

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