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.

247 lines
7.3KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021-2022 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 3 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 LICENSE file.
  16. */
  17. #include <engine/Engine.hpp>
  18. #include <patch.hpp>
  19. #include <system.hpp>
  20. #ifdef NDEBUG
  21. # undef DEBUG
  22. #endif
  23. #include "CardinalRemote.hpp"
  24. #include "PluginContext.hpp"
  25. #include "extra/Base64.hpp"
  26. #include "extra/ScopedSafeLocale.hpp"
  27. #if defined(STATIC_BUILD) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  28. # undef HAVE_LIBLO
  29. #endif
  30. #if (defined(HAVE_LIBLO) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS) && !defined(HEADLESS)
  31. # define CARDINAL_REMOTE_ENABLED
  32. #endif
  33. #ifdef HAVE_LIBLO
  34. # include <lo/lo.h>
  35. #endif
  36. // -----------------------------------------------------------------------------------------------------------
  37. namespace remoteUtils {
  38. #ifdef HAVE_LIBLO
  39. static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self)
  40. {
  41. d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc);
  42. if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's')
  43. {
  44. d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s);
  45. if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0)
  46. static_cast<RemoteDetails*>(self)->connected = true;
  47. }
  48. return 0;
  49. }
  50. #endif
  51. RemoteDetails* getRemote()
  52. {
  53. #ifdef CARDINAL_REMOTE_ENABLED
  54. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  55. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr);
  56. CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui);
  57. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, nullptr);
  58. return ui->remoteDetails;
  59. #else
  60. return nullptr;
  61. #endif
  62. }
  63. bool connectToRemote(const char* const url)
  64. {
  65. #ifdef CARDINAL_REMOTE_ENABLED
  66. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  67. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, false);
  68. CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui);
  69. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false);
  70. RemoteDetails* remoteDetails = ui->remoteDetails;
  71. #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  72. if (remoteDetails == nullptr)
  73. {
  74. ui->remoteDetails = remoteDetails = new RemoteDetails;
  75. remoteDetails->handle = ui;
  76. remoteDetails->url = strdup(url);
  77. remoteDetails->connected = true;
  78. remoteDetails->autoDeploy = true;
  79. }
  80. #elif defined(HAVE_LIBLO)
  81. const lo_address addr = lo_address_new_from_url(url);
  82. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr, false);
  83. if (remoteDetails == nullptr)
  84. {
  85. const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr);
  86. DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr, false);
  87. ui->remoteDetails = remoteDetails = new RemoteDetails;
  88. remoteDetails->handle = oscServer;
  89. remoteDetails->url = strdup(url);
  90. remoteDetails->connected = false;
  91. remoteDetails->autoDeploy = false;
  92. lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, remoteDetails);
  93. }
  94. else if (std::strcmp(remoteDetails->url, url) != 0)
  95. {
  96. ui->remoteDetails = nullptr;
  97. disconnectFromRemote(remoteDetails);
  98. return connectToRemote(url);
  99. }
  100. lo_send(addr, "/hello", "");
  101. lo_address_free(addr);
  102. #endif
  103. return remoteDetails != nullptr;
  104. #else
  105. return false;
  106. #endif
  107. }
  108. void disconnectFromRemote(RemoteDetails* const remote)
  109. {
  110. if (remote != nullptr)
  111. {
  112. #ifdef HAVE_LIBLO
  113. lo_server_free(static_cast<lo_server>(remote->handle));
  114. #endif
  115. std::free(const_cast<char*>(remote->url));
  116. delete remote;
  117. }
  118. }
  119. void idleRemote(RemoteDetails* const remote)
  120. {
  121. DISTRHO_SAFE_ASSERT_RETURN(remote != nullptr,);
  122. #ifdef HAVE_LIBLO
  123. while (lo_server_recv_noblock(static_cast<lo_server>(remote->handle), 0) != 0) {}
  124. #endif
  125. }
  126. void sendParamChangeToRemote(RemoteDetails* const remote, int64_t moduleId, int paramId, float value)
  127. {
  128. #ifdef CARDINAL_REMOTE_ENABLED
  129. #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  130. char paramBuf[512] = {};
  131. {
  132. const ScopedSafeLocale cssl;
  133. std::snprintf(paramBuf, sizeof(paramBuf), "%lld:%d:%f", (long long)moduleId, paramId, value);
  134. }
  135. static_cast<CardinalBaseUI*>(remote->handle)->setState("param", paramBuf);
  136. #elif defined(HAVE_LIBLO)
  137. const lo_address addr = lo_address_new_from_url(remote->url);
  138. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  139. lo_send(addr, "/param", "hif", moduleId, paramId, value);
  140. lo_address_free(addr);
  141. #endif
  142. #endif
  143. }
  144. void sendFullPatchToRemote(RemoteDetails* const remote)
  145. {
  146. #ifdef CARDINAL_REMOTE_ENABLED
  147. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  148. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,);
  149. context->engine->prepareSave();
  150. context->patch->saveAutosave();
  151. context->patch->cleanAutosave();
  152. std::vector<uint8_t> data;
  153. using namespace rack::system;
  154. #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  155. FILE* const f = std::fopen(join(context->patch->autosavePath, "patch.json").c_str(), "r");
  156. DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
  157. DEFER({
  158. std::fclose(f);
  159. });
  160. std::fseek(f, 0, SEEK_END);
  161. const long fileSize = std::ftell(f);
  162. DISTRHO_SAFE_ASSERT_RETURN(fileSize > 0,);
  163. std::fseek(f, 0, SEEK_SET);
  164. char* const fileContent = new char[fileSize+1];
  165. DISTRHO_SAFE_ASSERT_RETURN(std::fread(fileContent, fileSize, 1, f) == 1,);
  166. fileContent[fileSize] = '\0';
  167. static_cast<CardinalBaseUI*>(remote->handle)->setState("patch", fileContent);
  168. delete[] fileContent;
  169. #elif defined(HAVE_LIBLO)
  170. try {
  171. data = archiveDirectory(context->patch->autosavePath, 1);
  172. } DISTRHO_SAFE_EXCEPTION_RETURN("sendFullPatchToRemote",);
  173. DISTRHO_SAFE_ASSERT_RETURN(data.size() >= 4,);
  174. const lo_address addr = lo_address_new_from_url(remote->url);
  175. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  176. if (const lo_blob blob = lo_blob_new(data.size(), data.data()))
  177. {
  178. lo_send(addr, "/load", "b", blob);
  179. lo_blob_free(blob);
  180. }
  181. lo_address_free(addr);
  182. #endif
  183. #endif
  184. }
  185. void sendScreenshotToRemote(RemoteDetails* const remote, const char* const screenshot)
  186. {
  187. #if defined(HAVE_LIBLO) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  188. const lo_address addr = lo_address_new_from_url(remote->url);
  189. DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
  190. std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot));
  191. if (const lo_blob blob = lo_blob_new(data.size(), data.data()))
  192. {
  193. lo_send(addr, "/screenshot", "b", blob);
  194. lo_blob_free(blob);
  195. }
  196. lo_address_free(addr);
  197. #endif
  198. }
  199. }
  200. // -----------------------------------------------------------------------------------------------------------