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.

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