DISTRHO Plugin Framework
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.

512 lines
13KB

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