zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

Connection.cpp (16136B)


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