Mercurial > hg > piper-cpp
changeset 24:533ca5ca3404
More on reading and writing json messages
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Mon, 23 May 2016 16:09:25 +0100 |
parents | d678cd00e593 |
children | 5b9690d18241 |
files | capnproto/VampnProto.h capnproto/vamp.capnp json/VampJson.h utilities/json-cli.cpp utilities/vampipe-convert.cpp |
diffstat | 5 files changed, 334 insertions(+), 156 deletions(-) [+] |
line wrap: on
line diff
--- a/capnproto/VampnProto.h Fri May 20 18:05:02 2016 +0100 +++ b/capnproto/VampnProto.h Mon May 23 16:09:25 2016 +0100 @@ -658,10 +658,11 @@ const std::vector<Vamp::HostExt::PluginStaticData> &d) { b.setSuccess(errorText == ""); b.setErrorText(errorText); - auto r = b.getResponse().initList(d.size()); + auto r = b.getResponse().initList(); + auto p = r.initPlugins(d.size()); for (size_t i = 0; i < d.size(); ++i) { - auto rd = r[i]; - buildPluginStaticData(rd, d[i]); + auto pd = p[i]; + buildPluginStaticData(pd, d[i]); } }
--- a/capnproto/vamp.capnp Fri May 20 18:05:02 2016 +0100 +++ b/capnproto/vamp.capnp Mon May 23 16:09:25 2016 +0100 @@ -114,6 +114,10 @@ blockSize @4 :Int32; } +struct ListResponse { + plugins @0 :List(PluginStaticData); +} + struct LoadRequest { pluginKey @0 :Text; inputSampleRate @1 :Float32; @@ -162,7 +166,7 @@ success @0 :Bool; errorText @1 :Text = ""; response :union { - list @2 :List(PluginStaticData); + list @2 :ListResponse; load @3 :LoadResponse; configure @4 :ConfigurationResponse; process @5 :ProcessResponse;
--- a/json/VampJson.h Fri May 20 18:05:02 2016 +0100 +++ b/json/VampJson.h Mon May 23 16:09:25 2016 +0100 @@ -870,6 +870,7 @@ const std::vector<Vamp::HostExt::PluginStaticData> &d) { json11::Json::object jo; + jo["type"] = "list"; jo["success"] = (errorText == ""); jo["errorText"] = errorText; @@ -877,7 +878,10 @@ for (const auto &a: d) { arr.push_back(fromPluginStaticData(a)); } - jo["response"] = arr; + json11::Json::object po; + po["plugins"] = arr; + + jo["content"] = po; return json11::Json(jo); } @@ -895,9 +899,10 @@ PluginHandleMapper &mapper) { json11::Json::object jo; + jo["type"] = "load"; jo["success"] = (resp.plugin != 0); jo["errorText"] = ""; - jo["response"] = fromLoadResponse(resp, mapper); + jo["content"] = fromLoadResponse(resp, mapper); return json11::Json(jo); } @@ -915,9 +920,10 @@ fromVampResponse_Configure(const Vamp::HostExt::ConfigurationResponse &resp) { json11::Json::object jo; + jo["type"] = "configure"; jo["success"] = (!resp.outputs.empty()); jo["errorText"] = ""; - jo["response"] = fromConfigurationResponse(resp); + jo["content"] = fromConfigurationResponse(resp); return json11::Json(jo); } @@ -935,17 +941,22 @@ fromVampResponse_Process(const Vamp::HostExt::ProcessResponse &resp) { json11::Json::object jo; + jo["type"] = "process"; jo["success"] = true; jo["errorText"] = ""; - jo["response"] = fromFeatureSet(resp.features); + jo["content"] = fromFeatureSet(resp.features); return json11::Json(jo); } static json11::Json - fromVampRequest_Finish() { + fromVampRequest_Finish(Vamp::Plugin *p, + PluginHandleMapper &mapper) { json11::Json::object jo; jo["type"] = "finish"; + json11::Json::object fo; + fo["pluginHandle"] = mapper.pluginToHandle(p); + jo["content"] = fo; return json11::Json(jo); } @@ -953,11 +964,119 @@ fromVampResponse_Finish(const Vamp::HostExt::ProcessResponse &resp) { json11::Json::object jo; + jo["type"] = "finish"; jo["success"] = true; jo["errorText"] = ""; - jo["response"] = fromFeatureSet(resp.features); + jo["content"] = fromFeatureSet(resp.features); return json11::Json(jo); } + +private: // go private briefly for a couple of helper functions + + static void + checkTypeField(json11::Json j, std::string expected) { + if (!j["type"].is_string()) { + throw Failure("string expected for type"); + } + if (j["type"].string_value() != expected) { + throw Failure("expected value \"" + expected + "\" for type"); + } + } + + static bool + successful(json11::Json j) { + if (!j["success"].is_bool()) { + throw Failure("bool expected for success"); + } + return j["success"].bool_value(); + } + +public: + static void + toVampRequest_List(json11::Json j) { + + checkTypeField(j, "list"); + } + + static std::vector<Vamp::HostExt::PluginStaticData> + toVampResponse_List(json11::Json j) { + + std::vector<Vamp::HostExt::PluginStaticData> arr; + if (successful(j)) { + for (const auto &a: j["content"]["plugins"].array_items()) { + arr.push_back(toPluginStaticData(a)); + } + } + return arr; + } + + static Vamp::HostExt::LoadRequest + toVampRequest_Load(json11::Json j) { + + checkTypeField(j, "load"); + return toLoadRequest(j["content"]); + } + + static Vamp::HostExt::LoadResponse + toVampResponse_Load(json11::Json j, PluginHandleMapper &mapper) { + + Vamp::HostExt::LoadResponse resp; + if (successful(j)) { + resp = toLoadResponse(j["content"], mapper); + } + return resp; + } + + static Vamp::HostExt::ConfigurationRequest + toVampRequest_Configure(json11::Json j, PluginHandleMapper &mapper) { + + checkTypeField(j, "configure"); + return toConfigurationRequest(j["content"], mapper); + } + + static Vamp::HostExt::ConfigurationResponse + toVampResponse_Configure(json11::Json j) { + + Vamp::HostExt::ConfigurationResponse resp; + if (successful(j)) { + resp = toConfigurationResponse(j["content"]); + } + return resp; + } + + static Vamp::HostExt::ProcessRequest + toVampRequest_Process(json11::Json j, PluginHandleMapper &mapper) { + + checkTypeField(j, "process"); + return toProcessRequest(j["content"], mapper); + } + + static Vamp::HostExt::ProcessResponse + toVampResponse_Process(json11::Json j) { + + Vamp::HostExt::ProcessResponse resp; + if (successful(j)) { + resp.features = toFeatureSet(j["content"]); + } + return resp; + } + + static Vamp::Plugin * + toVampRequest_Finish(json11::Json j, PluginHandleMapper &mapper) { + + checkTypeField(j, "finish"); + return mapper.handleToPlugin(j["content"]["pluginHandle"].int_value()); + } + + static Vamp::HostExt::ProcessResponse + toVampResponse_Finish(json11::Json j) { + + Vamp::HostExt::ProcessResponse resp; + if (successful(j)) { + resp.features = toFeatureSet(j["content"]); + } + return resp; + } }; }
--- a/utilities/json-cli.cpp Fri May 20 18:05:02 2016 +0100 +++ b/utilities/json-cli.cpp Mon May 23 16:09:25 2016 +0100 @@ -160,8 +160,8 @@ throw VampJson::Failure("invalid request: " + err); } - if (!j["verb"].is_string()) { - throw VampJson::Failure("verb expected in request"); + if (!j["type"].is_string()) { + throw VampJson::Failure("type expected in request"); } if (!j["content"].is_null() && @@ -169,7 +169,7 @@ throw VampJson::Failure("object expected for content"); } - string verb = j["verb"].string_value(); + string verb = j["type"].string_value(); Json content = j["content"]; Json result;
--- a/utilities/vampipe-convert.cpp Fri May 20 18:05:02 2016 +0100 +++ b/utilities/vampipe-convert.cpp Mon May 23 16:09:25 2016 +0100 @@ -10,31 +10,11 @@ using namespace json11; using namespace vampipe; -// Accepting JSON objects with two fields, "type" and "payload". The +// Accepting JSON objects with two fields, "type" and "content". The // "type" string corresponds to the JSON schema filename -// (e.g. "outputdescriptor") and the "payload" is the JSON object +// (e.g. "outputdescriptor") and the "content" is the JSON object // encoded with that schema. -Json -json_input(string input) -{ - string err; - Json j = Json::parse(input, err); - if (err != "") { - throw VampJson::Failure("invalid json: " + err); - } - if (!j.is_object()) { - throw VampJson::Failure("object expected at top level"); - } - if (!j["type"].is_string()) { - throw VampJson::Failure("string expected for type field"); - } - if (!j["payload"].is_object()) { - throw VampJson::Failure("object expected for payload field"); - } - return j; -} - class PreservingPluginHandleMapper : public PluginHandleMapper { public: @@ -56,112 +36,63 @@ Vamp::Plugin *m_plugin; }; -void -handle_input(::capnp::MallocMessageBuilder &message, string input) -{ - string err; - - Json j = json_input(input); - string type = j["type"].string_value(); - Json payload = j["payload"]; - - if (type == "configurationrequest") { - auto req = message.initRoot<ConfigurationRequest>(); - PreservingPluginHandleMapper mapper; - VampnProto::buildConfigurationRequest - (req, VampJson::toConfigurationRequest(payload, mapper), mapper); - - } else if (type == "configurationresponse") { - auto resp = message.initRoot<ConfigurationResponse>(); - VampnProto::buildConfigurationResponse - (resp, VampJson::toConfigurationResponse(payload)); - - } else if (type == "feature") { - auto f = message.initRoot<Feature>(); - VampnProto::buildFeature - (f, VampJson::toFeature(payload)); - - } else if (type == "featureset") { - auto fs = message.initRoot<FeatureSet>(); - VampnProto::buildFeatureSet - (fs, VampJson::toFeatureSet(payload)); - - } else if (type == "loadrequest") { - auto req = message.initRoot<LoadRequest>(); - VampnProto::buildLoadRequest - (req, VampJson::toLoadRequest(payload)); - - } else if (type == "loadresponse") { - auto resp = message.initRoot<LoadResponse>(); - PreservingPluginHandleMapper mapper; - VampnProto::buildLoadResponse - (resp, VampJson::toLoadResponse(payload, mapper), mapper); - - } else if (type == "outputdescriptor") { - auto od = message.initRoot<OutputDescriptor>(); - VampnProto::buildOutputDescriptor - (od, VampJson::toOutputDescriptor(payload)); - - } else if (type == "parameterdescriptor") { - auto pd = message.initRoot<ParameterDescriptor>(); - VampnProto::buildParameterDescriptor - (pd, VampJson::toParameterDescriptor(payload)); - - } else if (type == "pluginconfiguration") { - auto pc = message.initRoot<PluginConfiguration>(); - auto config = VampJson::toPluginConfiguration(payload); - VampnProto::buildPluginConfiguration(pc, config); - - } else if (type == "pluginstaticdata") { - auto pc = message.initRoot<PluginStaticData>(); - auto sd = VampJson::toPluginStaticData(payload); - VampnProto::buildPluginStaticData(pc, sd); - - } else if (type == "processrequest") { - auto p = message.initRoot<ProcessRequest>(); - PreservingPluginHandleMapper mapper; - VampnProto::buildProcessRequest - (p, VampJson::toProcessRequest(payload, mapper), mapper); - - } else if (type == "realtime") { - auto b = message.initRoot<RealTime>(); - VampnProto::buildRealTime - (b, VampJson::toRealTime(payload)); - - } else { - throw VampJson::Failure("unknown or unsupported JSON schema type " + - type); - } -} - void usage() { string myname = "vampipe-convert"; cerr << "\n" << myname << - ": Convert Vamp request and response messages between formats\n\n" - " Usage: " << myname << " -i <informat> -o <outformat>\n\n" - "Where <informat> and <outformat> may be \"json\" or \"capnp\".\n" - "Messages are read from stdin and written to stdout.\n" << endl; + ": Validate and convert Vamp request and response messages\n\n" + " Usage: " << myname << " [-i <informat>] [-o <outformat>] request\n" + " " << myname << " [-i <informat>] [-o <outformat>] response\n\n" + " where\n" + " <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" + "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"; + exit(2); } class RequestOrResponse { public: + enum Direction { + Request, Response + }; enum Type { List, Load, Configure, Process, Finish, Eof }; + + static Type typeForName(string name) { + if (name == "list") return List; + else if (name == "load") return Load; + else if (name == "configure") return Configure; + else if (name == "process") return Process; + else if (name == "finish") return Finish; + else if (name == "eof") return Eof; + else { + throw runtime_error("unknown or unexpected request/response type \"" + + name + "\""); + } + } + RequestOrResponse() : // nothing by default + direction(Request), type(Eof), success(false), finishPlugin(0) { } + Direction direction; Type type; bool success; string errorText; PreservingPluginHandleMapper mapper; - + + vector<Vamp::HostExt::PluginStaticData> listResponse; Vamp::HostExt::LoadRequest loadRequest; Vamp::HostExt::LoadResponse loadResponse; Vamp::HostExt::ConfigurationRequest configurationRequest; @@ -170,96 +101,219 @@ Vamp::HostExt::ProcessResponse processResponse; Vamp::Plugin *finishPlugin; Vamp::HostExt::ProcessResponse finishResponse; - }; +Json +convertRequestJson(string input) +{ + string err; + Json j = Json::parse(input, err); + if (err != "") { + throw VampJson::Failure("invalid json: " + err); + } + if (!j.is_object()) { + throw VampJson::Failure("object expected at top level"); + } + if (!j["type"].is_string()) { + throw VampJson::Failure("string expected for type field"); + } + if (!j["content"].is_object()) { + throw VampJson::Failure("object expected for content field"); + } + return j; +} + +Json +convertResponseJson(string input) +{ + string err; + Json j = Json::parse(input, err); + if (err != "") { + throw VampJson::Failure("invalid json: " + err); + } + if (!j.is_object()) { + throw VampJson::Failure("object expected at top level"); + } + if (!j["success"].is_bool()) { + throw VampJson::Failure("bool expected for success field"); + } + if (!j["content"].is_object()) { + throw VampJson::Failure("object expected for content field"); + } + return j; +} + RequestOrResponse -readInputJson() +readRequestJson() { RequestOrResponse rr; + rr.direction = RequestOrResponse::Request; + + string input; + if (!getline(cin, input)) { + rr.type = RequestOrResponse::Eof; + return rr; + } + + Json j = convertRequestJson(input); + string type = j["type"].string_value(); + rr.type = RequestOrResponse::typeForName(type); + + if (rr.type == RequestOrResponse::Load) { + rr.loadRequest = VampJson::toVampRequest_Load(j); + + } else if (rr.type == RequestOrResponse::Configure) { + rr.configurationRequest = VampJson::toVampRequest_Configure(j, rr.mapper); + + } else if (rr.type == RequestOrResponse::Process) { + rr.processRequest = VampJson::toVampRequest_Process(j, rr.mapper); + + } else if (rr.type == RequestOrResponse::Finish) { + rr.finishPlugin = VampJson::toVampRequest_Finish(j, rr.mapper); + } + + return rr; +} + +void +writeRequestJson(RequestOrResponse &rr) +{ + Json j; + + if (rr.type == RequestOrResponse::List) { + j = VampJson::fromVampRequest_List(); + + } else if (rr.type == RequestOrResponse::Load) { + j = VampJson::fromVampRequest_Load(rr.loadRequest); + + } else if (rr.type == RequestOrResponse::Configure) { + j = VampJson::fromVampRequest_Configure(rr.configurationRequest, rr.mapper); + + } else if (rr.type == RequestOrResponse::Process) { + j = VampJson::fromVampRequest_Process(rr.processRequest, rr.mapper); + + } else if (rr.type == RequestOrResponse::Finish) { + j = VampJson::fromVampRequest_Finish(rr.finishPlugin, rr.mapper); + } + + cout << j.dump() << endl; +} + +RequestOrResponse +readResponseJson() +{ + RequestOrResponse rr; + rr.direction = RequestOrResponse::Response; + string input; if (!getline(cin, input)) { rr.type = RequestOrResponse::Eof; return rr; } - Json j = json_input(input); + Json j = convertResponseJson(input); string type = j["type"].string_value(); + rr.type = RequestOrResponse::typeForName(type); - if (type == "list") { - rr.type = RequestOrResponse::List; + if (rr.type == RequestOrResponse::List) { + rr.listResponse = VampJson::toVampResponse_List(j); + + } else if (rr.type == RequestOrResponse::Load) { + rr.loadResponse = VampJson::toVampResponse_Load(j, rr.mapper); - } else if (type == "load") { - //!!! ah, we need a way to know whether we're dealing with a request or response here - rr.type = RequestOrResponse::Load; - rr.loadRequest = VampJson::toLoadRequest(j["content"]); + } else if (rr.type == RequestOrResponse::Configure) { + rr.configurationResponse = VampJson::toVampResponse_Configure(j); - } else if (type == "configure") { - rr.type = RequestOrResponse::Configure; - rr.configurationRequest = - VampJson::toConfigurationRequest(j["content"], rr.mapper); + } else if (rr.type == RequestOrResponse::Process) { + rr.processResponse = VampJson::toVampResponse_Process(j); - } else if (type == "process") { - rr.type = RequestOrResponse::Process; - rr.processRequest = - VampJson::toProcessRequest(j["content"], rr.mapper); - - } else if (type == "finish") { - rr.type = RequestOrResponse::Finish; - //!!! VampJsonify - rr.finishPlugin = rr.mapper.handleToPlugin(j["content"]["pluginHandle"].int_value()); - - } else { - throw runtime_error("unknown or unexpected request/response type \"" + - type + "\""); + } else if (rr.type == RequestOrResponse::Finish) { + rr.finishResponse = VampJson::toVampResponse_Finish(j); } return rr; } +void +writeResponseJson(RequestOrResponse &rr) +{ + Json j; + + if (rr.type == RequestOrResponse::List) { + j = VampJson::fromVampResponse_List("", rr.listResponse); + + } else if (rr.type == RequestOrResponse::Load) { + j = VampJson::fromVampResponse_Load(rr.loadResponse, rr.mapper); + } + + cout << j.dump() << endl; +} + RequestOrResponse -readInput(string format) +readInput(string format, RequestOrResponse::Direction direction) { if (format == "json") { - return readInputJson(); + if (direction == RequestOrResponse::Request) { + return readRequestJson(); + } else { + return readResponseJson(); + } } else { throw runtime_error("unknown or unimplemented format \"" + format + "\""); } } void -writeOutput(string format, const RequestOrResponse &rr) +writeOutput(string format, RequestOrResponse &rr) { - throw runtime_error("writeOutput not implemented yet"); - + if (format == "json") { + if (rr.direction == RequestOrResponse::Request) { + writeRequestJson(rr); + } else { + writeResponseJson(rr); + } + } else { + throw runtime_error("unknown or unimplemented format \"" + format + "\""); + } } int main(int argc, char **argv) { - if (argc != 5) { + if (argc < 2) { usage(); } - string informat, outformat; + string informat = "json", outformat = "json"; + RequestOrResponse::Direction direction; + bool haveDirection = false; - for (int i = 1; i + 1 < argc; ++i) { + for (int i = 1; i < argc; ++i) { string arg = argv[i]; + bool final = (i + 1 == argc); if (arg == "-i") { - if (informat != "") usage(); + if (informat != "" || final) usage(); else informat = argv[++i]; } else if (arg == "-o") { - if (outformat != "") usage(); + if (outformat != "" || final) usage(); else outformat = argv[++i]; + } else if (arg == "request") { + direction = RequestOrResponse::Request; + haveDirection = true; + + } else if (arg == "response") { + direction = RequestOrResponse::Response; + haveDirection = true; + } else { usage(); } } - if (informat == "" || outformat == "") { + if (informat == "" || outformat == "" || !haveDirection) { usage(); } @@ -267,7 +321,7 @@ try { - RequestOrResponse rr = readInput(informat); + RequestOrResponse rr = readInput(informat, direction); if (rr.type == RequestOrResponse::Eof) break; writeOutput(outformat, rr);