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.

347 lines
9.2KB

  1. // SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "CarlaBridgeFormat.hpp"
  4. #include "CarlaBridgeToolkit.hpp"
  5. #include "CarlaProcessUtils.hpp"
  6. #include "CarlaMIDI.h"
  7. #include "extra/Base64.hpp"
  8. #include "extra/Sleep.hpp"
  9. // needed for atom-util
  10. #ifndef nullptr
  11. # undef NULL
  12. # define NULL nullptr
  13. #endif
  14. #include "lv2/atom-util.h"
  15. CARLA_BRIDGE_UI_START_NAMESPACE
  16. // ---------------------------------------------------------------------
  17. CarlaBridgeFormat::CarlaBridgeFormat() noexcept
  18. : CarlaPipeClient(),
  19. fQuitReceived(false),
  20. fGotOptions(false),
  21. fLastMsgTimer(-1),
  22. fToolkit(nullptr),
  23. fLib(nullptr),
  24. fLibFilename(),
  25. fBase64ReservedChunk()
  26. {
  27. carla_debug("CarlaBridgeFormat::CarlaBridgeFormat()");
  28. try {
  29. fToolkit = CarlaBridgeToolkit::createNew(this);
  30. } CARLA_SAFE_EXCEPTION_RETURN("CarlaBridgeToolkit::createNew",);
  31. }
  32. CarlaBridgeFormat::~CarlaBridgeFormat() /*noexcept*/
  33. {
  34. carla_debug("CarlaBridgeFormat::~CarlaBridgeFormat()");
  35. if (isPipeRunning() && ! fQuitReceived)
  36. writeExitingMessageAndWait();
  37. if (fLib != nullptr)
  38. {
  39. lib_close(fLib);
  40. fLib = nullptr;
  41. }
  42. if (fToolkit != nullptr)
  43. {
  44. fToolkit->quit();
  45. delete fToolkit;
  46. fToolkit = nullptr;
  47. }
  48. closePipeClient();
  49. }
  50. // ---------------------------------------------------------------------
  51. bool CarlaBridgeFormat::libOpen(const char* const filename) noexcept
  52. {
  53. CARLA_SAFE_ASSERT_RETURN(fLib == nullptr, false);
  54. fLib = lib_open(filename);
  55. fLibFilename = filename;
  56. return fLib != nullptr;
  57. }
  58. void* CarlaBridgeFormat::libSymbol(const char* const symbol) const noexcept
  59. {
  60. CARLA_SAFE_ASSERT_RETURN(fLib != nullptr, nullptr);
  61. return lib_symbol<void*>(fLib, symbol);
  62. }
  63. const char* CarlaBridgeFormat::libError() const noexcept
  64. {
  65. CARLA_SAFE_ASSERT_RETURN(fLibFilename.isNotEmpty(), nullptr);
  66. return lib_error(fLibFilename);
  67. }
  68. // ---------------------------------------------------------------------
  69. bool CarlaBridgeFormat::msgReceived(const char* const msg) noexcept
  70. {
  71. carla_debug("CarlaBridgeFormat::msgReceived(\"%s\")", msg);
  72. if (! fGotOptions && std::strcmp(msg, "urid") != 0 && std::strcmp(msg, "uiOptions") != 0)
  73. {
  74. carla_stderr2("CarlaBridgeFormat::msgReceived(\"%s\") - invalid message while waiting for options", msg);
  75. return true;
  76. }
  77. if (fLastMsgTimer > 0)
  78. --fLastMsgTimer;
  79. if (std::strcmp(msg, "atom") == 0)
  80. {
  81. uint32_t index, atomTotalSize, base64Size;
  82. const char* base64atom;
  83. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(index), true);
  84. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(atomTotalSize), true);
  85. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(base64Size), true);
  86. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(base64atom, false, base64Size), true);
  87. d_getChunkFromBase64String_impl(fBase64ReservedChunk, base64atom);
  88. CARLA_SAFE_ASSERT_UINT2_RETURN(fBase64ReservedChunk.size() >= sizeof(LV2_Atom),
  89. fBase64ReservedChunk.size(), sizeof(LV2_Atom), true);
  90. #ifdef CARLA_PROPER_CPP11_SUPPORT
  91. const LV2_Atom* const atom((const LV2_Atom*)fBase64ReservedChunk.data());
  92. #else
  93. const LV2_Atom* const atom((const LV2_Atom*)&fBase64ReservedChunk.front());
  94. #endif
  95. const uint32_t atomTotalSizeCheck(lv2_atom_total_size(atom));
  96. CARLA_SAFE_ASSERT_UINT2_RETURN(atomTotalSizeCheck == atomTotalSize, atomTotalSizeCheck, atomTotalSize, true);
  97. CARLA_SAFE_ASSERT_UINT2_RETURN(atomTotalSizeCheck == fBase64ReservedChunk.size(),
  98. atomTotalSizeCheck, fBase64ReservedChunk.size(), true);
  99. dspAtomReceived(index, atom);
  100. return true;
  101. }
  102. if (std::strcmp(msg, "urid") == 0)
  103. {
  104. uint32_t urid, size;
  105. const char* uri;
  106. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(urid), true);
  107. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(size), true);
  108. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(uri, false, size), true);
  109. if (urid != 0)
  110. dspURIDReceived(urid, uri);
  111. return true;
  112. }
  113. if (std::strcmp(msg, "control") == 0)
  114. {
  115. uint32_t index;
  116. float value;
  117. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(index), true);
  118. CARLA_SAFE_ASSERT_RETURN(readNextLineAsFloat(value), true);
  119. dspParameterChanged(index, value);
  120. return true;
  121. }
  122. if (std::strcmp(msg, "parameter") == 0)
  123. {
  124. const char* uri;
  125. float value;
  126. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(uri, true), true);
  127. CARLA_SAFE_ASSERT_RETURN(readNextLineAsFloat(value), true);
  128. dspParameterChanged(uri, value);
  129. return true;
  130. }
  131. if (std::strcmp(msg, "program") == 0)
  132. {
  133. uint32_t index;
  134. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(index), true);
  135. dspProgramChanged(index);
  136. return true;
  137. }
  138. if (std::strcmp(msg, "midiprogram") == 0)
  139. {
  140. uint32_t bank, program;
  141. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(bank), true);
  142. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(program), true);
  143. dspMidiProgramChanged(bank, program);
  144. return true;
  145. }
  146. if (std::strcmp(msg, "configure") == 0)
  147. {
  148. const char* key;
  149. const char* value;
  150. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(key, true), true);
  151. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(value, false), true);
  152. dspStateChanged(key, value);
  153. delete[] key;
  154. return true;
  155. }
  156. if (std::strcmp(msg, "note") == 0)
  157. {
  158. bool onOff;
  159. uint8_t channel, note, velocity;
  160. CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(onOff), true);
  161. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(channel), true);
  162. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(note), true);
  163. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(velocity), true);
  164. dspNoteReceived(onOff, channel, note, velocity);
  165. return true;
  166. }
  167. if (std::strcmp(msg, "uiOptions") == 0)
  168. {
  169. BridgeFormatOptions opts;
  170. uint64_t transientWindowId;
  171. CARLA_SAFE_ASSERT_RETURN(readNextLineAsDouble(opts.sampleRate), true);
  172. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(opts.bgColor), true);
  173. CARLA_SAFE_ASSERT_RETURN(readNextLineAsUInt(opts.fgColor), true);
  174. CARLA_SAFE_ASSERT_RETURN(readNextLineAsFloat(opts.uiScale), true);
  175. CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(opts.useTheme), true);
  176. CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(opts.useThemeColors), true);
  177. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(opts.windowTitle, true), true);
  178. CARLA_SAFE_ASSERT_RETURN(readNextLineAsULong(transientWindowId), true);
  179. opts.transientWindowId = transientWindowId;
  180. // we can assume we are not standalone if we got options from controller side
  181. opts.isStandalone = false;
  182. fGotOptions = true;
  183. uiOptionsChanged(opts);
  184. delete[] opts.windowTitle;
  185. return true;
  186. }
  187. CARLA_SAFE_ASSERT_RETURN(fToolkit != nullptr, true);
  188. if (std::strcmp(msg, "show") == 0)
  189. {
  190. fToolkit->show();
  191. fToolkit->focus();
  192. return true;
  193. }
  194. if (std::strcmp(msg, "focus") == 0)
  195. {
  196. fToolkit->focus();
  197. return true;
  198. }
  199. if (std::strcmp(msg, "hide") == 0)
  200. {
  201. fToolkit->hide();
  202. return true;
  203. }
  204. if (std::strcmp(msg, "quit") == 0)
  205. {
  206. fQuitReceived = true;
  207. fToolkit->quit();
  208. delete fToolkit;
  209. fToolkit = nullptr;
  210. return true;
  211. }
  212. if (std::strcmp(msg, "uiTitle") == 0)
  213. {
  214. const char* title;
  215. CARLA_SAFE_ASSERT_RETURN(readNextLineAsString(title, false), true);
  216. fToolkit->setTitle(title);
  217. return true;
  218. }
  219. carla_stderr("CarlaBridgeFormat::msgReceived : %s", msg);
  220. return false;
  221. }
  222. // ---------------------------------------------------------------------
  223. bool CarlaBridgeFormat::init(const int argc, const char* argv[])
  224. {
  225. CARLA_SAFE_ASSERT_RETURN(fToolkit != nullptr, false);
  226. if (argc == 7)
  227. {
  228. if (! initPipeClient(argv))
  229. return false;
  230. fLastMsgTimer = 0;
  231. // wait for ui options
  232. for (; ++fLastMsgTimer < 50 && ! fGotOptions;)
  233. {
  234. idlePipe(true);
  235. d_msleep(20);
  236. }
  237. if (! fGotOptions)
  238. {
  239. carla_stderr2("CarlaBridgeFormat::init() - did not get options on time, quitting...");
  240. writeExitingMessageAndWait();
  241. closePipeClient();
  242. return false;
  243. }
  244. }
  245. if (! fToolkit->init(argc, argv))
  246. {
  247. if (argc == 7)
  248. closePipeClient();
  249. return false;
  250. }
  251. return true;
  252. }
  253. void CarlaBridgeFormat::exec(const bool showUI)
  254. {
  255. CARLA_SAFE_ASSERT_RETURN(fToolkit != nullptr,);
  256. carla_terminateProcessOnParentExit(true);
  257. fToolkit->exec(showUI);
  258. }
  259. // ---------------------------------------------------------------------
  260. CARLA_BRIDGE_UI_END_NAMESPACE
  261. #include "CarlaPipeUtils.cpp"
  262. // ---------------------------------------------------------------------