changeset 44:a98ef4c2616b

Make base64/text selectable when serialising process and feature blocks; add base64 version as an output format for vampipe-convert; make VamPipePluginLibrary switch to returning base64 encoding as soon as it is fed any as input
author Chris Cannam <c.cannam@qmul.ac.uk>
date Thu, 08 Sep 2016 15:27:48 +0100
parents 62c17e143aba
children 7668fbdcfb15
files json/VampJson.h utilities/json-to-capnp.cpp utilities/vampipe-convert.cpp
diffstat 3 files changed, 97 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/json/VampJson.h	Wed Aug 24 10:50:40 2016 +0100
+++ b/json/VampJson.h	Thu Sep 08 15:27:48 2016 +0100
@@ -59,6 +59,12 @@
 class VampJson
 {
 public:
+    /// Serialisation format for arrays of floats (process input and feature values)
+    enum class BufferSerialisation {
+        Text,  // default JSON serialisation of values in array form
+        Base64 // base64-encoded string of the raw data as packed ieee 32-bit floats
+    };
+    
     class Failure : virtual public std::runtime_error {
     public:
         Failure(std::string s) : runtime_error(s) { }
@@ -308,11 +314,12 @@
     }
 
     static json11::Json
-    fromFeature(const Vamp::Plugin::Feature &f, bool asText) {
+    fromFeature(const Vamp::Plugin::Feature &f,
+                BufferSerialisation serialisation) {
 
         json11::Json::object jo;
         if (f.values.size() > 0) {
-            if (asText) {
+            if (serialisation == BufferSerialisation::Text) {
                 jo["values"] = json11::Json::array(f.values.begin(),
                                                    f.values.end());
             } else {
@@ -333,7 +340,8 @@
     }
 
     static Vamp::Plugin::Feature
-    toFeature(json11::Json j) {
+    toFeature(json11::Json j,
+              BufferSerialisation &serialisation) {
 
         Vamp::Plugin::Feature f;
         if (!j.is_object()) {
@@ -349,23 +357,26 @@
         }
         if (j["b64values"].is_string()) {
             f.values = toFloatBuffer(j["b64values"].string_value());
+            serialisation = BufferSerialisation::Base64;
         } else if (j["values"].is_array()) {
             for (auto v : j["values"].array_items()) {
                 f.values.push_back(v.number_value());
             }
+            serialisation = BufferSerialisation::Text;
         }
         f.label = j["label"].string_value();
         return f;
     }
 
     static json11::Json
-    fromFeatureSet(const Vamp::Plugin::FeatureSet &fs, bool asText) {
+    fromFeatureSet(const Vamp::Plugin::FeatureSet &fs,
+                   BufferSerialisation serialisation) {
 
         json11::Json::object jo;
         for (const auto &fsi : fs) {
             std::vector<json11::Json> fj;
             for (const Vamp::Plugin::Feature &f: fsi.second) {
-                fj.push_back(fromFeature(f, asText));
+                fj.push_back(fromFeature(f, serialisation));
             }
             std::stringstream sstr;
             sstr << fsi.first;
@@ -376,20 +387,21 @@
     }
 
     static Vamp::Plugin::FeatureList
-    toFeatureList(json11::Json j) {
+    toFeatureList(json11::Json j,
+                  BufferSerialisation &serialisation) {
 
         Vamp::Plugin::FeatureList fl;
         if (!j.is_array()) {
             throw Failure("array expected for feature list");
         }
         for (const json11::Json &fj : j.array_items()) {
-            fl.push_back(toFeature(fj));
+            fl.push_back(toFeature(fj, serialisation));
         }
         return fl;
     }
 
     static Vamp::Plugin::FeatureSet
-    toFeatureSet(json11::Json j) {
+    toFeatureSet(json11::Json j, BufferSerialisation &serialisation) {
 
         Vamp::Plugin::FeatureSet fs;
         if (!j.is_object()) {
@@ -402,7 +414,7 @@
             if (n < 0 || fs.find(n) != fs.end() || count < nstr.size()) {
                 throw Failure("invalid or duplicate numerical index for output");
             }
-            fs[n] = toFeatureList(entry.second);
+            fs[n] = toFeatureList(entry.second, serialisation);
         }
         return fs;
     }
@@ -802,7 +814,8 @@
 
     static json11::Json
     fromProcessRequest(const Vamp::HostExt::ProcessRequest &r,
-                       const PluginHandleMapper &mapper) {
+                       const PluginHandleMapper &mapper,
+                       BufferSerialisation serialisation) {
 
         json11::Json::object jo;
         jo["pluginHandle"] = mapper.pluginToHandle(r.plugin);
@@ -813,8 +826,13 @@
         json11::Json::array chans;
         for (size_t i = 0; i < r.inputBuffers.size(); ++i) {
             json11::Json::object c;
-            c["b64values"] = fromFloatBuffer(r.inputBuffers[i].data(),
-                                             r.inputBuffers[i].size());
+            if (serialisation == BufferSerialisation::Text) {
+                c["values"] = json11::Json::array(r.inputBuffers[i].begin(),
+                                                  r.inputBuffers[i].end());
+            } else {
+                c["b64values"] = fromFloatBuffer(r.inputBuffers[i].data(),
+                                                 r.inputBuffers[i].size());
+            }
             chans.push_back(c);
         }
         io["inputBuffers"] = chans;
@@ -824,7 +842,9 @@
     }
 
     static Vamp::HostExt::ProcessRequest
-    toProcessRequest(json11::Json j, const PluginHandleMapper &mapper) {
+    toProcessRequest(json11::Json j,
+                     const PluginHandleMapper &mapper,
+                     BufferSerialisation &serialisation) {
 
         std::string err;
 
@@ -848,15 +868,20 @@
         r.timestamp = toRealTime(input["timestamp"]);
 
         for (auto a: input["inputBuffers"].array_items()) {
+
             if (a["b64values"].is_string()) {
-                r.inputBuffers.push_back(toFloatBuffer
-                                         (a["b64values"].string_value()));
+                std::vector<float> buf = toFloatBuffer(a["b64values"].string_value());
+                r.inputBuffers.push_back(buf);
+                serialisation = BufferSerialisation::Base64;
+
             } else if (a["values"].is_array()) {
                 std::vector<float> buf;
                 for (auto v : a["values"].array_items()) {
                     buf.push_back(v.number_value());
                 }
                 r.inputBuffers.push_back(buf);
+                serialisation = BufferSerialisation::Text;
+
             } else {
                 throw Failure("expected values or b64values in inputBuffers object");
             }
@@ -937,22 +962,24 @@
     
     static json11::Json
     fromVampRequest_Process(const Vamp::HostExt::ProcessRequest &req,
-                            const PluginHandleMapper &mapper) {
+                            const PluginHandleMapper &mapper,
+                            BufferSerialisation serialisation) {
 
         json11::Json::object jo;
         jo["type"] = "process";
-        jo["content"] = fromProcessRequest(req, mapper);
+        jo["content"] = fromProcessRequest(req, mapper, serialisation);
         return json11::Json(jo);
     }    
 
     static json11::Json
-    fromVampResponse_Process(const Vamp::HostExt::ProcessResponse &resp) {
+    fromVampResponse_Process(const Vamp::HostExt::ProcessResponse &resp,
+                             BufferSerialisation serialisation) {
         
         json11::Json::object jo;
         jo["type"] = "process";
         jo["success"] = true;
         jo["errorText"] = "";
-        jo["content"] = fromFeatureSet(resp.features, true);
+        jo["content"] = fromFeatureSet(resp.features, serialisation);
         return json11::Json(jo);
     }
     
@@ -969,13 +996,14 @@
     }    
     
     static json11::Json
-    fromVampResponse_Finish(const Vamp::HostExt::ProcessResponse &resp) {
+    fromVampResponse_Finish(const Vamp::HostExt::ProcessResponse &resp,
+                            BufferSerialisation serialisation) {
 
         json11::Json::object jo;
         jo["type"] = "finish";
         jo["success"] = true;
         jo["errorText"] = "";
-        jo["content"] = fromFeatureSet(resp.features, true);
+        jo["content"] = fromFeatureSet(resp.features, serialisation);
         return json11::Json(jo);
     }
 
@@ -1039,7 +1067,7 @@
                           type + "\"");
 	}
     }
-    
+
     static void
     toVampRequest_List(json11::Json j) {
         
@@ -1093,18 +1121,19 @@
     }
     
     static Vamp::HostExt::ProcessRequest
-    toVampRequest_Process(json11::Json j, const PluginHandleMapper &mapper) {
+    toVampRequest_Process(json11::Json j, const PluginHandleMapper &mapper,
+                          BufferSerialisation &serialisation) {
         
         checkTypeField(j, "process");
-        return toProcessRequest(j["content"], mapper);
+        return toProcessRequest(j["content"], mapper, serialisation);
     }
     
     static Vamp::HostExt::ProcessResponse
-    toVampResponse_Process(json11::Json j) {
+    toVampResponse_Process(json11::Json j, BufferSerialisation &serialisation) {
         
         Vamp::HostExt::ProcessResponse resp;
         if (successful(j)) {
-            resp.features = toFeatureSet(j["content"]);
+            resp.features = toFeatureSet(j["content"], serialisation);
         }
         return resp;
     }
@@ -1117,11 +1146,11 @@
     }
     
     static Vamp::HostExt::ProcessResponse
-    toVampResponse_Finish(json11::Json j) {
+    toVampResponse_Finish(json11::Json j, BufferSerialisation &serialisation) {
         
         Vamp::HostExt::ProcessResponse resp;
         if (successful(j)) {
-            resp.features = toFeatureSet(j["content"]);
+            resp.features = toFeatureSet(j["content"], serialisation);
         }
         return resp;
     }
--- a/utilities/json-to-capnp.cpp	Wed Aug 24 10:50:40 2016 +0100
+++ b/utilities/json-to-capnp.cpp	Thu Sep 08 15:27:48 2016 +0100
@@ -45,6 +45,7 @@
     Json j = json_input(input);
     string type = j["type"].string_value();
     Json payload = j["payload"];
+    VampJson::BufferSerialisation serialisation;
 
     if (type == "configurationrequest") {
 	auto req = message.initRoot<ConfigurationRequest>();
@@ -60,12 +61,12 @@
     } else if (type == "feature") {
 	auto f = message.initRoot<Feature>();
 	VampnProto::buildFeature
-	    (f, VampJson::toFeature(payload));
+	    (f, VampJson::toFeature(payload, serialisation));
 
     } else if (type == "featureset") {
 	auto fs = message.initRoot<FeatureSet>();
 	VampnProto::buildFeatureSet
-	    (fs, VampJson::toFeatureSet(payload));
+	    (fs, VampJson::toFeatureSet(payload, serialisation));
 
     } else if (type == "loadrequest") {
 	auto req = message.initRoot<LoadRequest>();
@@ -102,7 +103,7 @@
 	auto p = message.initRoot<ProcessRequest>();
 	PreservingPluginHandleMapper mapper;
 	VampnProto::buildProcessRequest
-	    (p, VampJson::toProcessRequest(payload, mapper), mapper);
+	    (p, VampJson::toProcessRequest(payload, mapper, serialisation), mapper);
 
     } else if (type == "realtime") {
 	auto b = message.initRoot<RealTime>();
--- a/utilities/vampipe-convert.cpp	Wed Aug 24 10:50:40 2016 +0100
+++ b/utilities/vampipe-convert.cpp	Thu Sep 08 15:27:48 2016 +0100
@@ -24,11 +24,14 @@
 	"       <informat>: the format to read from stdin\n"
 	"           (\"json\" or \"capnp\", default is \"json\")\n"
 	"       <outformat>: the format to convert to and write to stdout\n"
-	"           (\"json\" or \"capnp\", default is \"json\")\n"
-	"       request|response: whether to expect Vamp request or response messages\n\n"
+	"           (\"json\", \"json-b64\" or \"capnp\", default is \"json\")\n"
+	"       request|response: whether messages are Vamp request or response type\n\n"
 	"If <informat> and <outformat> differ, convert from <informat> to <outformat>.\n"
 	"If <informat> and <outformat> are the same, just check validity of incoming\n"
-	"messages and pass them to output.\n\n";
+	"messages and pass them to output.\n\n"
+	"Specifying \"json-b64\" as output format forces base64 encoding for process and\n"
+	"feature blocks, unlike the \"json\" output format which uses text encoding.\n"
+	"The \"json\" input format accepts either.\n\n";
 
     exit(2);
 }
@@ -93,6 +96,7 @@
     Json j = convertRequestJson(input);
 
     rr.type = VampJson::getRequestResponseType(j);
+    VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
 
     switch (rr.type) {
 
@@ -106,7 +110,7 @@
 	rr.configurationRequest = VampJson::toVampRequest_Configure(j, mapper);
 	break;
     case RRType::Process:
-	rr.processRequest = VampJson::toVampRequest_Process(j, mapper);
+	rr.processRequest = VampJson::toVampRequest_Process(j, mapper, serialisation);
 	break;
     case RRType::Finish:
 	rr.finishPlugin = VampJson::toVampRequest_Finish(j, mapper);
@@ -119,7 +123,7 @@
 }
 
 void
-writeRequestJson(RequestOrResponse &rr)
+writeRequestJson(RequestOrResponse &rr, bool useBase64)
 {
     Json j;
 
@@ -135,7 +139,11 @@
 	j = VampJson::fromVampRequest_Configure(rr.configurationRequest, mapper);
 	break;
     case RRType::Process:
-	j = VampJson::fromVampRequest_Process(rr.processRequest, mapper);
+	j = VampJson::fromVampRequest_Process
+	    (rr.processRequest, mapper,
+	     useBase64 ?
+	     VampJson::BufferSerialisation::Base64 :
+	     VampJson::BufferSerialisation::Text);
 	break;
     case RRType::Finish:
 	j = VampJson::fromVampRequest_Finish(rr.finishPlugin, mapper);
@@ -162,6 +170,7 @@
     Json j = convertResponseJson(input);
 
     rr.type = VampJson::getRequestResponseType(j);
+    VampJson::BufferSerialisation serialisation = VampJson::BufferSerialisation::Text;
 
     switch (rr.type) {
 
@@ -175,10 +184,10 @@
 	rr.configurationResponse = VampJson::toVampResponse_Configure(j);
 	break;
     case RRType::Process: 
-	rr.processResponse = VampJson::toVampResponse_Process(j);
+	rr.processResponse = VampJson::toVampResponse_Process(j, serialisation);
 	break;
     case RRType::Finish:
-	rr.finishResponse = VampJson::toVampResponse_Finish(j);
+	rr.finishResponse = VampJson::toVampResponse_Finish(j, serialisation);
 	break;
     case RRType::NotValid:
 	break;
@@ -188,7 +197,7 @@
 }
 
 void
-writeResponseJson(RequestOrResponse &rr)
+writeResponseJson(RequestOrResponse &rr, bool useBase64)
 {
     Json j;
 
@@ -204,10 +213,18 @@
 	j = VampJson::fromVampResponse_Configure(rr.configurationResponse);
 	break;
     case RRType::Process:
-	j = VampJson::fromVampResponse_Process(rr.processResponse);
+	j = VampJson::fromVampResponse_Process
+	    (rr.processResponse,
+	     useBase64 ?
+	     VampJson::BufferSerialisation::Base64 :
+	     VampJson::BufferSerialisation::Text);
 	break;
     case RRType::Finish:
-	j = VampJson::fromVampResponse_Finish(rr.finishResponse);
+	j = VampJson::fromVampResponse_Finish
+	    (rr.finishResponse,
+	     useBase64 ?
+	     VampJson::BufferSerialisation::Base64 :
+	     VampJson::BufferSerialisation::Text);
 	break;
     case RRType::NotValid:
 	break;
@@ -393,9 +410,15 @@
 {
     if (format == "json") {
 	if (rr.direction == RequestOrResponse::Request) {
-	    writeRequestJson(rr);
+	    writeRequestJson(rr, false);
 	} else {
-	    writeResponseJson(rr);
+	    writeResponseJson(rr, false);
+	}
+    } else if (format == "json-b64") {
+	if (rr.direction == RequestOrResponse::Request) {
+	    writeRequestJson(rr, true);
+	} else {
+	    writeResponseJson(rr, true);
 	}
     } else if (format == "capnp") {
 	if (rr.direction == RequestOrResponse::Request) {