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.

494 lines
12KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoUIInternal.hpp"
  17. #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
  18. # error DSSI UIs do not support direct access!
  19. #endif
  20. #include <lo/lo.h>
  21. START_NAMESPACE_DISTRHO
  22. // -----------------------------------------------------------------------
  23. struct OscData {
  24. lo_address addr;
  25. const char* path;
  26. lo_server server;
  27. OscData()
  28. : addr(nullptr),
  29. path(nullptr),
  30. server(nullptr) {}
  31. void idle() const
  32. {
  33. if (server == nullptr)
  34. return;
  35. while (lo_server_recv_noblock(server, 0) != 0) {}
  36. }
  37. void send_configure(const char* const key, const char* const value) const
  38. {
  39. char targetPath[std::strlen(path)+11];
  40. std::strcpy(targetPath, path);
  41. std::strcat(targetPath, "/configure");
  42. lo_send(addr, targetPath, "ss", key, value);
  43. }
  44. void send_control(const int32_t index, const float value) const
  45. {
  46. char targetPath[std::strlen(path)+9];
  47. std::strcpy(targetPath, path);
  48. std::strcat(targetPath, "/control");
  49. lo_send(addr, targetPath, "if", index, value);
  50. }
  51. void send_midi(unsigned char data[4]) const
  52. {
  53. char targetPath[std::strlen(path)+6];
  54. std::strcpy(targetPath, path);
  55. std::strcat(targetPath, "/midi");
  56. lo_send(addr, targetPath, "m", data);
  57. }
  58. void send_update(const char* const url) const
  59. {
  60. char targetPath[std::strlen(path)+8];
  61. std::strcpy(targetPath, path);
  62. std::strcat(targetPath, "/update");
  63. lo_send(addr, targetPath, "s", url);
  64. }
  65. void send_exiting() const
  66. {
  67. char targetPath[std::strlen(path)+9];
  68. std::strcpy(targetPath, path);
  69. std::strcat(targetPath, "/exiting");
  70. lo_send(addr, targetPath, "");
  71. }
  72. };
  73. // -----------------------------------------------------------------------
  74. class UIDssi
  75. {
  76. public:
  77. UIDssi(const OscData& oscData, const char* const uiTitle)
  78. : fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback),
  79. fHostClosed(false),
  80. fOscData(oscData)
  81. {
  82. fUI.setTitle(uiTitle);
  83. }
  84. ~UIDssi()
  85. {
  86. if (fOscData.server != nullptr && ! fHostClosed)
  87. fOscData.send_exiting();
  88. }
  89. void exec()
  90. {
  91. for (;;)
  92. {
  93. fOscData.idle();
  94. if (! fUI.idle())
  95. break;
  96. d_msleep(50);
  97. }
  98. }
  99. // -------------------------------------------------------------------
  100. #if DISTRHO_PLUGIN_WANT_STATE
  101. void dssiui_configure(const char* key, const char* value)
  102. {
  103. fUI.stateChanged(key, value);
  104. }
  105. #endif
  106. void dssiui_control(unsigned long index, float value)
  107. {
  108. fUI.parameterChanged(index, value);
  109. }
  110. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  111. void dssiui_program(unsigned long bank, unsigned long program)
  112. {
  113. fUI.programChanged(bank * 128 + program);
  114. }
  115. #endif
  116. void dssiui_show()
  117. {
  118. fUI.setVisible(true);
  119. }
  120. void dssiui_hide()
  121. {
  122. fUI.setVisible(false);
  123. }
  124. void dssiui_quit()
  125. {
  126. fHostClosed = true;
  127. fUI.quit();
  128. }
  129. // -------------------------------------------------------------------
  130. protected:
  131. void setParameterValue(const uint32_t rindex, const float value)
  132. {
  133. if (fOscData.server == nullptr)
  134. return;
  135. fOscData.send_control(rindex, value);
  136. }
  137. void setState(const char* const key, const char* const value)
  138. {
  139. if (fOscData.server == nullptr)
  140. return;
  141. fOscData.send_configure(key, value);
  142. }
  143. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  144. {
  145. if (fOscData.server == nullptr)
  146. return;
  147. if (channel > 0xF)
  148. return;
  149. uint8_t mdata[4] = { 0, channel, note, velocity };
  150. mdata[1] += (velocity != 0) ? 0x90 : 0x80;
  151. fOscData.send_midi(mdata);
  152. }
  153. void uiResize(const unsigned int width, const unsigned int height)
  154. {
  155. fUI.setSize(width, height);
  156. }
  157. private:
  158. UIExporter fUI;
  159. bool fHostClosed;
  160. const OscData& fOscData;
  161. // -------------------------------------------------------------------
  162. // Callbacks
  163. #define uiPtr ((UIDssi*)ptr)
  164. static void setParameterCallback(void* ptr, uint32_t rindex, float value)
  165. {
  166. uiPtr->setParameterValue(rindex, value);
  167. }
  168. static void setStateCallback(void* ptr, const char* key, const char* value)
  169. {
  170. uiPtr->setState(key, value);
  171. }
  172. static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
  173. {
  174. uiPtr->sendNote(channel, note, velocity);
  175. }
  176. static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height)
  177. {
  178. uiPtr->uiResize(width, height);
  179. }
  180. #undef uiPtr
  181. };
  182. // -----------------------------------------------------------------------
  183. static OscData gOscData;
  184. static const char* gUiTitle = nullptr;
  185. static UIDssi* globalUI = nullptr;
  186. static void initUiIfNeeded()
  187. {
  188. if (globalUI != nullptr)
  189. return;
  190. if (d_lastUiSampleRate == 0.0)
  191. d_lastUiSampleRate = 44100.0;
  192. globalUI = new UIDssi(gOscData, gUiTitle);
  193. }
  194. // -----------------------------------------------------------------------
  195. int osc_debug_handler(const char* path, const char*, lo_arg**, int, lo_message, void*)
  196. {
  197. d_debug("osc_debug_handler(\"%s\")", path);
  198. return 0;
  199. }
  200. void osc_error_handler(int num, const char* msg, const char* path)
  201. {
  202. d_stderr("osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path);
  203. }
  204. #if DISTRHO_PLUGIN_WANT_STATE
  205. int osc_configure_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*)
  206. {
  207. const char* const key = &argv[0]->s;
  208. const char* const value = &argv[1]->s;
  209. d_debug("osc_configure_handler(\"%s\", \"%s\")", key, value);
  210. initUiIfNeeded();
  211. globalUI->dssiui_configure(key, value);
  212. return 0;
  213. }
  214. #endif
  215. int osc_control_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*)
  216. {
  217. const int32_t rindex = argv[0]->i;
  218. const float value = argv[1]->f;
  219. d_debug("osc_control_handler(%i, %f)", rindex, value);
  220. int32_t index = rindex - DISTRHO_PLUGIN_NUM_INPUTS - DISTRHO_PLUGIN_NUM_OUTPUTS;
  221. // latency
  222. #if DISTRHO_PLUGIN_WANT_LATENCY
  223. index -= 1;
  224. #endif
  225. if (index < 0)
  226. return 0;
  227. initUiIfNeeded();
  228. globalUI->dssiui_control(index, value);
  229. return 0;
  230. }
  231. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  232. int osc_program_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*)
  233. {
  234. const int32_t bank = argv[0]->i;
  235. const int32_t program = argv[1]->f;
  236. d_debug("osc_program_handler(%i, %i)", bank, program);
  237. initUiIfNeeded();
  238. globalUI->dssiui_program(bank, program);
  239. return 0;
  240. }
  241. #endif
  242. int osc_sample_rate_handler(const char*, const char*, lo_arg** argv, int, lo_message, void*)
  243. {
  244. const int32_t sampleRate = argv[0]->i;
  245. d_debug("osc_sample_rate_handler(%i)", sampleRate);
  246. d_lastUiSampleRate = sampleRate;
  247. return 0;
  248. }
  249. int osc_show_handler(const char*, const char*, lo_arg**, int, lo_message, void*)
  250. {
  251. d_debug("osc_show_handler()");
  252. initUiIfNeeded();
  253. globalUI->dssiui_show();
  254. return 0;
  255. }
  256. int osc_hide_handler(const char*, const char*, lo_arg**, int, lo_message, void*)
  257. {
  258. d_debug("osc_hide_handler()");
  259. if (globalUI != nullptr)
  260. globalUI->dssiui_hide();
  261. return 0;
  262. }
  263. int osc_quit_handler(const char*, const char*, lo_arg**, int, lo_message, void*)
  264. {
  265. d_debug("osc_quit_handler()");
  266. if (globalUI != nullptr)
  267. globalUI->dssiui_quit();
  268. return 0;
  269. }
  270. END_NAMESPACE_DISTRHO
  271. // -----------------------------------------------------------------------
  272. int main(int argc, char* argv[])
  273. {
  274. USE_NAMESPACE_DISTRHO
  275. // dummy test mode
  276. if (argc == 1)
  277. {
  278. gUiTitle = "DSSI UI Test";
  279. initUiIfNeeded();
  280. globalUI->dssiui_show();
  281. globalUI->exec();
  282. return 0;
  283. }
  284. if (argc != 5)
  285. {
  286. fprintf(stderr, "Usage: %s <osc-url> <plugin-dll> <plugin-label> <instance-name>\n", argv[0]);
  287. return 1;
  288. }
  289. const char* oscUrl = argv[1];
  290. const char* uiTitle = argv[4];
  291. char* const oscHost = lo_url_get_hostname(oscUrl);
  292. char* const oscPort = lo_url_get_port(oscUrl);
  293. char* const oscPath = lo_url_get_path(oscUrl);
  294. size_t oscPathSize = strlen(oscPath);
  295. lo_address oscAddr = lo_address_new(oscHost, oscPort);
  296. lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, osc_error_handler);
  297. char* const oscServerPath = lo_server_get_url(oscServer);
  298. char pluginPath[strlen(oscServerPath)+oscPathSize];
  299. strcpy(pluginPath, oscServerPath);
  300. strcat(pluginPath, oscPath+1);
  301. #if DISTRHO_PLUGIN_WANT_STATE
  302. char oscPathConfigure[oscPathSize+11];
  303. strcpy(oscPathConfigure, oscPath);
  304. strcat(oscPathConfigure, "/configure");
  305. lo_server_add_method(oscServer, oscPathConfigure, "ss", osc_configure_handler, nullptr);
  306. #endif
  307. char oscPathControl[oscPathSize+9];
  308. strcpy(oscPathControl, oscPath);
  309. strcat(oscPathControl, "/control");
  310. lo_server_add_method(oscServer, oscPathControl, "if", osc_control_handler, nullptr);
  311. d_stdout("oscServerPath: \"%s\"", oscServerPath);
  312. d_stdout("pluginPath: \"%s\"", pluginPath);
  313. d_stdout("oscPathControl: \"%s\"", oscPathControl);
  314. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  315. char oscPathProgram[oscPathSize+9];
  316. strcpy(oscPathProgram, oscPath);
  317. strcat(oscPathProgram, "/program");
  318. lo_server_add_method(oscServer, oscPathProgram, "ii", osc_program_handler, nullptr);
  319. #endif
  320. char oscPathSampleRate[oscPathSize+13];
  321. strcpy(oscPathSampleRate, oscPath);
  322. strcat(oscPathSampleRate, "/sample-rate");
  323. lo_server_add_method(oscServer, oscPathSampleRate, "i", osc_sample_rate_handler, nullptr);
  324. char oscPathShow[oscPathSize+6];
  325. strcpy(oscPathShow, oscPath);
  326. strcat(oscPathShow, "/show");
  327. lo_server_add_method(oscServer, oscPathShow, "", osc_show_handler, nullptr);
  328. char oscPathHide[oscPathSize+6];
  329. strcpy(oscPathHide, oscPath);
  330. strcat(oscPathHide, "/hide");
  331. lo_server_add_method(oscServer, oscPathHide, "", osc_hide_handler, nullptr);
  332. char oscPathQuit[oscPathSize+6];
  333. strcpy(oscPathQuit, oscPath);
  334. strcat(oscPathQuit, "/quit");
  335. lo_server_add_method(oscServer, oscPathQuit, "", osc_quit_handler, nullptr);
  336. lo_server_add_method(oscServer, nullptr, nullptr, osc_debug_handler, nullptr);
  337. gUiTitle = uiTitle;
  338. gOscData.addr = oscAddr;
  339. gOscData.path = oscPath;
  340. gOscData.server = oscServer;
  341. gOscData.send_update(pluginPath);
  342. // wait for init
  343. for (int i=0; i < 100; ++i)
  344. {
  345. lo_server_recv(oscServer);
  346. if (d_lastUiSampleRate != 0.0 || globalUI != nullptr)
  347. break;
  348. d_msleep(50);
  349. }
  350. int ret = 1;
  351. if (d_lastUiSampleRate != 0.0 || globalUI != nullptr)
  352. {
  353. initUiIfNeeded();
  354. globalUI->exec();
  355. delete globalUI;
  356. globalUI = nullptr;
  357. ret = 0;
  358. }
  359. #if DISTRHO_PLUGIN_WANT_STATE
  360. lo_server_del_method(oscServer, oscPathConfigure, "ss");
  361. #endif
  362. lo_server_del_method(oscServer, oscPathControl, "if");
  363. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  364. lo_server_del_method(oscServer, oscPathProgram, "ii");
  365. #endif
  366. lo_server_del_method(oscServer, oscPathSampleRate, "i");
  367. lo_server_del_method(oscServer, oscPathShow, "");
  368. lo_server_del_method(oscServer, oscPathHide, "");
  369. lo_server_del_method(oscServer, oscPathQuit, "");
  370. lo_server_del_method(oscServer, nullptr, nullptr);
  371. std::free(oscServerPath);
  372. std::free(oscHost);
  373. std::free(oscPort);
  374. std::free(oscPath);
  375. lo_address_free(oscAddr);
  376. lo_server_free(oscServer);
  377. return ret;
  378. }