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:
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;