zynaddsubfx

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

main.cpp (24037B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   main.cpp  -  Main file of the synthesizer
      5   Copyright (C) 2002-2005 Nasca Octavian Paul
      6   Copyright (C) 2012-2019 Mark McCurry
      7 
      8   This program is free software; you can redistribute it and/or
      9   modify it under the terms of the GNU General Public License
     10   as published by the Free Software Foundation; either version 2
     11   of the License, or (at your option) any later version.
     12 */
     13 
     14 
     15 #include <iostream>
     16 #include <fstream>
     17 #include <map>
     18 #include <cmath>
     19 #include <cctype>
     20 #include <ctime>
     21 #include <algorithm>
     22 #include <signal.h>
     23 
     24 #ifndef WIN32
     25 #include <err.h>
     26 #endif
     27 #include <unistd.h>
     28 
     29 #include <getopt.h>
     30 
     31 #include <sys/types.h>
     32 #include <signal.h>
     33 
     34 #include <rtosc/rtosc.h>
     35 #include <rtosc/ports.h>
     36 #include "Params/PADnoteParameters.h"
     37 
     38 #include "DSP/FFTwrapper.h"
     39 #include "Misc/MemLocker.h"
     40 #include "Misc/PresetExtractor.h"
     41 #include "Misc/Master.h"
     42 #include "Misc/Part.h"
     43 #include "Misc/Util.h"
     44 #include "zyn-config.h"
     45 #include "zyn-version.h"
     46 
     47 //Nio System
     48 #include "Nio/Nio.h"
     49 #include "Nio/InMgr.h"
     50 
     51 //GUI System
     52 #include "UI/Connection.h"
     53 GUI::ui_handle_t gui;
     54 
     55 #ifdef ZEST_GUI
     56 #ifndef WIN32
     57 #include <sys/wait.h>
     58 #endif
     59 #endif
     60 
     61 //Glue Layer
     62 #include "Misc/MiddleWare.h"
     63 
     64 using namespace std;
     65 using namespace zyn;
     66 
     67 MiddleWare *middleware;
     68 
     69 Master   *master;
     70 int       swaplr = 0; //1 for left-right swapping
     71 bool      compr = false; // enables output audio compressor
     72 
     73 // forward declarations of namespace zyn
     74 namespace zyn
     75 {
     76     extern int Pexitprogram;     //if the UI set this to 1, the program will exit
     77     void dump_json(std::ostream &o,
     78                    const rtosc::Ports &p);
     79 }
     80 
     81 #if LASH
     82 #include "Misc/LASHClient.h"
     83 LASHClient *lash = NULL;
     84 #endif
     85 
     86 #if USE_NSM
     87 #include "UI/NSM.H"
     88 
     89 NSM_Client *nsm = 0;
     90 #endif
     91 
     92 char *instance_name = 0;
     93 
     94 void exitprogram(const Config &config);
     95 
     96 
     97 //cleanup on signaled exit
     98 void sigterm_exit(int /*sig*/)
     99 {
    100     if(Pexitprogram)
    101         exit(1);
    102     Pexitprogram = 1;
    103 }
    104 
    105 /*
    106  * Program initialisation
    107  */
    108 void initprogram(SYNTH_T synth, Config* config, int preferred_port)
    109 {
    110     middleware = new MiddleWare(std::move(synth), config, preferred_port);
    111     master = middleware->spawnMaster();
    112     master->swaplr = swaplr;
    113 
    114     signal(SIGINT, sigterm_exit);
    115     signal(SIGTERM, sigterm_exit);
    116     Nio::init(master->synth, config->cfg.oss_devs, master);
    117 }
    118 
    119 /*
    120  * Program exit
    121  */
    122 void exitprogram(const Config& config)
    123 {
    124     Nio::stop();
    125     config.save();
    126     middleware->removeAutoSave();
    127 
    128     GUI::destroyUi(gui);
    129     delete middleware;
    130 #if LASH
    131     if(lash)
    132         delete lash;
    133 #endif
    134 #if USE_NSM
    135     if(nsm)
    136         delete nsm;
    137 #endif
    138 
    139     FFT_cleanup();
    140 }
    141 
    142 //Windows MIDI OH WHAT A HACK...
    143 #ifdef WIN32
    144 #include <windows.h>
    145 #include <mmsystem.h>
    146 namespace zyn{
    147 extern InMgr  *in;
    148 }
    149 HMIDIIN winmidiinhandle = 0;
    150 
    151 void CALLBACK WinMidiInProc(HMIDIIN hMidiIn,UINT wMsg,DWORD dwInstance,
    152                             DWORD dwParam1,DWORD dwParam2)
    153 {
    154     int midicommand=0;
    155     if (wMsg==MIM_DATA) {
    156         int cmd,par1,par2;
    157         cmd=dwParam1&0xff;
    158         if (cmd==0xfe) return;
    159         par1=(dwParam1>>8)&0xff;
    160         par2=dwParam1>>16;
    161         int cmdchan=cmd&0x0f;
    162         int cmdtype=(cmd>>4)&0x0f;
    163 
    164         int tmp=0;
    165         MidiEvent ev;
    166         switch (cmdtype) {
    167             case(0x8)://noteon
    168                 ev.type = 1;
    169                 ev.num = par1;
    170                 ev.channel = cmdchan;
    171                 ev.value = 0;
    172                 in->putEvent(ev);
    173                 break;
    174             case(0x9)://noteoff
    175                 ev.type = 1;
    176                 ev.num = par1;
    177                 ev.channel = cmdchan;
    178                 ev.value = par2&0xff;
    179                 in->putEvent(ev);
    180                 break;
    181             case(0xb)://controller
    182                 ev.type = 2;
    183                 ev.num = par1;
    184                 ev.channel = cmdchan;
    185                 ev.value = par2&0xff;
    186                 in->putEvent(ev);
    187                 break;
    188             case(0xe)://pitch wheel
    189                 //tmp=(par1+par2*(long int) 128)-8192;
    190                 //winmaster->SetController(cmdchan,C_pitchwheel,tmp);
    191                 break;
    192             default:
    193                 break;
    194         };
    195 
    196     };
    197 };
    198 
    199 void InitWinMidi(int midi)
    200 {
    201 (void)midi;
    202     for(int i=0; i<10; ++i) {
    203         long int res=midiInOpen(&winmidiinhandle,i,(DWORD_PTR)(void*)WinMidiInProc,0,CALLBACK_FUNCTION);
    204         if(res == MMSYSERR_NOERROR) {
    205             res=midiInStart(winmidiinhandle);
    206             printf("[INFO] Starting Windows MIDI At %d with code %d(noerror=%d)\n", i, res, MMSYSERR_NOERROR);
    207             if(res == 0)
    208                 return;
    209         } else
    210             printf("[INFO] No Windows MIDI Device At id %d\n", i);
    211     }
    212 };
    213 
    214 //void StopWinMidi()
    215 //{
    216 //    midiInStop(winmidiinhandle);
    217 //    midiInClose(winmidiinhandle);
    218 //};
    219 #else
    220 void InitWinMidi(int) {}
    221 #endif
    222 
    223 
    224 int main(int argc, char *argv[])
    225 {
    226     SYNTH_T synth;
    227     Config config;
    228     int noui = 0;
    229     cerr
    230     << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others"
    231     << endl;
    232     cerr
    233     << "                Copyright (c) 2009-2019 Mark McCurry [active maintainer]"
    234     << endl;
    235     cerr << "This program is free software (GNU GPL v2 or later) and \n";
    236     cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl;
    237     if(argc == 1)
    238         cerr << "Try 'zynaddsubfx --help' for command-line options." << endl;
    239 
    240     /* Get the settings from the Config*/
    241     synth.samplerate = config.cfg.SampleRate;
    242     synth.buffersize = config.cfg.SoundBufferSize;
    243     synth.oscilsize  = config.cfg.OscilSize;
    244     swaplr = config.cfg.SwapStereo;
    245     compr = config.cfg.AudioOutputCompressor;
    246 
    247     Nio::preferredSampleRate(synth.samplerate);
    248 
    249     synth.alias(); //build aliases
    250 
    251     sprng(time(NULL));
    252 
    253     // for option entries with the 3rd member (flag) pointing here,
    254     // getopt_long*() will return 0 and set this flag to the 4th member (val)
    255     int getopt_flag;
    256 
    257     /* Parse command-line options */
    258     struct option opts[] = {
    259         // options with single char equivalents
    260         {
    261             "load", 2, NULL, 'l'
    262         },
    263         {
    264             "load-instrument", 2, NULL, 'L'
    265         },
    266         {
    267             "midi-learn", 2, NULL, 'M'
    268         },
    269         {
    270             "sample-rate", 2, NULL, 'r'
    271         },
    272         {
    273             "buffer-size", 2, NULL, 'b'
    274         },
    275         {
    276             "oscil-size", 2, NULL, 'o'
    277         },
    278         {
    279             "swap", 2, NULL, 'S'
    280         },
    281         {
    282             "no-gui", 0, NULL, 'U'
    283         },
    284         {
    285             "dummy", 2, NULL, 'Y'
    286         },
    287         {
    288             "help", 2, NULL, 'h'
    289         },
    290         {
    291             "version", 2, NULL, 'v'
    292         },
    293         {
    294             "named", 1, NULL, 'N'
    295         },
    296         {
    297             "auto-connect", 0, NULL, 'a'
    298         },
    299         {
    300             "auto-save", 0, NULL, 'A'
    301         },
    302         {
    303             "pid-in-client-name", 0, NULL, 'p'
    304         },
    305         {
    306             "preferred-port", 1, NULL, 'P',
    307         },
    308         {
    309             "output", 1, NULL, 'O'
    310         },
    311         {
    312             "input", 1, NULL, 'I'
    313         },
    314         {
    315             "exec-after-init", 1, NULL, 'e'
    316         },
    317         {
    318             "dump-oscdoc", 2, NULL, 'd'
    319         },
    320         {
    321             "dump-json-schema", 2, NULL, 'D'
    322         },
    323         // options without single char equivalents ("getopt_flag" compulsory)
    324         {
    325             "list-inputs", no_argument, &getopt_flag, 'i'
    326         },
    327         {
    328             "list-outputs", no_argument, &getopt_flag, 'o'
    329         },
    330         {
    331             0, 0, 0, 0
    332         }
    333     };
    334     opterr = 0;
    335     int option_index = 0, opt;
    336     enum class exit_with_t
    337     {
    338         dont_exit,
    339         help,
    340         version,
    341         list_inputs,
    342         list_outputs
    343     };
    344     exit_with_t exit_with = exit_with_t::dont_exit;
    345     int preferred_port = -1;
    346     int auto_save_interval = 0;
    347     int wmidi = -1;
    348 
    349     string loadfile, loadinstrument, execAfterInit, loadmidilearn;
    350 
    351     while(1) {
    352         int tmp = 0;
    353 
    354         /**\todo check this process for a small memory leak*/
    355         opt = getopt_long(argc,
    356                           argv,
    357                           "l:L:M:r:b:o:I:O:N:e:P:A:d:D:hvapSDUYZ",
    358                           opts,
    359                           &option_index);
    360         char *optarguments = optarg;
    361 
    362 #define GETOP(x) if(optarguments) \
    363         x = optarguments
    364 #define GETOPNUM(x) if(optarguments) \
    365         x = atoi(optarguments)
    366 
    367 
    368         if(opt == -1)
    369             break;
    370 
    371         switch(opt) {
    372             case 'h':
    373                 exit_with = exit_with_t::help;
    374                 break;
    375             case 'v':
    376                 exit_with = exit_with_t::version;
    377                 break;
    378             case 'Y': /* this command a dummy command (has NO effect)
    379                         and is used because I need for NSIS installer
    380                         (NSIS sometimes forces a command line for a
    381                         program, even if I don't need that; eg. when
    382                         I want to add a icon to a shortcut.
    383                      */
    384                 break;
    385             case 'U':
    386                 noui = 1;
    387                 break;
    388             case 'l':
    389                 GETOP(loadfile);
    390                 break;
    391             case 'L':
    392                 GETOP(loadinstrument);
    393                 break;
    394             case 'M':
    395                 GETOP(loadmidilearn);
    396                 break;
    397             case 'r':
    398                 GETOPNUM(synth.samplerate);
    399                 if(synth.samplerate < 4000) {
    400                     cerr << "ERROR:Incorrect sample rate: " << optarguments
    401                          << endl;
    402                     exit(1);
    403                 }
    404                 break;
    405             case 'b':
    406                 GETOPNUM(synth.buffersize);
    407                 if(synth.buffersize < 2) {
    408                     cerr << "ERROR:Incorrect buffer size: " << optarguments
    409                          << endl;
    410                     exit(1);
    411                 }
    412                 break;
    413             case 'o':
    414                 if(optarguments)
    415                     synth.oscilsize = tmp = atoi(optarguments);
    416                 if(synth.oscilsize < MAX_AD_HARMONICS * 2)
    417                     synth.oscilsize = MAX_AD_HARMONICS * 2;
    418                 synth.oscilsize =
    419                     (int) powf(2,
    420                                ceil(logf(synth.oscilsize - 1.0f) / logf(2.0f)));
    421                 if(tmp != synth.oscilsize)
    422                     cerr
    423                     <<
    424                     "synth.oscilsize is wrong (must be 2^n) or too small. Adjusting to "
    425                     << synth.oscilsize << "." << endl;
    426                 break;
    427             case 'S':
    428                 swaplr = 1;
    429                 break;
    430             case 'N':
    431                 Nio::setPostfix(optarguments);
    432                 break;
    433             case 'I':
    434                 if(optarguments)
    435                     Nio::setDefaultSource(optarguments);
    436                 break;
    437             case 'O':
    438                 if(optarguments)
    439                     Nio::setDefaultSink(optarguments);
    440                 break;
    441             case 'a':
    442                 Nio::autoConnect = true;
    443                 break;
    444             case 'p':
    445                 Nio::pidInClientName = true;
    446                 break;
    447             case 'P':
    448                 if(optarguments)
    449                     preferred_port = atoi(optarguments);
    450                 break;
    451             case 'A':
    452                 if(optarguments)
    453                     auto_save_interval = atoi(optarguments);
    454                 break;
    455             case 'e':
    456                 GETOP(execAfterInit);
    457                 break;
    458             case 'd':
    459                 if(optarguments)
    460                 {
    461                     rtosc::OscDocFormatter s;
    462                     ofstream outfile(optarguments);
    463                     s.prog_name    = "ZynAddSubFX";
    464                     s.p            = &MiddleWare::getAllPorts();
    465                     s.uri          = "http://example.com/fake/";
    466                     s.doc_origin   = "http://example.com/fake/url.xml";
    467                     s.author_first = "Mark";
    468                     s.author_last  = "McCurry";
    469                     outfile << s;
    470                 }
    471                 break;
    472             case 'D':
    473                 if(optarguments)
    474                 {
    475                     ofstream outfile(optarguments);
    476                     dump_json(outfile, MiddleWare::getAllPorts());
    477                 }
    478                 break;
    479             case 'Z':
    480                 if(optarguments)
    481                     wmidi = atoi(optarguments);
    482                 break;
    483             case 0: // catch options without single char equivalent
    484                 switch(getopt_flag)
    485                 {
    486                     case 'i':
    487                         exit_with = exit_with_t::list_inputs;
    488                         break;
    489                     case 'o':
    490                         exit_with = exit_with_t::list_outputs;
    491                         break;
    492                 }
    493                 break;
    494             case '?':
    495                 cerr << "ERROR:Bad option or parameter.\n" << endl;
    496                 exit_with = exit_with_t::help;
    497                 break;
    498         }
    499     }
    500 
    501     synth.alias();
    502 
    503     switch (exit_with)
    504     {
    505         case exit_with_t::version:
    506             cout << "Version: " << version << endl;
    507             break;
    508         case exit_with_t::help:
    509             cout << "Usage: zynaddsubfx [OPTION]\n\n"
    510                  << "  -h , --help \t\t\t\t Display command-line help and exit\n"
    511                  << "  -v , --version \t\t\t Display version and exit\n"
    512                  << "  -l file, --load=FILE\t\t\t Loads a .xmz file\n"
    513                  << "  -L file, --load-instrument=FILE\t Loads a .xiz file\n"
    514                  << "  -M file, --midi-learn=FILE\t\t Loads a .xlz file\n"
    515                  << "  -r SR, --sample-rate=SR\t\t Set the sample rate SR\n"
    516                  <<
    517             "  -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n"
    518                  << "  -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n"
    519                  << "  -S , --swap\t\t\t\t Swap Left <--> Right\n"
    520                  <<
    521             "  -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n"
    522                  << "  -N , --named\t\t\t\t Postfix IO Name when possible\n"
    523                  << "  -a , --auto-connect\t\t\t AutoConnect when using JACK\n"
    524                  << "  -A , --auto-save=INTERVAL\t\t Automatically save at interval\n"
    525                  << "\t\t\t\t\t (disabled with 0 interval)\n"
    526                  << "  -p , --pid-in-client-name\t\t Append PID to (JACK) "
    527                     "client name\n"
    528                  << "  -P , --preferred-port\t\t\t Preferred OSC Port\n"
    529                  << "  -O , --output\t\t\t\t Set Output Engine\n"
    530                  << "  -I , --input\t\t\t\t Set Input Engine\n"
    531                  << "  -e , --exec-after-init\t\t Run post-initialization script\n"
    532                  << "  -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n"
    533                  << "  -D , --dump-json-schema=FILE\t\t Dump osc schema (.json) to file\n"
    534                  << endl;
    535             break;
    536         case exit_with_t::list_inputs:
    537         case exit_with_t::list_outputs:
    538         {
    539             Nio::init(synth, config.cfg.oss_devs, nullptr);
    540             auto get_func = (getopt_flag == 'i')
    541                     ? &Nio::getSources
    542                     : &Nio::getSinks;
    543             std::set<std::string> engines = (*get_func)();
    544             for(std::string engine : engines)
    545             {
    546                 std::transform(engine.begin(), engine.end(), engine.begin(),
    547                                ::tolower);
    548                 cout << engine << endl;
    549             }
    550             break;
    551         }
    552         default:
    553             break;
    554     }
    555     if(exit_with != exit_with_t::dont_exit)
    556         return 0;
    557 
    558     cerr.precision(1);
    559     cerr << std::fixed;
    560     cerr << "\nSample Rate = \t\t" << synth.samplerate << endl;
    561     cerr << "Sound Buffer Size = \t" << synth.buffersize << " samples" << endl;
    562     cerr << "Internal latency = \t" << synth.dt() * 1000.0f << " ms" << endl;
    563     cerr << "ADsynth Oscil.Size = \t" << synth.oscilsize << " samples" << endl;
    564 
    565     initprogram(std::move(synth), &config, preferred_port);
    566 
    567     bool altered_master = false;
    568     if(!loadfile.empty()) {
    569         altered_master = true;
    570         const char *filename = loadfile.c_str();
    571         int tmp = master->loadXML(filename);
    572         if(tmp < 0) {
    573             cerr << "ERROR: Could not load master file " << loadfile
    574                  << "." << endl;
    575             exit(1);
    576         }
    577         else {
    578             fast_strcpy(master->last_xmz, filename, XMZ_PATH_MAX);
    579             master->last_xmz[XMZ_PATH_MAX-1] = 0;
    580             master->applyparameters();
    581             cout << "Master file loaded." << endl;
    582         }
    583     }
    584 
    585     if(!loadinstrument.empty()) {
    586         altered_master = true;
    587         int loadtopart = 0;
    588         int tmp = master->part[loadtopart]->loadXMLinstrument(
    589             loadinstrument.c_str());
    590         if(tmp < 0) {
    591             cerr << "ERROR: Could not load instrument file "
    592                  << loadinstrument << '.' << endl;
    593             exit(1);
    594         }
    595         else {
    596             master->part[loadtopart]->applyparameters();
    597             master->part[loadtopart]->initialize_rt();
    598             cout << "Instrument file loaded." << endl;
    599         }
    600     }
    601 
    602     if(!loadmidilearn.empty()) {
    603         char msg[1024];
    604         rtosc_message(msg, sizeof(msg), "/load_xlz",
    605                 "s", loadmidilearn.c_str());
    606         middleware->transmitMsg(msg);
    607     }
    608 
    609     if(altered_master)
    610         middleware->updateResources(master);
    611 
    612 
    613     //Run the Nio system
    614     printf("[INFO] Nio::start()\n");
    615     bool ioGood = Nio::start();
    616 
    617     printf("[INFO] exec-after-init\n");
    618     if(!execAfterInit.empty()) {
    619         cout << "Executing user supplied command: " << execAfterInit << endl;
    620         if(system(execAfterInit.c_str()) == -1)
    621             cerr << "Command Failed..." << endl;
    622     }
    623 
    624     InitWinMidi(wmidi);
    625     master->setAudioCompressor(compr);
    626 
    627     gui = NULL;
    628 
    629     //Capture Startup Responses
    630     printf("[INFO] startup OSC\n");
    631     typedef std::vector<const char *> wait_t;
    632     wait_t msg_waitlist;
    633     middleware->setUiCallback(0, [](void*v,const char*msg) {
    634             wait_t &wait = *(wait_t*)v;
    635             size_t len = rtosc_message_length(msg, -1);
    636             char *copy = new char[len];
    637             memcpy(copy, msg, len);
    638             wait.push_back(copy);
    639             }, &msg_waitlist);
    640 
    641     printf("[INFO] UI calbacks\n");
    642     if(!noui)
    643         gui = GUI::createUi(middleware->spawnUiApi(), &Pexitprogram);
    644     middleware->setUiCallback(0, GUI::raiseUi, gui);
    645     middleware->setIdleCallback([](void*){GUI::tickUi(gui);}, NULL);
    646 
    647     //Replay Startup Responses
    648     printf("[INFO] OSC replay\n");
    649     for(auto msg:msg_waitlist) {
    650         GUI::raiseUi(gui, msg);
    651         delete [] msg;
    652     }
    653 
    654     if(!noui)
    655     {
    656         GUI::raiseUi(gui, "/show",  "i", config.cfg.UserInterfaceMode);
    657         if(!ioGood)
    658             GUI::raiseUi(gui, "/alert", "s",
    659                     "Default IO did not initialize.\nDefaulting to NULL backend.");
    660     }
    661 
    662     printf("[INFO] auto_save setup\n");
    663     if(auto_save_interval > 0) {
    664         int old_save = middleware->checkAutoSave();
    665         if(old_save > 0)
    666             GUI::raiseUi(gui, "/alert-reload", "i", old_save);
    667         middleware->enableAutoSave(auto_save_interval);
    668     }
    669 
    670     //TODO move this stuff into Cmake
    671 #if USE_NSM && defined(WIN32)
    672 #undef USE_NSM
    673 #define USE_NSM 0
    674 #endif
    675 
    676 #if LASH && defined(WIN32)
    677 #undef LASH
    678 #define LASH 0
    679 #endif
    680 
    681 #if USE_NSM
    682     printf("[INFO] NSM Stuff\n");
    683     char *nsm_url = getenv("NSM_URL");
    684 
    685     if(nsm_url) {
    686         nsm = new NSM_Client(middleware);
    687 
    688         if(!nsm->init(nsm_url))
    689             nsm->announce("ZynAddSubFX", ":switch:", argv[0]);
    690         else {
    691             delete nsm;
    692             nsm = NULL;
    693         }
    694     }
    695 #endif
    696 
    697 #if USE_NSM
    698     printf("[INFO] LASH Stuff\n");
    699     if(!nsm)
    700 #endif
    701     {
    702 #if LASH
    703         lash = new LASHClient(&argc, &argv);
    704         GUI::raiseUi(gui, "/session-type", "s", "LASH");
    705 #endif
    706     }
    707 
    708 #ifdef ZEST_GUI
    709 #ifndef WIN32
    710     pid_t gui_pid = 0;
    711 #endif
    712     if(!noui) {
    713         printf("[INFO] Launching Zyn-Fusion...\n");
    714         char *addr = middleware->getServerAddress();
    715 #ifndef WIN32
    716         gui_pid = fork();
    717         if(gui_pid == 0) {
    718             auto exec_fusion = [&addr](const char* path) {
    719                 execlp(path, "zyn-fusion", addr, "--builtin", "--no-hotload",  0); };
    720 #ifndef __APPLE__
    721             if(fusion_dir && *fusion_dir)
    722             {
    723                 std::string fusion = fusion_dir;
    724                 fusion += "/zest";
    725                 if(access(fusion.c_str(), X_OK))
    726                     fputs("Warning: CMake's ZynFusionDir does not contain a"
    727                           "\"zest\" binary - ignoring.", stderr);
    728                 else {
    729                     const char* cur = getenv("LD_LIBRARY_PATH");
    730                     std::string ld_library_path;
    731                     if(cur) {
    732                         ld_library_path += cur;
    733                         ld_library_path += ":";
    734                     }
    735                     ld_library_path += fusion_dir;
    736                     setenv("LD_LIBRARY_PATH", ld_library_path.c_str(), 1);
    737                     exec_fusion(fusion.c_str());
    738                 }
    739             }
    740 #endif
    741             exec_fusion("./zyn-fusion");
    742             exec_fusion("/opt/zyn-fusion/zyn-fusion");
    743             exec_fusion("zyn-fusion");
    744             err(1,"Failed to launch Zyn-Fusion");
    745         }
    746 #else
    747         STARTUPINFO si;
    748         PROCESS_INFORMATION pi;
    749         memset(&si, 0, sizeof(si));
    750         memset(&pi, 0, sizeof(pi));
    751         char *why_windows = strrchr(addr, ':');
    752         char *seriously_why = why_windows + 1;
    753         char start_line[256] = {};
    754         if(why_windows)
    755             snprintf(start_line, sizeof(start_line), "zyn-fusion.exe osc.udp://127.0.0.1:%s", seriously_why);
    756         else {
    757             printf("COULD NOT PARSE <%s>\n", addr);
    758             exit(1);
    759         }
    760         printf("[INFO] starting subprocess via <%s>\n", start_line);
    761         if(!CreateProcess(NULL, start_line,
    762         NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) {
    763             printf("Failed to launch Zyn-Fusion...\n");
    764             exit(1);
    765         }
    766 #endif
    767         free(addr);
    768     }
    769 #endif
    770 
    771     MemLocker mem_locker;
    772     mem_locker.lock();
    773 
    774     printf("[INFO] Main Loop...\n");
    775     bool already_exited = false;
    776     while(Pexitprogram == 0) {
    777 #ifndef WIN32
    778 #if USE_NSM
    779         if(nsm) {
    780             nsm->check();
    781             goto done;
    782         }
    783 #endif
    784 #if LASH
    785         {
    786             string filename;
    787             switch(lash->checkevents(filename)) {
    788                 case LASHClient::Save:
    789                     GUI::raiseUi(gui, "/save-master", "s", filename.c_str());
    790                     lash->confirmevent(LASHClient::Save);
    791                     break;
    792                 case LASHClient::Restore:
    793                     GUI::raiseUi(gui, "/load-master", "s", filename.c_str());
    794                     lash->confirmevent(LASHClient::Restore);
    795                     break;
    796                 case LASHClient::Quit:
    797                     Pexitprogram = 1;
    798                 default:
    799                     break;
    800             }
    801         }
    802 #endif //LASH
    803 
    804 #if USE_NSM
    805 done:
    806 #endif
    807         GUI::tickUi(gui);
    808 #endif // !WIN32
    809         middleware->tick();
    810 #ifdef WIN32
    811         Sleep(1);
    812 #endif
    813 
    814 #ifdef ZEST_GUI
    815 #ifndef WIN32
    816         if(!noui) {
    817             int status = 0;
    818             int ret = waitpid(gui_pid, &status, WNOHANG);
    819             if(ret == gui_pid) {
    820                 Pexitprogram = 1;
    821                 already_exited = true;
    822             }
    823         }
    824 #endif
    825 #endif
    826     } // while !Pexitprogram
    827 
    828     mem_locker.unlock();
    829 
    830 #ifdef ZEST_GUI
    831 #ifndef WIN32
    832     if(!already_exited) {
    833         int ret = kill(gui_pid, SIGHUP);
    834         if (ret == -1) {
    835             err(1, "Failed to terminate Zyn-Fusion...\n");
    836         }
    837     }
    838 #else
    839     (void)already_exited;
    840 #endif
    841 #else
    842     (void)already_exited;
    843 #endif
    844     exitprogram(config);
    845     return 0;
    846 }