zynaddsubfx

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

PortChecker.cpp (8205B)


      1 #include <cassert>
      2 #include <queue>
      3 #include <thread>
      4 #include <mutex>
      5 #include <iostream>
      6 #include <ctime>
      7 #include <unistd.h>
      8 #include <rtosc/thread-link.h>
      9 #include <rtosc/rtosc-time.h>
     10 #include <rtosc/port-checker.h>
     11 #include <liblo-server.h>  // from rtosc's test directory
     12 
     13 #include "../Misc/Master.h"
     14 #include "../Misc/MiddleWare.h"
     15 #include "../UI/NSM.H"
     16 #include "rtosc/arg-val.h"
     17 
     18 // for linking purposes only:
     19 NSM_Client *nsm = 0;
     20 zyn::MiddleWare *middleware = 0;
     21 
     22 char *instance_name=(char*)"";
     23 
     24 //#define DEBUG_PORT_CHECKER
     25 
     26 //! non-network implementation of port_checker::server
     27 //! @note This is all in one thread - thus, no mutexes etc
     28 class direct_server : public rtosc::port_checker::server
     29 {
     30     zyn::MiddleWare* const mw;
     31     const std::size_t gui_id;
     32     std::queue<std::vector<char>> received;  // inbox for MW's replies
     33 
     34 public:
     35     //!< UI callback, will queue message in `received`
     36     void on_recv(const char* message)
     37     {
     38         int len = rtosc_message_length(message, -1);
     39         std::vector<char> buf(len);
     40         memcpy(buf.data(), message, len);
     41         received.push(std::move(buf));
     42     }
     43 
     44     //!< Fetch exactly 1 msg from `received`, may call server::handle_recv
     45     void fetch_received()
     46     {
     47         if(!received.empty())
     48         {
     49             const char* message = received.front().data();
     50             if(waiting && exp_paths_n_args[0].size())
     51             {
     52                 _replied_path = 0;
     53                 for(std::vector<const char*>* exp_strs = exp_paths_n_args; exp_strs->size();
     54                      ++exp_strs, ++_replied_path)
     55                     if(!strcmp((*exp_strs)[0], message))
     56                     {
     57 #ifdef DEBUG_PORT_CHECKER
     58                         std::cout << "on_recv: match:" << std::endl;
     59 #endif
     60                         int len = rtosc_message_length(message, -1);
     61 
     62                         *last_buffer = std::vector<char>(len);
     63                         memcpy(last_buffer->data(), message, len);
     64 
     65                         unsigned nargs = rtosc_narguments(message);
     66                         std::vector<char> types(nargs);
     67                         for (unsigned i = 0; i < nargs; ++i)
     68                             types[i] = rtosc_type(message, i);
     69 
     70                         server::handle_recv(nargs, types.data(), exp_strs);
     71                         break;
     72                     }
     73             }
     74             received.pop();
     75         }
     76     }
     77 
     78     //! Let port checker send a message to MW
     79     bool send_msg(const char* address,
     80                   size_t nargs, const rtosc_arg_val_t* args) override
     81     {
     82         char buf[8192];
     83         int len = rtosc_avmessage(buf, sizeof(buf), address, nargs, args);
     84         if(len <= 0)
     85             throw std::runtime_error("Could not serialize message");
     86         mw->transmitMsgGui(gui_id, buf);
     87         return true;
     88     }
     89 
     90     //! Let port checker wait until matching message was replied
     91     bool _wait_for_reply(std::vector<char>* buffer,
     92                          std::vector<rtosc_arg_val_t> * args,
     93                          int n0, int n1) override
     94     {
     95         (void)n0;
     96         // TODO: most of this function is common to liblo_server
     97         // => refactor into portchecker
     98         assert(n1);
     99         exp_paths_n_args[n1].clear();
    100 
    101         last_args = args;
    102         last_buffer = buffer;
    103 
    104         // allow up to 1000 recv calls = 1s
    105         // if there's no reply at all after 0.5 seconds, abort
    106         const int timeout_initial = timeout_msecs;
    107         int tries_left = 1000, timeout = timeout_initial;
    108         while(tries_left-->1 && timeout-->1 && waiting) // waiting is set in "on_recv"
    109         {
    110             mw->tick();
    111             usleep(1000);
    112             if(received.size()) {
    113                 fetch_received();
    114                 timeout = timeout_initial;
    115             }
    116             // message will be dispatched to the server's callback
    117             // server::handle_recv will unset `waiting` if a "good" message was found
    118         }
    119         waiting = true; // prepare for next round
    120 
    121         return tries_left && timeout;
    122     }
    123 
    124     void vinit(const char* ) override {}  // nothing to do here
    125 
    126     direct_server(std::size_t gui_id, int timeout_msecs, zyn::MiddleWare* mw)
    127         : server(timeout_msecs)
    128         , mw(mw)
    129         , gui_id(gui_id) {}
    130 };
    131 
    132 class PortChecker
    133 {
    134         void _masterChangedCallback(zyn::Master* m)
    135         {
    136             master = m;
    137             master->setMasterChangedCallback(
    138                 [](void* p, zyn::Master* m) {
    139                     ((PortChecker*)p)->_masterChangedCallback(m); },
    140                 this);
    141         }
    142 
    143         void setUp() {
    144             // this might be set to true in the future
    145             // when saving will work better
    146             config.cfg.SaveFullXml = false;
    147 
    148             synth = new zyn::SYNTH_T;
    149             synth->buffersize = 256;
    150             synth->samplerate = 48000;
    151             synth->oscilsize = 256;
    152             synth->alias();
    153 
    154             mw = new zyn::MiddleWare(std::move(*synth), &config);
    155             mw->setUiCallback(0,
    156                 [](void* p, const char* msg) {
    157                     ((PortChecker*)p)->uiCallback0(msg); },
    158                 this);
    159             mw->setUiCallback(1,
    160                 [](void* p, const char* msg) {
    161                     ((PortChecker*)p)->uiCallback1(msg); },
    162                 this);
    163             _masterChangedCallback(mw->spawnMaster());
    164             realtime = nullptr;
    165         }
    166 
    167         void tearDown() {
    168 #ifdef SAVE_OSC_DEBUG
    169             printf("Master at the end: %p\n", master);
    170 #endif
    171             delete mw;
    172             delete synth;
    173         }
    174 
    175         void uiCallback0(const char* msg)
    176         {
    177             sender->on_recv(msg);
    178         }
    179 
    180         void uiCallback1(const char* msg)
    181         {
    182             other->on_recv(msg);
    183         }
    184 
    185     public:
    186         PortChecker() { setUp(); }
    187         ~PortChecker() { tearDown(); }
    188 
    189         int run()
    190         {
    191             assert(mw);
    192 
    193             bool ok;
    194             try {
    195                 int timeout_msecs = 50;
    196                 sender = new direct_server(0u, timeout_msecs, mw);
    197                 other = new direct_server(1u, timeout_msecs, mw);
    198                 rtosc::port_checker pc(sender, other);
    199                 ok = pc(mw->getServerPort());
    200 
    201                 if(!pc.print_sanity_checks())
    202                     ok = false;
    203                 pc.print_evaluation();
    204                 if(pc.errors_found())
    205                     ok = false;
    206                 pc.print_not_affected();
    207                 pc.print_skipped();
    208                 pc.print_statistics();
    209 
    210             } catch(const std::exception& e) {
    211                 std::cerr << "**Error caught**: " << e.what() << std::endl;
    212                 ok = false;
    213             }
    214 
    215             int res = ok ? EXIT_SUCCESS : EXIT_FAILURE;
    216             return res;
    217         }
    218 
    219         void start_realtime(void)
    220         {
    221             do_exit = false;
    222 
    223             realtime = new std::thread([this](){
    224                 while(!do_exit)
    225                 {
    226                     if(master->uToB->hasNext()) {
    227                         const char *msg = master->uToB->read();
    228 #ifdef ZYN_PORT_CHECKER_DEBUG
    229                         printf("Master %p: handling <%s>\n", master, msg);
    230 #endif
    231                         master->applyOscEvent(msg, false);
    232                     }
    233                     else {
    234                         // RT has no incoming message?
    235                         if(do_exit)
    236                             break;
    237                         usleep(500);
    238                     }
    239                     master->last_ack = master->last_beat;
    240                 }});
    241         }
    242 
    243         void stop_realtime(void)
    244         {
    245             do_exit = true;
    246 
    247             realtime->join();
    248             delete realtime;
    249             realtime = NULL;
    250         }
    251 
    252     private:
    253         zyn::Config config;
    254         zyn::SYNTH_T* synth;
    255         zyn::Master* master = NULL;
    256         zyn::MiddleWare* mw;
    257         std::thread* realtime;
    258         bool do_exit;
    259         direct_server* sender, * other;
    260 };
    261 
    262 int main()
    263 {
    264     PortChecker test;
    265     test.start_realtime();
    266     int res = test.run();
    267     test.stop_realtime();
    268     std::cerr << "Summary: " << ((res == EXIT_SUCCESS) ? "SUCCESS" : "FAILURE")
    269               << std::endl;
    270     return res;
    271 }
    272 
    273