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.

558 lines
17KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. guimain.cpp - Main file of synthesizer GUI
  4. Copyright (C) 2015 Mark McCurry
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of version 2 of the GNU General Public License
  7. as published by the Free Software Foundation.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License (version 2 or later) for more details.
  12. You should have received a copy of the GNU General Public License (version 2)
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include <lo/lo.h>
  17. #include <string>
  18. //GUI System
  19. #include "Connection.h"
  20. #include "NSM.H"
  21. #include <sys/stat.h>
  22. GUI::ui_handle_t gui = 0;
  23. #if USE_NSM
  24. NSM_Client *nsm = 0;
  25. #endif
  26. lo_server server;
  27. std::string sendtourl;
  28. /*
  29. * Program exit
  30. */
  31. void exitprogram()
  32. {
  33. GUI::destroyUi(gui);
  34. }
  35. bool fileexists(const char *filename)
  36. {
  37. struct stat tmp;
  38. int result = stat(filename, &tmp);
  39. if(result >= 0)
  40. return true;
  41. return false;
  42. }
  43. int Pexitprogram = 0;
  44. #include "Connection.h"
  45. #include "Fl_Osc_Interface.h"
  46. #include "../globals.h"
  47. #include <map>
  48. #include <cassert>
  49. #include <rtosc/rtosc.h>
  50. #include <rtosc/ports.h>
  51. #include <FL/Fl.H>
  52. #include "Fl_Osc_Tree.H"
  53. #include "common.H"
  54. #include "MasterUI.h"
  55. #ifdef NTK_GUI
  56. #include <FL/Fl_Shared_Image.H>
  57. #include <FL/Fl_Tiled_Image.H>
  58. #include <FL/Fl_Dial.H>
  59. #include <err.h>
  60. #endif // NTK_GUI
  61. #ifndef NO_UI
  62. #include "Fl_Osc_Widget.H"
  63. #endif
  64. using namespace GUI;
  65. class MasterUI *ui=0;
  66. #ifdef NTK_GUI
  67. static Fl_Tiled_Image *module_backdrop;
  68. #endif
  69. int undo_redo_handler(int)
  70. {
  71. const bool undo_ = Fl::event_ctrl() && Fl::event_key() == 'z';
  72. const bool redo = Fl::event_ctrl() && Fl::event_key() == 'r';
  73. if(undo_)
  74. ui->osc->write("/undo", "");
  75. else if(redo)
  76. ui->osc->write("/redo", "");
  77. return undo_ || redo;
  78. }
  79. void
  80. set_module_parameters ( Fl_Widget *o )
  81. {
  82. #ifdef NTK_GUI
  83. o->box( FL_DOWN_FRAME );
  84. o->align( o->align() | FL_ALIGN_IMAGE_BACKDROP );
  85. o->color( FL_BLACK );
  86. o->image( module_backdrop );
  87. o->labeltype( FL_SHADOW_LABEL );
  88. if(o->parent()) {
  89. o->parent()->labeltype(FL_NO_LABEL);
  90. o->parent()->box(FL_NO_BOX);
  91. }
  92. #else
  93. o->box( FL_PLASTIC_UP_BOX );
  94. o->color( FL_CYAN );
  95. o->labeltype( FL_EMBOSSED_LABEL );
  96. #endif
  97. }
  98. ui_handle_t GUI::createUi(Fl_Osc_Interface *osc, void *exit)
  99. {
  100. #ifdef NTK_GUI
  101. fl_register_images();
  102. Fl_Dial::default_style(Fl_Dial::PIXMAP_DIAL);
  103. #ifdef CARLA_VERSION_STRING
  104. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/knob.png"))
  105. Fl_Dial::default_image(img);
  106. #else
  107. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/knob.png"))
  108. Fl_Dial::default_image(img);
  109. #endif
  110. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/knob.png"))
  111. Fl_Dial::default_image(img);
  112. else
  113. errx(1, "ERROR: Cannot find pixmaps/knob.png");
  114. #ifdef CARLA_VERSION_STRING
  115. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/window_backdrop.png"))
  116. Fl::scheme_bg(new Fl_Tiled_Image(img));
  117. #else
  118. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/window_backdrop.png"))
  119. Fl::scheme_bg(new Fl_Tiled_Image(img));
  120. #endif
  121. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/window_backdrop.png"))
  122. Fl::scheme_bg(new Fl_Tiled_Image(img));
  123. else
  124. errx(1, "ERROR: Cannot find pixmaps/window_backdrop.png");
  125. #ifdef CARLA_VERSION_STRING
  126. if(Fl_Shared_Image *img = Fl_Shared_Image::get(gUiPixmapPath + "/module_backdrop.png"))
  127. module_backdrop = new Fl_Tiled_Image(img);
  128. #else
  129. if(Fl_Shared_Image *img = Fl_Shared_Image::get(PIXMAP_PATH "/module_backdrop.png"))
  130. module_backdrop = new Fl_Tiled_Image(img);
  131. #endif
  132. else if(Fl_Shared_Image *img = Fl_Shared_Image::get(SOURCE_DIR "/pixmaps/module_backdrop.png"))
  133. module_backdrop = new Fl_Tiled_Image(img);
  134. else
  135. errx(1, "ERROR: Cannot find pixmaps/module_backdrop");
  136. Fl::background(50, 50, 50);
  137. Fl::background2(70, 70, 70);
  138. Fl::foreground(255, 255, 255);
  139. #endif
  140. //Fl_Window *midi_win = new Fl_Double_Window(400, 400, "Midi connections");
  141. //Fl_Osc_Tree *tree = new Fl_Osc_Tree(0,0,400,400);
  142. //midi_win->resizable(tree);
  143. //tree->root_ports = &Master::ports;
  144. //tree->osc = osc;
  145. //midi_win->show();
  146. Fl::add_handler(undo_redo_handler);
  147. return (void*) (ui = new MasterUI((int*)exit, osc));
  148. }
  149. void GUI::destroyUi(ui_handle_t ui)
  150. {
  151. delete static_cast<MasterUI*>(ui);
  152. }
  153. #define BEGIN(x) {x,":non-realtime\0",NULL,[](const char *m, rtosc::RtData d){ \
  154. MasterUI *ui = static_cast<MasterUI*>(d.obj); \
  155. rtosc_arg_t a0 = {0}, a1 = {0}; \
  156. if(rtosc_narguments(m) > 0) \
  157. a0 = rtosc_argument(m,0); \
  158. if(rtosc_narguments(m) > 1) \
  159. a1 = rtosc_argument(m,1); \
  160. (void)ui;(void)a1;(void)a0;
  161. #define END }},
  162. struct uiPorts {
  163. static rtosc::Ports ports;
  164. };
  165. //DSL based ports
  166. rtosc::Ports uiPorts::ports = {
  167. BEGIN("show:i") {
  168. ui->showUI(a0.i);
  169. } END
  170. BEGIN("alert:s") {
  171. fl_alert("%s",a0.s);
  172. } END
  173. BEGIN("session-type:s") {
  174. if(strcmp(a0.s,"LASH"))
  175. return;
  176. ui->sm_indicator1->value(1);
  177. ui->sm_indicator2->value(1);
  178. ui->sm_indicator1->tooltip("LASH");
  179. ui->sm_indicator2->tooltip("LASH");
  180. } END
  181. BEGIN("save-master:s") {
  182. ui->do_save_master(a0.s);
  183. } END
  184. BEGIN("load-master:s") {
  185. ui->do_load_master(a0.s);
  186. } END
  187. BEGIN("vu-meter:bb") {
  188. #ifdef DEBUG
  189. printf("Vu meter handler...\n");
  190. #endif
  191. if(a0.b.len == sizeof(vuData) &&
  192. a1.b.len == sizeof(float)*NUM_MIDI_PARTS) {
  193. #ifdef DEBUG
  194. printf("Normal behavior...\n");
  195. #endif
  196. //Refresh the primary VU meters
  197. ui->simplemastervu->update((vuData*)a0.b.data);
  198. ui->mastervu->update((vuData*)a0.b.data);
  199. float *partvu = (float*)a1.b.data;
  200. for(int i=0; i<NUM_MIDI_PARTS; ++i)
  201. ui->panellistitem[i]->partvu->update(partvu[i]);
  202. }
  203. } END
  204. BEGIN("close-ui") {
  205. ui->close();
  206. } END
  207. };
  208. void GUI::raiseUi(ui_handle_t gui, const char *message)
  209. {
  210. if(!gui)
  211. return;
  212. MasterUI *mui = (MasterUI*)gui;
  213. mui->osc->tryLink(message);
  214. #ifdef DEBUG
  215. printf("got message for UI '%s:%s'\n", message, rtosc_argument_string(message));
  216. #endif
  217. char buffer[1024];
  218. memset(buffer, 0, sizeof(buffer));
  219. rtosc::RtData d;
  220. d.loc = buffer;
  221. d.loc_size = 1024;
  222. d.obj = gui;
  223. uiPorts::ports.dispatch(message+1, d);
  224. }
  225. void GUI::raiseUi(ui_handle_t gui, const char *dest, const char *args, ...)
  226. {
  227. char buffer[1024];
  228. va_list va;
  229. va_start(va,args);
  230. if(rtosc_vmessage(buffer,1024,dest,args,va))
  231. raiseUi(gui, buffer);
  232. va_end(va);
  233. }
  234. void GUI::tickUi(ui_handle_t)
  235. {
  236. Fl::wait(0.02f);
  237. }
  238. /******************************************************************************
  239. * OSC Interface For User Interface *
  240. * *
  241. * This is a largely out of date section of code *
  242. * Most type specific write methods are no longer used *
  243. * See UI/Fl_Osc_* to see what is actually used in this interface *
  244. ******************************************************************************/
  245. class UI_Interface:public Fl_Osc_Interface
  246. {
  247. public:
  248. UI_Interface()
  249. {}
  250. void transmitMsg(const char *path, const char *args, ...)
  251. {
  252. char buffer[1024];
  253. va_list va;
  254. va_start(va,args);
  255. if(rtosc_vmessage(buffer,1024,path,args,va))
  256. transmitMsg(buffer);
  257. else
  258. fprintf(stderr, "Error in transmitMsg(...)\n");
  259. va_end(va);
  260. }
  261. void transmitMsg(const char *rtmsg)
  262. {
  263. //Send to known url
  264. if(!sendtourl.empty()) {
  265. lo_message msg = lo_message_deserialise((void*)rtmsg,
  266. rtosc_message_length(rtmsg, rtosc_message_length(rtmsg,-1)), NULL);
  267. lo_address addr = lo_address_new_from_url(sendtourl.c_str());
  268. lo_send_message(addr, rtmsg, msg);
  269. }
  270. }
  271. void requestValue(string s) override
  272. {
  273. //printf("Request Value '%s'\n", s.c_str());
  274. assert(s!="/Psysefxvol-1/part0");
  275. //Fl_Osc_Interface::requestValue(s);
  276. /*
  277. if(impl->activeUrl() != "GUI") {
  278. impl->transmitMsg("/echo", "ss", "OSC_URL", "GUI");
  279. impl->activeUrl("GUI");
  280. }*/
  281. transmitMsg(s.c_str(),"");
  282. }
  283. void write(string s, const char *args, ...) override
  284. {
  285. char buffer[4096];
  286. va_list va;
  287. va_start(va, args);
  288. rtosc_vmessage(buffer, sizeof(buffer), s.c_str(), args, va);
  289. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  290. ////fprintf(stderr, ".");
  291. //fprintf(stderr, "write(%s:%s)\n", s.c_str(), args);
  292. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  293. transmitMsg(buffer);
  294. va_end(va);
  295. }
  296. void writeRaw(const char *msg) override
  297. {
  298. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  299. ////fprintf(stderr, ".");
  300. //fprintf(stderr, "rawWrite(%s:%s)\n", msg, rtosc_argument_string(msg));
  301. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  302. transmitMsg(msg);
  303. }
  304. void writeValue(string s, string ss) override
  305. {
  306. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  307. //fprintf(stderr, "writevalue<string>(%s,%s)\n", s.c_str(),ss.c_str());
  308. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  309. transmitMsg(s.c_str(), "s", ss.c_str());
  310. }
  311. void writeValue(string s, char c) override
  312. {
  313. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  314. //fprintf(stderr, "writevalue<char>(%s,%d)\n", s.c_str(),c);
  315. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  316. transmitMsg(s.c_str(), "c", c);
  317. }
  318. void writeValue(string s, float f) override
  319. {
  320. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 4 + 30, 0 + 40);
  321. //fprintf(stderr, "writevalue<float>(%s,%f)\n", s.c_str(),f);
  322. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  323. transmitMsg(s.c_str(), "f", f);
  324. }
  325. void createLink(string s, class Fl_Osc_Widget*w) override
  326. {
  327. assert(s.length() != 0);
  328. Fl_Osc_Interface::createLink(s,w);
  329. assert(!strstr(s.c_str(), "/part0/kit-1"));
  330. map.insert(std::pair<string,Fl_Osc_Widget*>(s,w));
  331. }
  332. void renameLink(string old, string newer, Fl_Osc_Widget *w) override
  333. {
  334. fprintf(stdout, "renameLink('%s','%s',%p)\n",
  335. old.c_str(), newer.c_str(), w);
  336. removeLink(old, w);
  337. createLink(newer, w);
  338. }
  339. void removeLink(string s, class Fl_Osc_Widget*w) override
  340. {
  341. for(auto i = map.begin(); i != map.end(); ++i) {
  342. if(i->first == s && i->second == w) {
  343. map.erase(i);
  344. break;
  345. }
  346. }
  347. //printf("[%d] removing '%s' (%p)...\n", map.size(), s.c_str(), w);
  348. }
  349. virtual void removeLink(class Fl_Osc_Widget *w) override
  350. {
  351. bool processing = true;
  352. while(processing)
  353. {
  354. //Verify Iterator invalidation sillyness
  355. processing = false;//Exit if no new elements are found
  356. for(auto i = map.begin(); i != map.end(); ++i) {
  357. if(i->second == w) {
  358. //printf("[%d] removing '%s' (%p)...\n", map.size()-1,
  359. // i->first.c_str(), w);
  360. map.erase(i);
  361. processing = true;
  362. break;
  363. }
  364. }
  365. }
  366. }
  367. //A very simplistic implementation of a UI agnostic refresh method
  368. virtual void damage(const char *path) override
  369. {
  370. //printf("\n\nDamage(\"%s\")\n", path);
  371. for(auto pair:map) {
  372. if(strstr(pair.first.c_str(), path)) {
  373. auto *tmp = dynamic_cast<Fl_Widget*>(pair.second);
  374. //if(tmp)
  375. // printf("%x, %d %d [%s]\n", (int)pair.second, tmp->visible_r(), tmp->visible(), pair.first.c_str());
  376. //else
  377. // printf("%x, (NULL)[%s]\n", (int)pair.second,pair.first.c_str());
  378. if(!tmp || tmp->visible_r())
  379. pair.second->update();
  380. }
  381. }
  382. }
  383. void tryLink(const char *msg) override
  384. {
  385. //DEBUG
  386. //if(strcmp(msg, "/vu-meter"))//Ignore repeated message
  387. // printf("trying the link for a '%s'<%s>\n", msg, rtosc_argument_string(msg));
  388. const char *handle = rindex(msg,'/');
  389. if(handle)
  390. ++handle;
  391. int found_count = 0;
  392. auto range = map.equal_range(msg);
  393. for(auto itr = range.first; itr != range.second; ++itr) {
  394. auto widget = itr->second;
  395. found_count++;
  396. const char *arg_str = rtosc_argument_string(msg);
  397. //Always provide the raw message
  398. widget->OSC_raw(msg);
  399. if(!strcmp(arg_str, "b")) {
  400. widget->OSC_value(rtosc_argument(msg,0).b.len,
  401. rtosc_argument(msg,0).b.data,
  402. handle);
  403. } else if(!strcmp(arg_str, "c")) {
  404. widget->OSC_value((char)rtosc_argument(msg,0).i,
  405. handle);
  406. } else if(!strcmp(arg_str, "s")) {
  407. widget->OSC_value((const char*)rtosc_argument(msg,0).s,
  408. handle);
  409. } else if(!strcmp(arg_str, "i")) {
  410. widget->OSC_value((int)rtosc_argument(msg,0).i,
  411. handle);
  412. } else if(!strcmp(arg_str, "f")) {
  413. widget->OSC_value((float)rtosc_argument(msg,0).f,
  414. handle);
  415. } else if(!strcmp(arg_str, "T") || !strcmp(arg_str, "F")) {
  416. widget->OSC_value((bool)rtosc_argument(msg,0).T, handle);
  417. }
  418. }
  419. if(found_count == 0
  420. && strcmp(msg, "/vu-meter")
  421. && strcmp(msg, "undo_change")
  422. && !strstr(msg, "parameter")
  423. && !strstr(msg, "Prespoint")) {
  424. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 1, 7 + 30, 0 + 40);
  425. //fprintf(stderr, "Unknown widget '%s'\n", msg);
  426. //fprintf(stderr, "%c[%d;%d;%dm", 0x1B, 0, 7 + 30, 0 + 40);
  427. }
  428. };
  429. void dumpLookupTable(void)
  430. {
  431. if(!map.empty()) {
  432. printf("Leaked controls:\n");
  433. for(auto i = map.begin(); i != map.end(); ++i) {
  434. printf("Known control '%s' (%p)...\n", i->first.c_str(), i->second);
  435. }
  436. }
  437. }
  438. private:
  439. std::multimap<string,Fl_Osc_Widget*> map;
  440. };
  441. Fl_Osc_Interface *GUI::genOscInterface(MiddleWare *)
  442. {
  443. return new UI_Interface();
  444. }
  445. static void liblo_error_cb(int i, const char *m, const char *loc)
  446. {
  447. fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc);
  448. }
  449. static int handler_function(const char *path, const char *types, lo_arg **argv,
  450. int argc, lo_message msg, void *user_data)
  451. {
  452. (void) types;
  453. (void) argv;
  454. (void) argc;
  455. (void) user_data;
  456. char buffer[8192];
  457. memset(buffer, 0, sizeof(buffer));
  458. size_t size = sizeof(buffer);
  459. assert(lo_message_length(msg, path) <= sizeof(buffer));
  460. lo_message_serialise(msg, path, buffer, &size);
  461. assert(size <= sizeof(buffer));
  462. raiseUi(gui, buffer);
  463. return 0;
  464. }
  465. #ifndef CARLA_VERSION_STRING
  466. int main(int argc, char *argv[])
  467. {
  468. //Startup Liblo Link
  469. if(argc == 2) {
  470. server = lo_server_new_with_proto(NULL, LO_UDP, liblo_error_cb);
  471. lo_server_add_method(server, NULL, NULL, handler_function, 0);
  472. sendtourl = argv[1];
  473. }
  474. gui = GUI::createUi(new UI_Interface(), &Pexitprogram);
  475. GUI::raiseUi(gui, "/show", "i", 1);
  476. while(Pexitprogram == 0) {
  477. if(server)
  478. while(lo_server_recv_noblock(server, 0));
  479. GUI::tickUi(gui);
  480. }
  481. exitprogram();
  482. return 0;
  483. }
  484. #endif