c@75: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
c@75: 
c@75: /*
c@75:     Piper C++
c@75: 
c@75:     Centre for Digital Music, Queen Mary, University of London.
c@75:     Copyright 2015-2016 QMUL.
c@75:   
c@75:     Permission is hereby granted, free of charge, to any person
c@75:     obtaining a copy of this software and associated documentation
c@75:     files (the "Software"), to deal in the Software without
c@75:     restriction, including without limitation the rights to use, copy,
c@75:     modify, merge, publish, distribute, sublicense, and/or sell copies
c@75:     of the Software, and to permit persons to whom the Software is
c@75:     furnished to do so, subject to the following conditions:
c@75: 
c@75:     The above copyright notice and this permission notice shall be
c@75:     included in all copies or substantial portions of the Software.
c@75: 
c@75:     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@75:     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@75:     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@75:     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@75:     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@75:     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@75:     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@75: 
c@75:     Except as contained in this notice, the names of the Centre for
c@75:     Digital Music; Queen Mary, University of London; and Chris Cannam
c@75:     shall not be used in advertising or otherwise to promote the sale,
c@75:     use or other dealings in this Software without prior written
c@75:     authorization.
c@75: */
c@75: 
c@75: #ifndef PIPER_VAMP_JSON_H
c@75: #define PIPER_VAMP_JSON_H
c@75: 
c@75: #include <vector>
c@75: #include <string>
c@75: #include <sstream>
c@75: 
c@75: #include <json11/json11.hpp>
c@75: #include <base-n/include/basen.hpp>
c@75: 
c@75: #include <vamp-hostsdk/Plugin.h>
c@75: #include <vamp-hostsdk/PluginLoader.h>
c@97: 
c@97: #include "vamp-support/PluginStaticData.h"
c@97: #include "vamp-support/PluginConfiguration.h"
c@97: #include "vamp-support/RequestResponse.h"
c@75: 
c@75: #include "vamp-support/PluginHandleMapper.h"
c@75: #include "vamp-support/PluginOutputIdMapper.h"
c@75: #include "vamp-support/RequestResponseType.h"
c@75: 
c@97: namespace piper_vamp {
c@75: 
c@75: /**
c@75:  * Convert the structures laid out in the Vamp SDK classes into JSON
c@75:  * (and back again) following the schema in the vamp-json-schema
c@75:  * project repo.
c@75:  *
c@75:  * Functions with names starting "from" convert from a Vamp SDK object
c@75:  * to JSON output. Most of them return a json11::Json object, with a
c@75:  * few exceptions for low-level utilities that return a string. These
c@75:  * functions succeed all of the time.
c@75:  *
c@75:  * Functions with names starting "to" convert to a Vamp SDK object
c@75:  * from JSON input. These functions all accept a json11::Json object
c@75:  * as first argument, with a few exceptions for low-level utilities
c@75:  * that accept a string. These functions all accept a string reference
c@75:  * as a final argument and return an error string through it if the
c@75:  * conversion fails. If conversion fails the return value is
c@75:  * undefined, and any returned object may be incomplete or
c@75:  * invalid. Callers should check for an empty error string (indicating
c@75:  * success) before using the returned value.
c@75:  */
c@75: 
c@75: class VampJson
c@75: {
c@75: public:
c@75:     /** Serialisation format for arrays of floats (process input and
c@75:      *  feature values). Wherever such an array appears, it may
c@75:      *  alternatively be replaced by a single string containing a
c@75:      *  base-64 encoding of the IEEE float buffer. When parsing, if a
c@75:      *  string is found instead of an array in this case, it will be
c@75:      *  interpreted as a base-64 encoded buffer. Only array or base-64
c@75:      *  encoding may be provided, not both.
c@75:      */
c@75:     enum class BufferSerialisation {
c@75: 
c@75:         /** Default JSON serialisation of values in array form. This
c@75:          *  is relatively slow to parse and serialise, and can take a
c@75:          *  lot of space.
c@75:          */
c@75:         Array,
c@75: 
c@75:         /** Base64-encoded string of the raw data as packed
c@75:          *  little-endian IEEE 32-bit floats. Faster and more compact
c@75:          *  than the text encoding but more complicated to
c@75:          *  provide. Note that Base64 serialisations produced by this
c@75:          *  library do not including padding characters and so are not
c@75:          *  necessarily multiples of 4 characters long. You will need
c@75:          *  to pad them yourself if concatenating them or supplying to
c@75:          *  a consumer that expects padding.
c@75:          */
c@75:         Base64
c@75:     };
c@75:     
c@75:     static bool failed(const std::string &err) {
c@75:         return err != "";
c@75:     }
c@75:     
c@75:     template <typename T>
c@75:     static json11::Json
c@75:     fromBasicDescriptor(const T &t) {
c@75:         return json11::Json::object { 
c@75:             { "identifier", t.identifier },
c@75:             { "name", t.name },
c@75:             { "description", t.description }
c@75:         };
c@75:     }
c@75: 
c@75:     template <typename T>
c@75:     static void
c@75:     toBasicDescriptor(json11::Json j, T &t, std::string &err) {
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for basic descriptor content";
c@75:             return;
c@75:         }
c@75:         if (!j["identifier"].is_string()) {
c@75:             err = "string expected for identifier";
c@75:             return;
c@75:         }
c@75:         t.identifier = j["identifier"].string_value();
c@75:         t.name = j["name"].string_value();
c@75:         t.description = j["description"].string_value();
c@75:     }
c@75: 
c@75:     template <typename T>
c@75:     static json11::Json
c@75:     fromValueExtents(const T &t) {
c@75:         return json11::Json::object {
c@75:             { "min", t.minValue },
c@75:             { "max", t.maxValue }
c@75:         };
c@75:     }
c@75: 
c@75:     template <typename T>
c@75:     static bool
c@75:     toValueExtents(json11::Json j, T &t, std::string &err) {
c@75:         if (j["extents"].is_null()) {
c@75:             return false;
c@75:         } else if (j["extents"].is_object()) {
c@75:             if (j["extents"]["min"].is_number() &&
c@75:                 j["extents"]["max"].is_number()) {
c@75:                 t.minValue = j["extents"]["min"].number_value();
c@75:                 t.maxValue = j["extents"]["max"].number_value();
c@75:                 return true;
c@75:             } else {
c@75:                 err = "numbers expected for min and max";
c@75:                 return false;
c@75:             }
c@75:         } else {
c@75:             err = "object expected for extents (if present)";
c@75:             return false;
c@75:         }
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromRealTime(const Vamp::RealTime &r) {
c@75:         return json11::Json::object {
c@75:             { "s", r.sec },
c@75:             { "n", r.nsec }
c@75:         };
c@75:     }
c@75: 
c@75:     static Vamp::RealTime
c@75:     toRealTime(json11::Json j, std::string &err) {
c@75:         json11::Json sec = j["s"];
c@75:         json11::Json nsec = j["n"];
c@75:         if (!sec.is_number() || !nsec.is_number()) {
c@75:             err = "invalid Vamp::RealTime object " + j.dump();
c@75:             return {};
c@75:         }
c@75:         return Vamp::RealTime(sec.int_value(), nsec.int_value());
c@75:     }
c@75: 
c@75:     static std::string
c@75:     fromSampleType(Vamp::Plugin::OutputDescriptor::SampleType type) {
c@75:         switch (type) {
c@75:         case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
c@75:             return "OneSamplePerStep";
c@75:         case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
c@75:             return "FixedSampleRate";
c@75:         case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
c@75:             return "VariableSampleRate";
c@75:         }
c@75:         return "";
c@75:     }
c@75: 
c@75:     static Vamp::Plugin::OutputDescriptor::SampleType
c@75:     toSampleType(std::string text, std::string &err) {
c@75:         if (text == "OneSamplePerStep") {
c@75:             return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
c@75:         } else if (text == "FixedSampleRate") {
c@75:             return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
c@75:         } else if (text == "VariableSampleRate") {
c@75:             return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
c@75:         } else {
c@75:             err = "invalid sample type string: " + text;
c@75:             return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
c@75:         }
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromConfiguredOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
c@75:         json11::Json::object jo {
c@75:             { "unit", desc.unit },
c@75:             { "sampleType", fromSampleType(desc.sampleType) },
c@75:             { "sampleRate", desc.sampleRate },
c@75:             { "hasDuration", desc.hasDuration }
c@75:         };
c@75:         if (desc.hasFixedBinCount) {
c@75:             jo["binCount"] = int(desc.binCount);
c@75:             jo["binNames"] = json11::Json::array
c@75:                 (desc.binNames.begin(), desc.binNames.end());
c@75:         }
c@75:         if (desc.hasKnownExtents) {
c@75:             jo["extents"] = fromValueExtents(desc);
c@75:         }
c@75:         if (desc.isQuantized) {
c@75:             jo["quantizeStep"] = desc.quantizeStep;
c@75:         }
c@75:         return json11::Json(jo);
c@75:     }
c@75:     
c@75:     static json11::Json
c@75:     fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
c@75:         json11::Json::object jo {
c@75:             { "basic", fromBasicDescriptor(desc) },
c@75:             { "configured", fromConfiguredOutputDescriptor(desc) }
c@75:         };
c@75:         return json11::Json(jo);
c@75:     }
c@75:     
c@75:     static Vamp::Plugin::OutputDescriptor
c@75:     toConfiguredOutputDescriptor(json11::Json j, std::string &err) {
c@75: 
c@75:         Vamp::Plugin::OutputDescriptor od;
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for output descriptor";
c@75:             return {};
c@75:         }
c@75:     
c@75:         od.unit = j["unit"].string_value();
c@75: 
c@75:         od.sampleType = toSampleType(j["sampleType"].string_value(), err);
c@75:         if (failed(err)) return {};
c@75: 
c@75:         if (!j["sampleRate"].is_number()) {
c@75:             err = "number expected for sample rate";
c@75:             return {};
c@75:         }
c@75:         od.sampleRate = j["sampleRate"].number_value();
c@75:         od.hasDuration = j["hasDuration"].bool_value();
c@75: 
c@75:         if (j["binCount"].is_number() && j["binCount"].int_value() > 0) {
c@75:             od.hasFixedBinCount = true;
c@75:             od.binCount = j["binCount"].int_value();
c@75:             for (auto &n: j["binNames"].array_items()) {
c@75:                 if (!n.is_string()) {
c@75:                     err = "string expected for bin name";
c@75:                     return {};
c@75:                 }
c@75:                 od.binNames.push_back(n.string_value());
c@75:             }
c@75:         } else {
c@75:             od.hasFixedBinCount = false;
c@75:         }
c@75: 
c@75:         bool extentsPresent = toValueExtents(j, od, err);
c@75:         if (failed(err)) return {};
c@75:         
c@75:         od.hasKnownExtents = extentsPresent;
c@75: 
c@75:         if (j["quantizeStep"].is_number()) {
c@75:             od.isQuantized = true;
c@75:             od.quantizeStep = j["quantizeStep"].number_value();
c@75:         } else {
c@75:             od.isQuantized = false;
c@75:         }
c@75: 
c@75:         return od;
c@75:     }
c@75:     
c@75:     static Vamp::Plugin::OutputDescriptor
c@75:     toOutputDescriptor(json11::Json j, std::string &err) {
c@75: 
c@75:         Vamp::Plugin::OutputDescriptor od;
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for output descriptor";
c@75:             return {};
c@75:         }
c@75: 
c@75:         od = toConfiguredOutputDescriptor(j, err);
c@75:         if (failed(err)) return {};
c@75:     
c@75:         toBasicDescriptor(j["basic"], od, err);
c@75:         if (failed(err)) return {};
c@75: 
c@75:         return od;
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromParameterDescriptor(const Vamp::PluginBase::ParameterDescriptor &desc) {
c@75: 
c@75:         json11::Json::object jo {
c@75:             { "basic", fromBasicDescriptor(desc) },
c@75:             { "unit", desc.unit },
c@75:             { "extents", fromValueExtents(desc) },
c@75:             { "defaultValue", desc.defaultValue },
c@75:             { "valueNames", json11::Json::array
c@75:                     (desc.valueNames.begin(), desc.valueNames.end()) }
c@75:         };
c@75:         if (desc.isQuantized) {
c@75:             jo["quantizeStep"] = desc.quantizeStep;
c@75:         }
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static Vamp::PluginBase::ParameterDescriptor
c@75:     toParameterDescriptor(json11::Json j, std::string &err) {
c@75: 
c@75:         Vamp::PluginBase::ParameterDescriptor pd;
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for parameter descriptor";
c@75:             return {};
c@75:         }
c@75:     
c@75:         toBasicDescriptor(j["basic"], pd, err);
c@75:         if (failed(err)) return {};
c@75: 
c@75:         pd.unit = j["unit"].string_value();
c@75: 
c@75:         bool extentsPresent = toValueExtents(j, pd, err);
c@75:         if (failed(err)) return {};
c@75:         if (!extentsPresent) {
c@75:             err = "extents must be present in parameter descriptor";
c@75:             return {};
c@75:         }
c@75:     
c@75:         if (!j["defaultValue"].is_number()) {
c@75:             err = "number expected for default value";
c@75:             return {};
c@75:         }
c@75:     
c@75:         pd.defaultValue = j["defaultValue"].number_value();
c@75: 
c@75:         pd.valueNames.clear();
c@75:         for (auto &n: j["valueNames"].array_items()) {
c@75:             if (!n.is_string()) {
c@75:                 err = "string expected for value name";
c@75:                 return {};
c@75:             }
c@75:             pd.valueNames.push_back(n.string_value());
c@75:         }
c@75: 
c@75:         if (j["quantizeStep"].is_number()) {
c@75:             pd.isQuantized = true;
c@75:             pd.quantizeStep = j["quantizeStep"].number_value();
c@75:         } else {
c@75:             pd.isQuantized = false;
c@75:         }
c@75: 
c@75:         return pd;
c@75:     }
c@75: 
c@75:     static std::string
c@75:     fromFloatBuffer(const float *buffer, size_t nfloats) {
c@75:         // must use char pointers, otherwise the converter will only
c@75:         // encode every 4th byte (as it will count up in float* steps)
c@75:         const char *start = reinterpret_cast<const char *>(buffer);
c@75:         const char *end = reinterpret_cast<const char *>(buffer + nfloats);
c@75:         std::string encoded;
c@75:         bn::encode_b64(start, end, back_inserter(encoded));
c@75:         return encoded;
c@75:     }
c@75: 
c@75:     static std::vector<float>
c@75:     toFloatBuffer(std::string encoded, std::string & /* err */) {
c@75:         std::string decoded;
c@75:         bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded));
c@75:         const float *buffer = reinterpret_cast<const float *>(decoded.c_str());
c@75:         size_t n = decoded.size() / sizeof(float);
c@75:         return std::vector<float>(buffer, buffer + n);
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromFeature(const Vamp::Plugin::Feature &f,
c@75:                 BufferSerialisation serialisation) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         if (f.values.size() > 0) {
c@75:             if (serialisation == BufferSerialisation::Array) {
c@75:                 jo["featureValues"] = json11::Json::array(f.values.begin(),
c@75:                                                           f.values.end());
c@75:             } else {
c@75:                 jo["featureValues"] = fromFloatBuffer(f.values.data(),
c@75:                                                       f.values.size());
c@75:             }
c@75:         }
c@75:         if (f.label != "") {
c@75:             jo["label"] = f.label;
c@75:         }
c@75:         if (f.hasTimestamp) {
c@75:             jo["timestamp"] = fromRealTime(f.timestamp);
c@75:         }
c@75:         if (f.hasDuration) {
c@75:             jo["duration"] = fromRealTime(f.duration);
c@75:         }
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static Vamp::Plugin::Feature
c@75:     toFeature(json11::Json j, BufferSerialisation &serialisation, std::string &err) {
c@75: 
c@75:         Vamp::Plugin::Feature f;
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for feature";
c@75:             return {};
c@75:         }
c@75:         if (j["timestamp"].is_object()) {
c@75:             f.timestamp = toRealTime(j["timestamp"], err);
c@75:             if (failed(err)) return {};
c@75:             f.hasTimestamp = true;
c@75:         }
c@75:         if (j["duration"].is_object()) {
c@75:             f.duration = toRealTime(j["duration"], err);
c@75:             if (failed(err)) return {};
c@75:             f.hasDuration = true;
c@75:         }
c@75:         if (j["featureValues"].is_string()) {
c@75:             f.values = toFloatBuffer(j["featureValues"].string_value(), err);
c@75:             if (failed(err)) return {};
c@75:             serialisation = BufferSerialisation::Base64;
c@75:         } else if (j["featureValues"].is_array()) {
c@75:             for (auto v : j["featureValues"].array_items()) {
c@75:                 f.values.push_back(v.number_value());
c@75:             }
c@75:             serialisation = BufferSerialisation::Array;
c@75:         }
c@75:         f.label = j["label"].string_value();
c@75:         return f;
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromFeatureSet(const Vamp::Plugin::FeatureSet &fs,
c@75:                    const PluginOutputIdMapper &omapper,
c@75:                    BufferSerialisation serialisation) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         for (const auto &fsi : fs) {
c@75:             std::vector<json11::Json> fj;
c@75:             for (const Vamp::Plugin::Feature &f: fsi.second) {
c@75:                 fj.push_back(fromFeature(f, serialisation));
c@75:             }
c@75:             jo[omapper.indexToId(fsi.first)] = fj;
c@75:         }
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static Vamp::Plugin::FeatureList
c@75:     toFeatureList(json11::Json j,
c@75:                   BufferSerialisation &serialisation, std::string &err) {
c@75: 
c@75:         Vamp::Plugin::FeatureList fl;
c@75:         if (!j.is_array()) {
c@75:             err = "array expected for feature list";
c@75:             return {};
c@75:         }
c@75:         for (const json11::Json &fj : j.array_items()) {
c@75:             fl.push_back(toFeature(fj, serialisation, err));
c@75:             if (failed(err)) return {};
c@75:         }
c@75:         return fl;
c@75:     }
c@75: 
c@75:     static Vamp::Plugin::FeatureSet
c@75:     toFeatureSet(json11::Json j,
c@75:                  const PluginOutputIdMapper &omapper,
c@75:                  BufferSerialisation &serialisation,
c@75:                  std::string &err) {
c@75: 
c@75:         Vamp::Plugin::FeatureSet fs;
c@75:         if (!j.is_object()) {
c@75:             err = "object expected for feature set";
c@75:             return {};
c@75:         }
c@75:         for (auto &entry : j.object_items()) {
c@75:             int n = omapper.idToIndex(entry.first);
c@75:             if (fs.find(n) != fs.end()) {
c@75:                 err = "duplicate numerical index for output";
c@75:                 return {};
c@75:             }
c@75:             fs[n] = toFeatureList(entry.second, serialisation, err);
c@75:             if (failed(err)) return {};
c@75:         }
c@75:         return fs;
c@75:     }
c@75: 
c@75:     static std::string
c@75:     fromInputDomain(Vamp::Plugin::InputDomain domain) {
c@75: 
c@75:         switch (domain) {
c@75:         case Vamp::Plugin::TimeDomain:
c@75:             return "TimeDomain";
c@75:         case Vamp::Plugin::FrequencyDomain:
c@75:             return "FrequencyDomain";
c@75:         }
c@75:         return "";
c@75:     }
c@75: 
c@75:     static Vamp::Plugin::InputDomain
c@75:     toInputDomain(std::string text, std::string &err) {
c@75: 
c@75:         if (text == "TimeDomain") {
c@75:             return Vamp::Plugin::TimeDomain;
c@75:         } else if (text == "FrequencyDomain") {
c@75:             return Vamp::Plugin::FrequencyDomain;
c@75:         } else {
c@75:             err = "invalid input domain string: " + text;
c@75:             return {};
c@75:         }
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromPluginStaticData(const PluginStaticData &d) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         jo["key"] = d.pluginKey;
c@75:         jo["basic"] = fromBasicDescriptor(d.basic);
c@75:         jo["maker"] = d.maker;
c@75:         jo["copyright"] = d.copyright;
c@75:         jo["version"] = d.pluginVersion;
c@75: 
c@75:         json11::Json::array cat;
c@75:         for (const std::string &c: d.category) cat.push_back(c);
c@75:         jo["category"] = cat;
c@75: 
c@105:         jo["minChannelCount"] = int(d.minChannelCount);
c@105:         jo["maxChannelCount"] = int(d.maxChannelCount);
c@75: 
c@75:         json11::Json::array params;
c@75:         Vamp::PluginBase::ParameterList vparams = d.parameters;
c@75:         for (auto &p: vparams) params.push_back(fromParameterDescriptor(p));
c@75:         jo["parameters"] = params;
c@75: 
c@75:         json11::Json::array progs;
c@75:         Vamp::PluginBase::ProgramList vprogs = d.programs;
c@75:         for (auto &p: vprogs) progs.push_back(p);
c@75:         jo["programs"] = progs;
c@75: 
c@75:         jo["inputDomain"] = fromInputDomain(d.inputDomain);
c@75: 
c@75:         json11::Json::array outinfo;
c@75:         auto vouts = d.basicOutputInfo;
c@75:         for (auto &o: vouts) outinfo.push_back(fromBasicDescriptor(o));
c@75:         jo["basicOutputInfo"] = outinfo;
c@75:     
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static PluginStaticData
c@75:     toPluginStaticData(json11::Json j, std::string &err) {
c@75: 
c@75:         if (!j.has_shape({
c@75:                     { "key", json11::Json::STRING },
c@75:                     { "version", json11::Json::NUMBER },
c@75:                     { "minChannelCount", json11::Json::NUMBER },
c@75:                     { "maxChannelCount", json11::Json::NUMBER },
c@75:                     { "inputDomain", json11::Json::STRING }}, err)) {
c@75: 
c@75:             err = "malformed plugin static data: " + err;
c@75: 
c@75:         } else if (!j["basicOutputInfo"].is_array()) {
c@75: 
c@75:             err = "array expected for basic output info";
c@75: 
c@75:         } else if (!j["maker"].is_null() &&
c@75:                    !j["maker"].is_string()) {
c@75: 
c@75:             err = "string expected for maker";
c@75: 
c@75:         } else if (!j["copyright"].is_null() &&
c@75:                    !j["copyright"].is_string()) {
c@75:             err = "string expected for copyright";
c@75: 
c@75:         } else if (!j["category"].is_null() &&
c@75:                    !j["category"].is_array()) {
c@75: 
c@75:             err = "array expected for category";
c@75: 
c@75:         } else if (!j["parameters"].is_null() &&
c@75:                    !j["parameters"].is_array()) {
c@75: 
c@75:             err = "array expected for parameters";
c@75: 
c@75:         } else if (!j["programs"].is_null() &&
c@75:                    !j["programs"].is_array()) {
c@75: 
c@75:             err = "array expected for programs";
c@75: 
c@75:         } else if (!j["inputDomain"].is_null() &&
c@75:                    !j["inputDomain"].is_string()) {
c@75: 
c@75:             err = "string expected for inputDomain";
c@75: 
c@75:         } else if (!j["basicOutputInfo"].is_null() &&
c@75:                    !j["basicOutputInfo"].is_array()) {
c@75:             
c@75:             err = "array expected for basicOutputInfo";
c@75: 
c@75:         } else {
c@75: 
c@97:             PluginStaticData psd;
c@75: 
c@75:             psd.pluginKey = j["key"].string_value();
c@75: 
c@75:             toBasicDescriptor(j["basic"], psd.basic, err);
c@75:             if (failed(err)) return {};
c@75: 
c@75:             psd.maker = j["maker"].string_value();
c@75:             psd.copyright = j["copyright"].string_value();
c@75:             psd.pluginVersion = j["version"].int_value();
c@75: 
c@75:             for (const auto &c : j["category"].array_items()) {
c@75:                 if (!c.is_string()) {
c@75:                     err = "strings expected in category array";
c@75:                     return {};
c@75:                 }
c@75:                 psd.category.push_back(c.string_value());
c@75:             }
c@75: 
c@75:             psd.minChannelCount = j["minChannelCount"].int_value();
c@75:             psd.maxChannelCount = j["maxChannelCount"].int_value();
c@75: 
c@75:             for (const auto &p : j["parameters"].array_items()) {
c@75:                 auto pd = toParameterDescriptor(p, err);
c@75:                 if (failed(err)) return {};
c@75:                 psd.parameters.push_back(pd);
c@75:             }
c@75: 
c@75:             for (const auto &p : j["programs"].array_items()) {
c@75:                 if (!p.is_string()) {
c@75:                     err = "strings expected in programs array";
c@75:                     return {};
c@75:                 }
c@75:                 psd.programs.push_back(p.string_value());
c@75:             }
c@75: 
c@75:             psd.inputDomain = toInputDomain(j["inputDomain"].string_value(), err);
c@75:             if (failed(err)) return {};
c@75: 
c@75:             for (const auto &bo : j["basicOutputInfo"].array_items()) {
c@97:                 PluginStaticData::Basic b;
c@75:                 toBasicDescriptor(bo, b, err);
c@75:                 if (failed(err)) return {};
c@75:                 psd.basicOutputInfo.push_back(b);
c@75:             }
c@75:             
c@75:             return psd;
c@75:         }
c@75: 
c@75:         // fallthrough error case
c@75:         return {};
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromPluginConfiguration(const PluginConfiguration &c) {
c@75: 
c@75:         json11::Json::object jo;
c@75: 
c@75:         json11::Json::object paramValues;
c@75:         for (auto &vp: c.parameterValues) {
c@75:             paramValues[vp.first] = vp.second;
c@75:         }
c@75:         jo["parameterValues"] = paramValues;
c@75: 
c@75:         if (c.currentProgram != "") {
c@75:             jo["currentProgram"] = c.currentProgram;
c@75:         }
c@75: 
c@75:         jo["channelCount"] = c.channelCount;
c@75:         jo["stepSize"] = c.stepSize;
c@75:         jo["blockSize"] = c.blockSize;
c@75:     
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static PluginConfiguration
c@75:     toPluginConfiguration(json11::Json j, std::string &err) {
c@75:         
c@75:         if (!j.has_shape({
c@75:                     { "channelCount", json11::Json::NUMBER },
c@75:                     { "stepSize", json11::Json::NUMBER },
c@75:                     { "blockSize", json11::Json::NUMBER } }, err)) {
c@75:             err = "malformed plugin configuration: " + err;
c@75:             return {};
c@75:         }
c@75: 
c@75:         if (!j["parameterValues"].is_null() &&
c@75:             !j["parameterValues"].is_object()) {
c@75:             err = "object expected for parameter values";
c@75:             return {};
c@75:         }
c@75: 
c@75:         for (auto &pv : j["parameterValues"].object_items()) {
c@75:             if (!pv.second.is_number()) {
c@75:                 err = "number expected for parameter value";
c@75:                 return {};
c@75:             }
c@75:         }
c@75:     
c@75:         if (!j["currentProgram"].is_null() &&
c@75:             !j["currentProgram"].is_string()) {
c@75:             err = "string expected for program name";
c@75:             return {};
c@75:         }
c@75: 
c@97:         PluginConfiguration config;
c@75: 
c@75:         config.channelCount = j["channelCount"].number_value();
c@75:         config.stepSize = j["stepSize"].number_value();
c@75:         config.blockSize = j["blockSize"].number_value();
c@75:         
c@75:         for (auto &pv : j["parameterValues"].object_items()) {
c@75:             config.parameterValues[pv.first] = pv.second.number_value();
c@75:         }
c@75: 
c@75:         if (j["currentProgram"].is_string()) {
c@75:             config.currentProgram = j["currentProgram"].string_value();
c@75:         }
c@75: 
c@75:         return config;
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromAdapterFlags(int flags) {
c@75: 
c@75:         json11::Json::array arr;
c@75: 
c@75:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN) {
c@75:             arr.push_back("AdaptInputDomain");
c@75:         }
c@75:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT) {
c@75:             arr.push_back("AdaptChannelCount");
c@75:         }
c@75:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE) {
c@75:             arr.push_back("AdaptBufferSize");
c@75:         }
c@75: 
c@75:         return json11::Json(arr);
c@75:     }
c@75: 
c@75:     static Vamp::HostExt::PluginLoader::AdapterFlags
c@75:     toAdapterFlags(json11::Json j, std::string &err) {
c@75: 
c@75:         int flags = 0x0;
c@75: 
c@75:         if (!j.is_array()) {
c@75: 
c@75:             err = "array expected for adapter flags";
c@75: 
c@75:         } else {
c@75: 
c@75:             for (auto &jj: j.array_items()) {
c@75:                 if (!jj.is_string()) {
c@75:                     err = "string expected for adapter flag";
c@75:                     break;
c@75:                 }
c@75:                 std::string text = jj.string_value();
c@75:                 if (text == "AdaptInputDomain") {
c@75:                     flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
c@75:                 } else if (text == "AdaptChannelCount") {
c@75:                     flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
c@75:                 } else if (text == "AdaptBufferSize") {
c@75:                     flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
c@75:                 } else if (text == "AdaptAllSafe") {
c@75:                     flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE;
c@75:                 } else if (text == "AdaptAll") {
c@75:                     flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL;
c@75:                 } else {
c@75:                     err = "invalid adapter flag string: " + text;
c@75:                     break;
c@75:                 }
c@75:             }
c@75:         }
c@75: 
c@75:         return Vamp::HostExt::PluginLoader::AdapterFlags(flags);
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromLoadRequest(const LoadRequest &req) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         jo["key"] = req.pluginKey;
c@75:         jo["inputSampleRate"] = req.inputSampleRate;
c@75:         jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags);
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static LoadRequest
c@75:     toLoadRequest(json11::Json j, std::string &err) {
c@75:         
c@75:         if (!j.has_shape({
c@75:                     { "key", json11::Json::STRING },
c@75:                     { "inputSampleRate", json11::Json::NUMBER } }, err)) {
c@75:             err = "malformed load request: " + err;
c@75:             return {};
c@75:         }
c@75:     
c@97:         LoadRequest req;
c@75:         req.pluginKey = j["key"].string_value();
c@75:         req.inputSampleRate = j["inputSampleRate"].number_value();
c@75:         if (!j["adapterFlags"].is_null()) {
c@75:             req.adapterFlags = toAdapterFlags(j["adapterFlags"], err);
c@75:             if (failed(err)) return {};
c@75:         }
c@75:         return req;
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromLoadResponse(const LoadResponse &resp,
c@75:                      const PluginHandleMapper &pmapper) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         jo["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75:         jo["staticData"] = fromPluginStaticData(resp.staticData);
c@75:         jo["defaultConfiguration"] =
c@75:             fromPluginConfiguration(resp.defaultConfiguration);
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static LoadResponse
c@75:     toLoadResponse(json11::Json j,
c@75:                    const PluginHandleMapper &pmapper, std::string &err) {
c@75: 
c@75:         if (!j.has_shape({
c@75:                     { "handle", json11::Json::NUMBER },
c@75:                     { "staticData", json11::Json::OBJECT },
c@75:                     { "defaultConfiguration", json11::Json::OBJECT } }, err)) {
c@75:             err = "malformed load response: " + err;
c@75:             return {};
c@75:         }
c@75: 
c@97:         LoadResponse resp;
c@75:         resp.plugin = pmapper.handleToPlugin(j["handle"].int_value());
c@75:         resp.staticData = toPluginStaticData(j["staticData"], err);
c@75:         if (failed(err)) return {};
c@75:         resp.defaultConfiguration = toPluginConfiguration(j["defaultConfiguration"],
c@75:                                                           err);
c@75:         if (failed(err)) return {};
c@75:         return resp;
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromConfigurationRequest(const ConfigurationRequest &cr,
c@75:                              const PluginHandleMapper &pmapper) {
c@75: 
c@75:         json11::Json::object jo;
c@75: 
c@86:         jo["handle"] = double(pmapper.pluginToHandle(cr.plugin));
c@75:         jo["configuration"] = fromPluginConfiguration(cr.configuration);
c@75:         
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static ConfigurationRequest
c@75:     toConfigurationRequest(json11::Json j,
c@75:                            const PluginHandleMapper &pmapper, std::string &err) {
c@75: 
c@75:         if (!j.has_shape({
c@75:                     { "handle", json11::Json::NUMBER },
c@75:                     { "configuration", json11::Json::OBJECT } }, err)) {
c@75:             err = "malformed configuration request: " + err;
c@75:             return {};
c@75:         }
c@75: 
c@97:         ConfigurationRequest cr;
c@75:         cr.plugin = pmapper.handleToPlugin(j["handle"].int_value());
c@75:         cr.configuration = toPluginConfiguration(j["configuration"], err);
c@75:         if (failed(err)) return {};
c@75:         return cr;
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromConfigurationResponse(const ConfigurationResponse &cr,
c@75:                               const PluginHandleMapper &pmapper) {
c@75: 
c@75:         json11::Json::object jo;
c@75: 
c@86:         jo["handle"] = double(pmapper.pluginToHandle(cr.plugin));
c@75:         
c@75:         json11::Json::array outs;
c@75:         for (auto &d: cr.outputs) {
c@75:             outs.push_back(fromOutputDescriptor(d));
c@75:         }
c@75:         jo["outputList"] = outs;
c@75:         
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static ConfigurationResponse
c@75:     toConfigurationResponse(json11::Json j,
c@75:                             const PluginHandleMapper &pmapper, std::string &err) {
c@75:         
c@97:         ConfigurationResponse cr;
c@75: 
c@75:         cr.plugin = pmapper.handleToPlugin(j["handle"].int_value());
c@75:         
c@75:         if (!j["outputList"].is_array()) {
c@75:             err = "array expected for output list";
c@75:             return {};
c@75:         }
c@75: 
c@75:         for (const auto &o: j["outputList"].array_items()) {
c@75:             cr.outputs.push_back(toOutputDescriptor(o, err));
c@75:             if (failed(err)) return {};
c@75:         }
c@75: 
c@75:         return cr;
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromProcessRequest(const ProcessRequest &r,
c@75:                        const PluginHandleMapper &pmapper,
c@75:                        BufferSerialisation serialisation) {
c@75: 
c@75:         json11::Json::object jo;
c@86:         jo["handle"] = double(pmapper.pluginToHandle(r.plugin));
c@75: 
c@75:         json11::Json::object io;
c@75:         io["timestamp"] = fromRealTime(r.timestamp);
c@75: 
c@75:         json11::Json::array chans;
c@75:         for (size_t i = 0; i < r.inputBuffers.size(); ++i) {
c@75:             if (serialisation == BufferSerialisation::Array) {
c@75:                 chans.push_back(json11::Json::array(r.inputBuffers[i].begin(),
c@75:                                                     r.inputBuffers[i].end()));
c@75:             } else {
c@75:                 chans.push_back(fromFloatBuffer(r.inputBuffers[i].data(),
c@75:                                                 r.inputBuffers[i].size()));
c@75:             }
c@75:         }
c@75:         io["inputBuffers"] = chans;
c@75:         
c@75:         jo["processInput"] = io;
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@97:     static ProcessRequest
c@75:     toProcessRequest(json11::Json j,
c@75:                      const PluginHandleMapper &pmapper,
c@75:                      BufferSerialisation &serialisation, std::string &err) {
c@75: 
c@75:         if (!j.has_shape({
c@75:                     { "handle", json11::Json::NUMBER },
c@75:                     { "processInput", json11::Json::OBJECT } }, err)) {
c@75:             err = "malformed process request: " + err;
c@75:             return {};
c@75:         }
c@75: 
c@75:         auto input = j["processInput"];
c@75: 
c@75:         if (!input.has_shape({
c@75:                     { "timestamp", json11::Json::OBJECT },
c@75:                     { "inputBuffers", json11::Json::ARRAY } }, err)) {
c@75:             err = "malformed process request: " + err;
c@75:             return {};
c@75:         }
c@75: 
c@97:         ProcessRequest r;
c@75:         r.plugin = pmapper.handleToPlugin(j["handle"].int_value());
c@75: 
c@75:         r.timestamp = toRealTime(input["timestamp"], err);
c@75:         if (failed(err)) return {};
c@75: 
c@75:         for (const auto &a: input["inputBuffers"].array_items()) {
c@75: 
c@75:             if (a.is_string()) {
c@75:                 std::vector<float> buf = toFloatBuffer(a.string_value(),
c@75:                                                        err);
c@75:                 if (failed(err)) return {};
c@75:                 r.inputBuffers.push_back(buf);
c@75:                 serialisation = BufferSerialisation::Base64;
c@75: 
c@75:             } else if (a.is_array()) {
c@75:                 std::vector<float> buf;
c@75:                 for (auto v : a.array_items()) {
c@75:                     buf.push_back(v.number_value());
c@75:                 }
c@75:                 r.inputBuffers.push_back(buf);
c@75:                 serialisation = BufferSerialisation::Array;
c@75: 
c@75:             } else {
c@75:                 err = "expected arrays or strings in inputBuffers array";
c@75:                 return {};
c@75:             }
c@75:         }
c@75: 
c@75:         return r;
c@75:     }
c@75:     
c@75: private: // go private briefly for a couple of helper functions
c@75:     
c@75:     static void
c@75:     checkTypeField(json11::Json j, std::string expected, std::string &err) {
c@75:         if (!j["method"].is_string()) {
c@75:             err = "string expected for method";
c@75:             return;
c@75:         }
c@75:         if (j["method"].string_value() != expected) {
c@75:             err = "expected value \"" + expected + "\" for type";
c@75:             return;
c@75:         }
c@75:     }
c@75: 
c@75:     static bool
c@75:     successful(json11::Json j, std::string &err) {
c@75:         if (!j["success"].is_bool()) {
c@75:             err = "bool expected for success";
c@75:             return false;
c@75:         }
c@75:         return j["success"].bool_value();
c@75:     }
c@75: 
c@75:     static void
c@75:     markRPC(json11::Json::object &jo) {
c@75:         jo["jsonrpc"] = "2.0";
c@75:     }
c@75: 
c@75:     static void
c@75:     addId(json11::Json::object &jo, const json11::Json &id) {
c@75:         if (!id.is_null()) {
c@75:             jo["id"] = id;
c@75:         }
c@75:     }
c@75:     
c@75: public:
c@75: 
c@75:     static json11::Json
c@75:     fromRpcRequest_List(const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         jo["method"] = "list";
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromRpcResponse_List(const ListResponse &resp,
c@75:                          const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         json11::Json::array arr;
c@75:         for (const auto &a: resp.available) {
c@75:             arr.push_back(fromPluginStaticData(a));
c@75:         }
c@75:         json11::Json::object po;
c@75:         po["available"] = arr;
c@75: 
c@75:         jo["method"] = "list";
c@75:         jo["result"] = po;
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }
c@75:     
c@75:     static json11::Json
c@97:     fromRpcRequest_Load(const LoadRequest &req,
c@75:                         const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         jo["method"] = "load";
c@75:         jo["params"] = fromLoadRequest(req);
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }    
c@75: 
c@75:     static json11::Json
c@97:     fromRpcResponse_Load(const LoadResponse &resp,
c@75:                          const PluginHandleMapper &pmapper,
c@75:                          const json11::Json &id) {
c@75: 
c@75:         if (resp.plugin) {
c@75: 
c@75:             json11::Json::object jo;
c@75:             markRPC(jo);
c@75: 
c@75:             jo["method"] = "load";
c@75:             jo["result"] = fromLoadResponse(resp, pmapper);
c@75:             addId(jo, id);
c@75:             return json11::Json(jo);
c@75:             
c@75:         } else {
c@75:             return fromError("Failed to load plugin", RRType::Load, id);
c@75:         }
c@75:     }
c@75: 
c@75:     static json11::Json
c@97:     fromRpcRequest_Configure(const ConfigurationRequest &req,
c@75:                              const PluginHandleMapper &pmapper,
c@75:                              const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         jo["method"] = "configure";
c@75:         jo["params"] = fromConfigurationRequest(req, pmapper);
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }    
c@75: 
c@75:     static json11::Json
c@97:     fromRpcResponse_Configure(const ConfigurationResponse &resp,
c@75:                               const PluginHandleMapper &pmapper,
c@75:                               const json11::Json &id) {
c@75: 
c@75:         if (!resp.outputs.empty()) {
c@75:         
c@75:             json11::Json::object jo;
c@75:             markRPC(jo);
c@75: 
c@75:             jo["method"] = "configure";
c@75:             jo["result"] = fromConfigurationResponse(resp, pmapper);
c@75:             addId(jo, id);
c@75:             return json11::Json(jo);
c@75: 
c@75:         } else {
c@75:             return fromError("Failed to configure plugin", RRType::Configure, id);
c@75:         }
c@75:     }
c@75:     
c@75:     static json11::Json
c@97:     fromRpcRequest_Process(const ProcessRequest &req,
c@75:                            const PluginHandleMapper &pmapper,
c@75:                            BufferSerialisation serialisation,
c@75:                            const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         jo["method"] = "process";
c@75:         jo["params"] = fromProcessRequest(req, pmapper, serialisation);
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }    
c@75: 
c@75:     static json11::Json
c@97:     fromRpcResponse_Process(const ProcessResponse &resp,
c@75:                             const PluginHandleMapper &pmapper,
c@75:                             BufferSerialisation serialisation,
c@75:                             const json11::Json &id) {
c@75:         
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         json11::Json::object po;
c@86:         po["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75:         po["features"] = fromFeatureSet(resp.features,
c@75:                                         *pmapper.pluginToOutputIdMapper(resp.plugin),
c@75:                                         serialisation);
c@75:         jo["method"] = "process";
c@75:         jo["result"] = po;
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }
c@75:     
c@75:     static json11::Json
c@97:     fromRpcRequest_Finish(const FinishRequest &req,
c@75:                           const PluginHandleMapper &pmapper,
c@75:                           const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         json11::Json::object fo;
c@86:         fo["handle"] = double(pmapper.pluginToHandle(req.plugin));
c@75: 
c@75:         jo["method"] = "finish";
c@75:         jo["params"] = fo;
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }    
c@75:     
c@75:     static json11::Json
c@97:     fromRpcResponse_Finish(const FinishResponse &resp,
c@75:                            const PluginHandleMapper &pmapper,
c@75:                            BufferSerialisation serialisation,
c@75:                            const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         json11::Json::object po;
c@86:         po["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75:         po["features"] = fromFeatureSet(resp.features,
c@75:                                         *pmapper.pluginToOutputIdMapper(resp.plugin),
c@75:                                         serialisation);
c@75:         jo["method"] = "finish";
c@75:         jo["result"] = po;
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static json11::Json
c@75:     fromError(std::string errorText,
c@75:               RRType responseType,
c@75:               const json11::Json &id) {
c@75: 
c@75:         json11::Json::object jo;
c@75:         markRPC(jo);
c@75: 
c@75:         std::string type;
c@75: 
c@75:         if (responseType == RRType::List) type = "list";
c@75:         else if (responseType == RRType::Load) type = "load";
c@75:         else if (responseType == RRType::Configure) type = "configure";
c@75:         else if (responseType == RRType::Process) type = "process";
c@75:         else if (responseType == RRType::Finish) type = "finish";
c@75:         else type = "invalid";
c@75: 
c@75:         json11::Json::object eo;
c@75:         eo["code"] = 0;
c@75:         eo["message"] = 
c@75:             std::string("error in ") + type + " request: " + errorText;
c@75: 
c@75:         jo["method"] = type;
c@75:         jo["error"] = eo;
c@75:         addId(jo, id);
c@75:         return json11::Json(jo);
c@75:     }
c@75: 
c@75:     static RRType
c@75:     getRequestResponseType(json11::Json j, std::string &err) {
c@75: 
c@75:         if (!j["method"].is_string()) {
c@75:             err = "string expected for method";
c@75:             return RRType::NotValid;
c@75:         }
c@75:         
c@75:         std::string type = j["method"].string_value();
c@75: 
c@75: 	if (type == "list") return RRType::List;
c@75: 	else if (type == "load") return RRType::Load;
c@75: 	else if (type == "configure") return RRType::Configure;
c@75: 	else if (type == "process") return RRType::Process;
c@75: 	else if (type == "finish") return RRType::Finish;
c@75:         else if (type == "invalid") return RRType::NotValid;
c@75: 	else {
c@75: 	    err = "unknown or unexpected request/response type \"" + type + "\"";
c@75:             return RRType::NotValid;
c@75: 	}
c@75:     }
c@75: 
c@75:     static void
c@75:     toRpcRequest_List(json11::Json j, std::string &err) {
c@75:         checkTypeField(j, "list", err);
c@75:     }
c@75: 
c@97:     static ListResponse
c@75:     toRpcResponse_List(json11::Json j, std::string &err) {
c@75: 
c@97:         ListResponse resp;
c@75:         if (successful(j, err) && !failed(err)) {
c@75:             for (const auto &a: j["result"]["available"].array_items()) {
c@75:                 resp.available.push_back(toPluginStaticData(a, err));
c@75:                 if (failed(err)) return {};
c@75:             }
c@75:         }
c@75:         
c@75:         return resp;
c@75:     }
c@75: 
c@97:     static LoadRequest
c@75:     toRpcRequest_Load(json11::Json j, std::string &err) {
c@75:         
c@75:         checkTypeField(j, "load", err);
c@75:         if (failed(err)) return {};
c@75:         return toLoadRequest(j["params"], err);
c@75:     }
c@75:     
c@97:     static LoadResponse
c@75:     toRpcResponse_Load(json11::Json j,
c@75:                         const PluginHandleMapper &pmapper,
c@75:                         std::string &err) {
c@75:         
c@97:         LoadResponse resp;
c@75:         if (successful(j, err) && !failed(err)) {
c@75:             resp = toLoadResponse(j["result"], pmapper, err);
c@75:         }
c@75:         return resp;
c@75:     }
c@75:     
c@97:     static ConfigurationRequest
c@75:     toRpcRequest_Configure(json11::Json j,
c@75:                             const PluginHandleMapper &pmapper,
c@75:                             std::string &err) {
c@75:         
c@75:         checkTypeField(j, "configure", err);
c@75:         if (failed(err)) return {};
c@75:         return toConfigurationRequest(j["params"], pmapper, err);
c@75:     }
c@75:     
c@97:     static ConfigurationResponse
c@75:     toRpcResponse_Configure(json11::Json j,
c@75:                              const PluginHandleMapper &pmapper,
c@75:                              std::string &err) {
c@75:         
c@97:         ConfigurationResponse resp;
c@75:         if (successful(j, err) && !failed(err)) {
c@75:             resp = toConfigurationResponse(j["result"], pmapper, err);
c@75:         }
c@75:         return resp;
c@75:     }
c@75:     
c@97:     static ProcessRequest
c@75:     toRpcRequest_Process(json11::Json j, const PluginHandleMapper &pmapper,
c@75:                           BufferSerialisation &serialisation, std::string &err) {
c@75:         
c@75:         checkTypeField(j, "process", err);
c@75:         if (failed(err)) return {};
c@75:         return toProcessRequest(j["params"], pmapper, serialisation, err);
c@75:     }
c@75:     
c@97:     static ProcessResponse
c@75:     toRpcResponse_Process(json11::Json j,
c@75:                            const PluginHandleMapper &pmapper,
c@75:                            BufferSerialisation &serialisation, std::string &err) {
c@75:         
c@97:         ProcessResponse resp;
c@75:         if (successful(j, err) && !failed(err)) {
c@75:             auto jc = j["result"];
c@75:             auto h = jc["handle"].int_value();
c@75:             resp.plugin = pmapper.handleToPlugin(h);
c@75:             resp.features = toFeatureSet(jc["features"],
c@75:                                          *pmapper.handleToOutputIdMapper(h),
c@75:                                          serialisation, err);
c@75:         }
c@75:         return resp;
c@75:     }
c@75:     
c@97:     static FinishRequest
c@75:     toRpcRequest_Finish(json11::Json j, const PluginHandleMapper &pmapper,
c@75:                          std::string &err) {
c@75:         
c@75:         checkTypeField(j, "finish", err);
c@75:         if (failed(err)) return {};
c@97:         FinishRequest req;
c@75:         req.plugin = pmapper.handleToPlugin
c@75:             (j["params"]["handle"].int_value());
c@75:         return req;
c@75:     }
c@75:     
c@97:     static FinishResponse
c@75:     toRpcResponse_Finish(json11::Json j,
c@97:                          const PluginHandleMapper &pmapper,
c@97:                          BufferSerialisation &serialisation, std::string &err) {
c@75:         
c@97:         FinishResponse resp;
c@75:         if (successful(j, err) && !failed(err)) {
c@75:             auto jc = j["result"];
c@75:             auto h = jc["handle"].int_value();
c@75:             resp.plugin = pmapper.handleToPlugin(h);
c@75:             resp.features = toFeatureSet(jc["features"],
c@75:                                          *pmapper.handleToOutputIdMapper(h),
c@75:                                          serialisation, err);
c@75:         }
c@75:         return resp;
c@75:     }
c@75: };
c@75: 
c@75: }
c@75: 
c@75: #endif