zynaddsubfx

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

commit f273fa2ffee434accd0dafe2c2c333d867015b89
parent 07b9a3adf4612bc368f803aa90a987a6e18c6ece
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date:   Sun, 26 Jan 2025 02:46:19 +0100

Use port-checker directly without network

This removes all liblo code from PortChecker and rather lets it
communicate to MW by owning two in-process "UIs". This commit should
make the `PortChecker` test

1. run on macOS
2. more stable
3. faster

Diffstat:
Msrc/Tests/PortChecker.cpp | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 117 insertions(+), 20 deletions(-)

diff --git a/src/Tests/PortChecker.cpp b/src/Tests/PortChecker.cpp @@ -1,4 +1,5 @@ #include <cassert> +#include <queue> #include <thread> #include <mutex> #include <iostream> @@ -12,6 +13,7 @@ #include "../Misc/Master.h" #include "../Misc/MiddleWare.h" #include "../UI/NSM.H" +#include "rtosc/arg-val.h" // for linking purposes only: NSM_Client *nsm = 0; @@ -19,6 +21,114 @@ zyn::MiddleWare *middleware = 0; char *instance_name=(char*)""; +//#define DEBUG_PORT_CHECKER + +//! non-network implementation of port_checker::server +//! @note This is all in one thread - thus, no mutexes etc +class direct_server : public rtosc::port_checker::server +{ + zyn::MiddleWare* const mw; + const int gui_id; + std::queue<std::vector<char>> received; // inbox for MW's replies + +public: + //!< UI callback, will queue message in `received` + void on_recv(const char* message) + { + int len = rtosc_message_length(message, -1); + std::vector<char> buf(len); + memcpy(buf.data(), message, len); + received.push(std::move(buf)); + } + + //!< Fetch exactly 1 msg from `received`, may call server::handle_recv + void fetch_received() + { + if(!received.empty()) + { + const char* message = received.front().data(); + if(waiting && exp_paths_n_args[0].size()) + { + _replied_path = 0; + for(std::vector<const char*>* exp_strs = exp_paths_n_args; exp_strs->size(); + ++exp_strs, ++_replied_path) + if(!strcmp((*exp_strs)[0], message)) + { +#ifdef DEBUG_PORT_CHECKER + std::cout << "on_recv: match:" << std::endl; +#endif + int len = rtosc_message_length(message, -1); + + *last_buffer = std::vector<char>(len); + memcpy(last_buffer->data(), message, len); + + unsigned nargs = rtosc_narguments(message); + std::vector<char> types(nargs); + for (unsigned i = 0; i < nargs; ++i) + types[i] = rtosc_type(message, i); + + server::handle_recv(nargs, types.data(), exp_strs); + break; + } + } + received.pop(); + } + } + + //! Let port checker send a message to MW + bool send_msg(const char* address, + size_t nargs, const rtosc_arg_val_t* args) override + { + char buf[8192]; + int len = rtosc_avmessage(buf, sizeof(buf), address, nargs, args); + if(len <= 0) + throw std::runtime_error("Could not serialize message"); + mw->transmitMsgGui(gui_id, buf); + return true; + } + + //! Let port checker wait until matching message was replied + bool _wait_for_reply(std::vector<char>* buffer, + std::vector<rtosc_arg_val_t> * args, + int n0, int n1) override + { + (void)n0; + // TODO: most of this function is common to liblo_server + // => refactor into portchecker + assert(n1); + exp_paths_n_args[n1].clear(); + + last_args = args; + last_buffer = buffer; + + // allow up to 1000 recv calls = 1s + // if there's no reply at all after 0.5 seconds, abort + const int timeout_initial = timeout_msecs; + int tries_left = 1000, timeout = timeout_initial; + while(tries_left-->1 && timeout-->1 && waiting) // waiting is set in "on_recv" + { + mw->tick(); + usleep(1000); + if(received.size()) { + fetch_received(); + timeout = timeout_initial; + } + // message will be dispatched to the server's callback + // server::handle_recv will unset `waiting` if a "good" message was found + } + waiting = true; // prepare for next round + + return tries_left && timeout; + } + + void vinit(const char* ) override {} // nothing to do here + + direct_server(int gui_id, int timeout_msecs, zyn::MiddleWare* mw) + : server(timeout_msecs) + , mw(mw) + , gui_id(gui_id) {} +}; + class PortChecker { void _masterChangedCallback(zyn::Master* m) @@ -63,22 +173,12 @@ class PortChecker void uiCallback0(const char* msg) { - (void)msg; + sender->on_recv(msg); } - + void uiCallback1(const char* msg) { - (void)msg; - } - - bool exit_mw = false; - void run_mw() - { - while(!exit_mw) { - if(mw) - mw->tick(); - usleep(20000); - } + other->on_recv(msg); } public: @@ -89,13 +189,12 @@ class PortChecker { assert(mw); - std::thread mwthread(&PortChecker::run_mw, this); - bool ok; try { int timeout_msecs = 50; - rtosc::liblo_server sender(timeout_msecs), other(timeout_msecs); - rtosc::port_checker pc(&sender, &other); + sender = new direct_server(0, timeout_msecs, mw); + other = new direct_server(1, timeout_msecs, mw); + rtosc::port_checker pc(sender, other); ok = pc(mw->getServerPort()); if(!pc.print_sanity_checks()) @@ -113,12 +212,9 @@ class PortChecker } int res = ok ? EXIT_SUCCESS : EXIT_FAILURE; - exit_mw = true; - mwthread.join(); return res; } - void start_realtime(void) { do_exit = false; @@ -159,6 +255,7 @@ class PortChecker zyn::MiddleWare* mw; std::thread* realtime; bool do_exit; + direct_server* sender, * other; }; int main()