zynaddsubfx

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

commit 5b3824f7e94c1ef02b181352c753e57e4461fa5d
parent cd179cf215ba9b9d61847435e5a4a68890704f49
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date:   Sun,  8 Nov 2020 09:26:17 +0100

MW: path-search/save_osc: include non-RT ports

Split MW snoop ports into non-RT ports and other snoop ports, and then

* Let path-search iterate over non-RT ports, too (to let port-checker
  see those ports)
* Let save_osc save non-RT ports, too (to build complete savefiles)

Diffstat:
Msrc/Misc/Master.cpp | 85++++---------------------------------------------------------------------------
Msrc/Misc/Master.h | 25+++++++++----------------
Msrc/Misc/MiddleWare.cpp | 135++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 141 insertions(+), 104 deletions(-)

diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp @@ -1777,88 +1777,11 @@ char* Master::getXMLData() // this is being called as a "read only op" directly by the MiddleWare thread; // note that the Master itself is frozen -int Master::saveOSC(const char *filename, master_dispatcher_t* dispatcher, - Master* master2) +std::string Master::saveOSC(std::string savefile) { - std::string savefile = rtosc::save_to_file(ports, this, - "ZynAddSubFX", - version_in_rtosc_fmt()); - - // load the savefile string into another master to compare the results - // between the original and the savefile-loaded master - // this requires a temporary master switch - dispatcher->updateMaster(master2); - - int rval = master2->loadOSCFromStr(savefile.c_str(), dispatcher); - - // The above call is done by this thread (i.e. the MiddleWare thread), but - // it sends messages to master2 in order to load the values - // We need to wait until savefile has been loaded into master2 - int i; - for(i = 0; i < 20 && master2->uToB->hasNext(); ++i) - os_usleep(50000); - if(i >= 20) // >= 1 second? - { - // Master failed to fetch its messages - rval = -1; - } - printf("Saved in less than %d ms.\n", 50*i); - - dispatcher->updateMaster(this); - - if(rval < 0) - { - std::cerr << "invalid savefile (or a backend error)!" << std::endl; - std::cerr << "complete savefile:" << std::endl; - std::cerr << savefile << std::endl; - std::cerr << "first entry that could not be parsed:" << std::endl; - - for(int i = -rval + 1; savefile[i]; ++i) - if(savefile[i] == '\n') - { - savefile.resize(i); - break; - } - std::cerr << (savefile.c_str() - rval) << std::endl; - - rval = -1; - } - else - { - char* xml = getXMLData(), - * xml2 = master2->getXMLData(); - - rval = strcmp(xml, xml2) ? -1 : 0; - - if(rval == 0) - { - if(filename && *filename) - { - std::ofstream ofs(filename); - ofs << savefile; - } - else { - std::cout << "The savefile content follows" << std::endl; - std::cout << "---->8----" << std::endl; - std::cout << savefile << std::endl; - std::cout << "---->8----" << std::endl; - } - } - else - { - std::cout << savefile << std::endl; - std::cerr << "Can not write OSC savefile!! (see tmp1.txt and tmp2.txt)" - << std::endl; - std::ofstream tmp1("tmp1.txt"), tmp2("tmp2.txt"); - tmp1 << xml; - tmp2 << xml2; - rval = -1; - } - - free(xml); - free(xml2); - } - return rval; + return rtosc::save_to_file(ports, this, + nullptr, version_in_rtosc_fmt(), // both unused + savefile); } int Master::loadOSCFromStr(const char *file_content, diff --git a/src/Misc/Master.h b/src/Misc/Master.h @@ -75,17 +75,8 @@ class Master * @return 0 for ok or -1 if there is an error*/ int loadXML(const char *filename); - /**Save all settings to an OSC file (as specified by RT OSC) - * When the function returned, the OSC file has been either saved or - * an error occurred. - * @param filename File to save to or NULL (useful for testing) - * @param dispatcher Message dispatcher and modifier - * @param master2 An empty master dummy where the savefile will be - * loaded to and compared with the current master - * @return 0 for ok or <0 if there is an error*/ - int saveOSC(const char *filename, - class master_dispatcher_t* dispatcher, - Master* master2); + /**Append all settings to an OSC savefile (as specified by RT OSC)*/ + std::string saveOSC(std::string savefile); /**loads all settings from an OSC file (as specified by RT OSC) * @param dispatcher Message dispatcher and modifier * @return 0 for ok or <0 if there is an error*/ @@ -228,6 +219,13 @@ class Master constexpr static std::size_t dnd_buffer_size = 1024; char dnd_buffer[dnd_buffer_size] = {0}; + //Return XML data as string. Must be freed. + char* getXMLData(); + //Load OSC from OSC savefile + //Returns 0 if OK, <0 in case of failure + int loadOSCFromStr(const char *file_content, + rtosc::savefile_dispatcher_t* dispatcher); + private: std::atomic<bool> run_osc_in_use = { false }; @@ -245,11 +243,6 @@ class Master void(*mastercb)(void*,Master*); void* mastercb_ptr; - //Return XML data as string. Must be freed. - char* getXMLData(); - //Used by loadOSC and saveOSC - int loadOSCFromStr(const char *file_content, - rtosc::savefile_dispatcher_t* dispatcher); //! apply an OSC event with a DataObj parameter //! @note This may be called by MiddleWare if we are offline //! (in this case, the param offline is true) diff --git a/src/Misc/MiddleWare.cpp b/src/Misc/MiddleWare.cpp @@ -95,6 +95,11 @@ static void liblo_error_cb(int i, const char *m, const char *loc) fprintf(stderr, "liblo :-( %d-%s@%s\n",i,m,loc); } +// we need to access those earlier +// bad style? +static const rtosc::MergePorts& getParameterPorts(); +static const rtosc::Ports& getNonRtParamPorts(); + static int handler_function(const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data) { @@ -122,7 +127,7 @@ static int handler_function(const char *path, const char *types, lo_arg **argv, { char reply_buffer[1024*20]; std::size_t length = - rtosc::path_search(Master::ports, buffer, 128, + rtosc::path_search(getParameterPorts(), buffer, 128, reply_buffer, sizeof(reply_buffer)); if(length) { lo_message msg = lo_message_deserialise((void*)reply_buffer, @@ -343,6 +348,10 @@ struct NonRtObjStore void handleOscil(const char *msg, rtosc::RtData &d) { string obj_rl(d.message, msg); + assert(d.message); + assert(msg); + assert(msg >= d.message); + assert(msg - d.message < 256); void *osc = get(obj_rl); if(osc) { @@ -646,7 +655,11 @@ public: return 0; } - int saveMaster(const char *filename, bool osc_format = false) + // Save all possible parameters + // In user language, this is called "saving a master", but we + // are saving parameters owned by Master and by MiddleWare + // Return 0 if OK, <0 if not + int saveParams(const char *filename, bool osc_format = false) { int res; if(osc_format) @@ -665,9 +678,98 @@ public: master->copyMasterCbTo(&master2); master2.frozenState = true; - doReadOnlyOp([this,filename,&dispatcher,&master2,&res](){ - res = master->saveOSC(filename, &dispatcher, - &master2);}); + std::string savefile; + rtosc_version m_version = + { + (unsigned char) version.get_major(), + (unsigned char) version.get_minor(), + (unsigned char) version.get_revision() + }; + savefile = rtosc::save_to_file(getNonRtParamPorts(), this, "ZynAddSubFX", m_version); + savefile += '\n'; + + doReadOnlyOp([this,filename,&dispatcher,&master2,&savefile,&res]() + { + savefile = master->saveOSC(savefile); + + // load the savefile string into another master to compare the results + // between the original and the savefile-loaded master + // this requires a temporary master switch + Master* old_master = master; + dispatcher.updateMaster(&master2); + + res = master2.loadOSCFromStr(savefile.c_str(), &dispatcher); + // TODO: compare MiddleWare, too? + + // The above call is done by this thread (i.e. the MiddleWare thread), but + // it sends messages to master2 in order to load the values + // We need to wait until savefile has been loaded into master2 + int i; + for(i = 0; i < 20 && master2.uToB->hasNext(); ++i) + os_usleep(50000); + if(i >= 20) // >= 1 second? + { + // Master failed to fetch its messages + res = -1; + } + printf("Saved in less than %d ms.\n", 50*i); + + dispatcher.updateMaster(old_master); + + if(res < 0) + { + std::cerr << "invalid savefile (or a backend error)!" << std::endl; + std::cerr << "complete savefile:" << std::endl; + std::cerr << savefile << std::endl; + std::cerr << "first entry that could not be parsed:" << std::endl; + + for(int i = -res + 1; savefile[i]; ++i) + if(savefile[i] == '\n') + { + savefile.resize(i); + break; + } + std::cerr << (savefile.c_str() - res) << std::endl; + + res = -1; + } + else + { + char* xml = master->getXMLData(), + * xml2 = master2.getXMLData(); + // TODO: below here can be moved out of read only op + + res = strcmp(xml, xml2) ? -1 : 0; + + if(res == 0) + { + if(filename && *filename) + { + std::ofstream ofs(filename); + ofs << savefile; + } + else { + std::cout << "The savefile content follows" << std::endl; + std::cout << "---->8----" << std::endl; + std::cout << savefile << std::endl; + std::cout << "---->8----" << std::endl; + } + } + else + { + std::cout << savefile << std::endl; + std::cerr << "Can not write OSC savefile!! (see tmp1.txt and tmp2.txt)" + << std::endl; + std::ofstream tmp1("tmp1.txt"), tmp2("tmp2.txt"); + tmp1 << xml; + tmp2 << xml2; + res = -1; + } + + free(xml); + free(xml2); + } + }); } else // xml format { @@ -1289,7 +1391,7 @@ void save_cb(const char *msg, RtData &d) if(rtosc_narguments(msg) > 1) request_time = rtosc_argument(msg, 1).t; - int res = impl.saveMaster(file.c_str(), osc_format); + int res = impl.saveParams(file.c_str(), osc_format); d.broadcast(d.loc, (res == 0) ? "stT" : "stF", file.c_str(), request_time); } @@ -1322,7 +1424,7 @@ void gcc_10_1_0_is_dumb(const std::vector<std::string> &files, * BASE/part#/kit#/padpars/prepare * BASE/part#/kit#/padpars/oscil/\* */ -static rtosc::Ports middwareSnoopPorts = { +static rtosc::Ports nonRtParamPorts = { {"part#" STRINGIFY(NUM_MIDI_PARTS) "/kit#" STRINGIFY(NUM_KIT_ITEMS) "/adpars/VoicePar#" STRINGIFY(NUM_VOICES) "/OscilSmp/", 0, &OscilGen::non_realtime_ports, @@ -1340,6 +1442,9 @@ static rtosc::Ports middwareSnoopPorts = { rBegin impl.obj_store.handlePad(chomp(chomp(chomp(msg))), d); rEnd}, +}; + +static rtosc::Ports middwareSnoopPortsWithoutNonRtParams = { {"bank/", 0, &bankPorts, rBegin; d.obj = &impl.master->bank; @@ -1631,6 +1736,22 @@ static rtosc::Ports middwareSnoopPorts = { } }; +static rtosc::MergePorts middwareSnoopPorts = +{ + &nonRtParamPorts, + &middwareSnoopPortsWithoutNonRtParams +}; + +static rtosc::MergePorts parameterPorts = +{ + // order is important: params should be queried on Master first + // (because MiddleWare often just redirects, hiding the metadata) + &Master::ports, + &nonRtParamPorts +}; +const rtosc::MergePorts& getParameterPorts() { return parameterPorts; } +const rtosc::Ports& getNonRtParamPorts() { return nonRtParamPorts; } + static rtosc::Ports middlewareReplyPorts = { {"echo:ss", 0, 0, rBegin;