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.

263 lines
8.7KB

  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/QProcess>
  21. CARLA_BACKEND_START_NAMESPACE
  22. static const char* PluginThreadMode2str(const CarlaPluginThread::Mode mode)
  23. {
  24. switch (mode)
  25. {
  26. case CarlaPluginThread::PLUGIN_THREAD_NULL:
  27. return "PLUGIN_THREAD_NULL";
  28. case CarlaPluginThread::PLUGIN_THREAD_DSSI_GUI:
  29. return "PLUGIN_THREAD_DSSI_GUI";
  30. case CarlaPluginThread::PLUGIN_THREAD_LV2_GUI:
  31. return "PLUGIN_THREAD_LV2_GUI";
  32. case CarlaPluginThread::PLUGIN_THREAD_VST_GUI:
  33. return "PLUGIN_THREAD_VST_GUI";
  34. case CarlaPluginThread::PLUGIN_THREAD_BRIDGE:
  35. return "PLUGIN_THREAD_BRIDGE";
  36. }
  37. carla_stderr("CarlaPluginThread::PluginThreadMode2str(%i) - invalid mode", mode);
  38. return nullptr;
  39. }
  40. CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine, CarlaBackend::CarlaPlugin* const plugin, const Mode mode)
  41. : CarlaThread("CarlaPluginThread"),
  42. fEngine(engine),
  43. fPlugin(plugin),
  44. fMode(mode),
  45. fProcess(nullptr)
  46. {
  47. carla_debug("CarlaPluginThread::CarlaPluginThread(%p, %p, %s)", engine, plugin, PluginThreadMode2str(mode));
  48. }
  49. CarlaPluginThread::~CarlaPluginThread()
  50. {
  51. carla_debug("CarlaPluginThread::~CarlaPluginThread()");
  52. if (fProcess != nullptr)
  53. {
  54. delete fProcess;
  55. fProcess = nullptr;
  56. }
  57. }
  58. void CarlaPluginThread::setMode(const CarlaPluginThread::Mode mode)
  59. {
  60. CARLA_ASSERT(! isRunning());
  61. carla_debug("CarlaPluginThread::setMode(%s)", PluginThreadMode2str(mode));
  62. fMode = mode;
  63. }
  64. void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const extra1, const char* const extra2)
  65. {
  66. CARLA_ASSERT(! isRunning());
  67. carla_debug("CarlaPluginThread::setOscData(\"%s\", \"%s\", \"%s\", \"%s\")", binary, label, extra1, extra2);
  68. fBinary = binary;
  69. fLabel = label;
  70. fExtra1 = extra1;
  71. fExtra2 = extra2;
  72. }
  73. ulong CarlaPluginThread::getPid() const
  74. {
  75. CARLA_SAFE_ASSERT_RETURN(fProcess != nullptr, 0);
  76. return (ulong)fProcess->pid();
  77. }
  78. void CarlaPluginThread::run()
  79. {
  80. carla_debug("CarlaPluginThread::run()");
  81. if (fProcess == nullptr)
  82. {
  83. fProcess = new QProcess(nullptr);
  84. fProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  85. }
  86. else if (fProcess->state() == QProcess::Running)
  87. {
  88. carla_stderr("CarlaPluginThread::run() - already running, giving up...");
  89. switch (fMode)
  90. {
  91. case PLUGIN_THREAD_NULL:
  92. break;
  93. case PLUGIN_THREAD_DSSI_GUI:
  94. case PLUGIN_THREAD_LV2_GUI:
  95. case PLUGIN_THREAD_VST_GUI:
  96. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  97. fProcess->terminate();
  98. return;
  99. case PLUGIN_THREAD_BRIDGE:
  100. break;
  101. }
  102. }
  103. QString name(fPlugin->getName());
  104. if (name.isEmpty())
  105. name = "(none)";
  106. QStringList arguments;
  107. switch (fMode)
  108. {
  109. case PLUGIN_THREAD_NULL:
  110. break;
  111. case PLUGIN_THREAD_DSSI_GUI:
  112. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  113. /* filename */ arguments << fPlugin->getFilename();
  114. /* label */ arguments << (const char*)fLabel;
  115. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  116. break;
  117. case PLUGIN_THREAD_LV2_GUI:
  118. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  119. /* URI */ arguments << (const char*)fLabel;
  120. /* ui-URI */ arguments << (const char*)fExtra1;
  121. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  122. break;
  123. case PLUGIN_THREAD_VST_GUI:
  124. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  125. /* filename */ arguments << fPlugin->getFilename();
  126. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  127. break;
  128. case PLUGIN_THREAD_BRIDGE:
  129. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  130. /* stype */ arguments << (const char*)fExtra1;
  131. /* filename */ arguments << fPlugin->getFilename();
  132. /* name */ arguments << name;
  133. /* label */ arguments << (const char*)fLabel;
  134. /* SHM ids */ arguments << (const char*)fExtra2;
  135. break;
  136. }
  137. fProcess->start((const char*)fBinary, arguments);
  138. fProcess->waitForStarted();
  139. switch (fMode)
  140. {
  141. case PLUGIN_THREAD_NULL:
  142. break;
  143. case PLUGIN_THREAD_DSSI_GUI:
  144. case PLUGIN_THREAD_LV2_GUI:
  145. case PLUGIN_THREAD_VST_GUI:
  146. if (fPlugin->waitForOscGuiShow())
  147. {
  148. //fProcess->waitForFinished(-1);
  149. while (fProcess->state() != QProcess::NotRunning && ! shouldExit())
  150. carla_sleep(1);
  151. // we only get here if UI was closed or thread asked to exit
  152. if (fProcess->state() != QProcess::NotRunning && shouldExit())
  153. {
  154. fProcess->waitForFinished(static_cast<int>(fEngine->getOptions().uiBridgesTimeout));
  155. if (fProcess->state() == QProcess::Running)
  156. {
  157. carla_stdout("CarlaPluginThread::run() - UI refused to close, force kill now");
  158. fProcess->kill();
  159. }
  160. else
  161. {
  162. carla_stdout("CarlaPluginThread::run() - UI auto-closed successfully");
  163. }
  164. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  165. }
  166. else if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  167. {
  168. carla_stderr("CarlaPluginThread::run() - UI crashed while running");
  169. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  170. }
  171. else
  172. {
  173. carla_stdout("CarlaPluginThread::run() - UI closed cleanly");
  174. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  175. }
  176. }
  177. else
  178. {
  179. fProcess->close();
  180. CARLA_ASSERT(fProcess->state() == QProcess::NotRunning);
  181. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  182. {
  183. carla_stderr("CarlaPluginThread::run() - GUI crashed while opening");
  184. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  185. }
  186. else
  187. {
  188. carla_debug("CarlaPluginThread::run() - GUI timeout");
  189. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  190. }
  191. }
  192. break;
  193. case PLUGIN_THREAD_BRIDGE:
  194. //fProcess->waitForFinished(-1);
  195. while (fProcess->state() != QProcess::NotRunning && ! shouldExit())
  196. carla_sleep(1);
  197. // we only get here if bridge crashed or thread asked to exit
  198. if (shouldExit())
  199. {
  200. fProcess->waitForFinished(500);
  201. if (fProcess->state() == QProcess::Running)
  202. fProcess->close();
  203. }
  204. else
  205. {
  206. // forced quit, may have crashed
  207. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  208. {
  209. carla_stderr("CarlaPluginThread::run() - bridge crashed");
  210. CarlaString errorString("Plugin '" + CarlaString(fPlugin->getName()) + "' has crashed!\n"
  211. "Saving now will lose its current settings.\n"
  212. "Please remove this plugin, and not rely on it from this point.");
  213. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_ERROR, fPlugin->getId(), 0, 0, 0.0f, (const char*)errorString);
  214. }
  215. }
  216. break;
  217. }
  218. }
  219. CARLA_BACKEND_END_NAMESPACE