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.

256 lines
8.6KB

  1. /*
  2. * Carla Plugin Thread
  3. * Copyright (C) 2012-2013 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 "CarlaPluginThread.hpp"
  18. #include "CarlaPlugin.hpp"
  19. #include "CarlaEngine.hpp"
  20. #include <QtCore/QProcess>
  21. CARLA_BACKEND_START_NAMESPACE
  22. 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. void CarlaPluginThread::run()
  74. {
  75. carla_debug("CarlaPluginThread::run()");
  76. if (fProcess == nullptr)
  77. {
  78. fProcess = new QProcess(nullptr);
  79. fProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  80. }
  81. else if (fProcess->state() == QProcess::Running)
  82. {
  83. carla_stderr("CarlaPluginThread::run() - already running, giving up...");
  84. switch (fMode)
  85. {
  86. case PLUGIN_THREAD_NULL:
  87. break;
  88. case PLUGIN_THREAD_DSSI_GUI:
  89. case PLUGIN_THREAD_LV2_GUI:
  90. case PLUGIN_THREAD_VST_GUI:
  91. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  92. fProcess->terminate();
  93. return;
  94. case PLUGIN_THREAD_BRIDGE:
  95. break;
  96. }
  97. }
  98. QString name(fPlugin->getName());
  99. QStringList arguments;
  100. if (name.isEmpty())
  101. name = "(none)";
  102. switch (fMode)
  103. {
  104. case PLUGIN_THREAD_NULL:
  105. break;
  106. case PLUGIN_THREAD_DSSI_GUI:
  107. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathUDP()).arg(fPlugin->getId());
  108. /* filename */ arguments << fPlugin->getFilename();
  109. /* label */ arguments << (const char*)fLabel;
  110. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  111. break;
  112. case PLUGIN_THREAD_LV2_GUI:
  113. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  114. /* URI */ arguments << (const char*)fLabel;
  115. /* ui-URI */ arguments << (const char*)fExtra1;
  116. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  117. break;
  118. case PLUGIN_THREAD_VST_GUI:
  119. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  120. /* filename */ arguments << fPlugin->getFilename();
  121. /* ui-title */ arguments << QString("%1 (GUI)").arg(fPlugin->getName());
  122. break;
  123. case PLUGIN_THREAD_BRIDGE:
  124. /* osc-url */ arguments << QString("%1/%2").arg(fEngine->getOscServerPathTCP()).arg(fPlugin->getId());
  125. /* stype */ arguments << (const char*)fExtra1;
  126. /* filename */ arguments << fPlugin->getFilename();
  127. /* name */ arguments << name;
  128. /* label */ arguments << (const char*)fLabel;
  129. /* SHM ids */ arguments << (const char*)fExtra2;
  130. break;
  131. }
  132. fProcess->start((const char*)fBinary, arguments);
  133. fProcess->waitForStarted();
  134. switch (fMode)
  135. {
  136. case PLUGIN_THREAD_NULL:
  137. break;
  138. case PLUGIN_THREAD_DSSI_GUI:
  139. case PLUGIN_THREAD_LV2_GUI:
  140. case PLUGIN_THREAD_VST_GUI:
  141. if (fPlugin->waitForOscGuiShow())
  142. {
  143. //fProcess->waitForFinished(-1);
  144. while (fProcess->state() != QProcess::NotRunning && ! shouldExit())
  145. carla_sleep(1);
  146. // we only get here if UI was closed or thread asked to exit
  147. if (fProcess->state() != QProcess::NotRunning && shouldExit())
  148. {
  149. fProcess->waitForFinished(fEngine->getOptions().uiBridgesTimeout);
  150. if (fProcess->state() == QProcess::Running)
  151. {
  152. carla_stdout("CarlaPluginThread::run() - UI refused to close, force kill now");
  153. fProcess->kill();
  154. }
  155. else
  156. {
  157. carla_stdout("CarlaPluginThread::run() - UI auto-closed successfully");
  158. }
  159. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  160. }
  161. else if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  162. {
  163. carla_stderr("CarlaPluginThread::run() - UI crashed while running");
  164. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  165. }
  166. else
  167. {
  168. carla_stdout("CarlaPluginThread::run() - UI closed cleanly");
  169. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  170. }
  171. }
  172. else
  173. {
  174. fProcess->close();
  175. CARLA_ASSERT(fProcess->state() == QProcess::NotRunning);
  176. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  177. {
  178. carla_stderr("CarlaPluginThread::run() - GUI crashed while opening");
  179. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), -1, 0, 0.0f, nullptr);
  180. }
  181. else
  182. {
  183. carla_debug("CarlaPluginThread::run() - GUI timeout");
  184. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_UI_STATE_CHANGED, fPlugin->getId(), 0, 0, 0.0f, nullptr);
  185. }
  186. }
  187. break;
  188. case PLUGIN_THREAD_BRIDGE:
  189. //fProcess->waitForFinished(-1);
  190. while (fProcess->state() != QProcess::NotRunning && ! shouldExit())
  191. carla_sleep(1);
  192. // we only get here if bridge crashed or thread asked to exit
  193. if (shouldExit())
  194. {
  195. fProcess->waitForFinished(500);
  196. if (fProcess->state() == QProcess::Running)
  197. fProcess->close();
  198. }
  199. else
  200. {
  201. // forced quit, may have crashed
  202. if (fProcess->exitCode() != 0 || fProcess->exitStatus() == QProcess::CrashExit)
  203. {
  204. carla_stderr("CarlaPluginThread::run() - bridge crashed");
  205. CarlaString errorString("Plugin '" + CarlaString(fPlugin->getName()) + "' has crashed!\n"
  206. "Saving now will lose its current settings.\n"
  207. "Please remove this plugin, and not rely on it from this point.");
  208. fEngine->callback(CarlaBackend::ENGINE_CALLBACK_ERROR, fPlugin->getId(), 0, 0, 0.0f, (const char*)errorString);
  209. }
  210. }
  211. break;
  212. }
  213. }
  214. CARLA_BACKEND_END_NAMESPACE