c@5: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
c@5: 
c@5: #ifndef VAMP_JSON_H
c@5: #define VAMP_JSON_H
c@5: 
c@5: #include <vector>
c@5: #include <string>
c@5: #include <sstream>
c@5: #include <stdexcept>
c@5: 
c@5: #include <json11/json11.hpp>
c@5: #include <base-n/include/basen.hpp>
c@5: 
c@5: #include <vamp-hostsdk/Plugin.h>
c@5: #include <vamp-hostsdk/PluginLoader.h>
c@5: 
c@6: /**
c@6:  * Convert the structures laid out in the Vamp SDK classes into JSON
c@6:  * (and back again) following the schema in the vamp-json-schema
c@6:  * project repo.
c@6:  */
c@5: class VampJson
c@5: {
c@5: public:
c@5:     class Failure : virtual public std::runtime_error {
c@5:     public:
c@5:         Failure(std::string s) : runtime_error(s) { }
c@5:     };
c@5:     
c@5:     template <typename T>
c@5:     static json11::Json
c@5:     fromBasicDescriptor(const T &t) {
c@5:         return json11::Json::object { 
c@5:             { "identifier", t.identifier },
c@5:             { "name", t.name },
c@5:             { "description", t.description }
c@5:         };
c@5:     }
c@5: 
c@5:     template <typename T>
c@5:     static void
c@5:     toBasicDescriptor(json11::Json j, T &t) {
c@5:         if (!j.is_object()) {
c@5:             throw Failure("object expected for basic descriptor content");
c@5:         }
c@5:         if (!j["identifier"].is_string()) {
c@5:             throw Failure("string expected for identifier");
c@5:         }
c@5:         t.identifier = j["identifier"].string_value();
c@5:         t.name = j["name"].string_value();
c@5:         t.description = j["description"].string_value();
c@5:     }
c@5: 
c@5:     template <typename T>
c@5:     static json11::Json
c@5:     fromValueExtents(const T &t) {
c@5:         return json11::Json::object {
c@5:             { "min", t.minValue },
c@5:             { "max", t.maxValue }
c@5:         };
c@5:     }
c@5: 
c@5:     template <typename T>
c@5:     static bool
c@5:     toValueExtents(json11::Json j, T &t) {
c@5:         if (j["extents"].is_null()) {
c@5:             return false;
c@5:         } else if (j["extents"].is_object()) {
c@5:             if (j["extents"]["min"].is_number() &&
c@5:                 j["extents"]["max"].is_number()) {
c@5:                 t.minValue = j["extents"]["min"].number_value();
c@5:                 t.maxValue = j["extents"]["max"].number_value();
c@5:                 return true;
c@5:             } else {
c@5:                 throw Failure("numbers expected for min and max");
c@5:             }
c@5:         } else {
c@5:             throw Failure("object expected for extents (if present)");
c@5:         }
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromRealTime(const Vamp::RealTime &r) {
c@5:         return json11::Json::object {
c@5:             { "s", r.sec },
c@5:             { "n", r.nsec }
c@5:         };
c@5:     }
c@5: 
c@5:     static Vamp::RealTime
c@5:     toRealTime(json11::Json j) {
c@5:         json11::Json sec = j["s"];
c@5:         json11::Json nsec = j["n"];
c@5:         if (!sec.is_number() || !nsec.is_number()) {
c@5:             throw Failure("invalid Vamp::RealTime object " + j.dump());
c@5:         }
c@5:         return Vamp::RealTime(sec.int_value(), nsec.int_value());
c@5:     }
c@5: 
c@5:     static std::string
c@5:     fromSampleType(Vamp::Plugin::OutputDescriptor::SampleType type) {
c@5:         switch (type) {
c@5:         case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
c@5:             return "OneSamplePerStep";
c@5:         case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
c@5:             return "FixedSampleRate";
c@5:         case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
c@5:             return "VariableSampleRate";
c@5:         }
c@5:         return "";
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::OutputDescriptor::SampleType
c@5:     toSampleType(std::string text) {
c@5:         if (text == "OneSamplePerStep") {
c@5:             return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
c@5:         } else if (text == "FixedSampleRate") {
c@5:             return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
c@5:         } else if (text == "VariableSampleRate") {
c@5:             return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
c@5:         } else {
c@5:             throw Failure("invalid sample type string: " + text);
c@5:         }
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
c@5:         json11::Json::object jo {
c@5:             { "basic", fromBasicDescriptor(desc) },
c@5:             { "unit", desc.unit },
c@5:             { "sampleType", fromSampleType(desc.sampleType) },
c@5:             { "sampleRate", desc.sampleRate },
c@5:             { "hasDuration", desc.hasDuration }
c@5:         };
c@5:         if (desc.hasFixedBinCount) {
c@5:             jo["binCount"] = int(desc.binCount);
c@5:             jo["binNames"] = json11::Json::array
c@5:                 (desc.binNames.begin(), desc.binNames.end());
c@5:         }
c@5:         if (desc.hasKnownExtents) {
c@5:             jo["extents"] = fromValueExtents(desc);
c@5:         }
c@5:         if (desc.isQuantized) {
c@5:             jo["quantizeStep"] = desc.quantizeStep;
c@5:         }
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::OutputDescriptor
c@5:     toOutputDescriptor(json11::Json j) {
c@5: 
c@5:         Vamp::Plugin::OutputDescriptor od;
c@5:         if (!j.is_object()) {
c@5:             throw Failure("object expected for output descriptor");
c@5:         }
c@5:     
c@5:         toBasicDescriptor(j["basic"], od);
c@5: 
c@5:         od.unit = j["unit"].string_value();
c@5: 
c@5:         od.sampleType = toSampleType(j["sampleType"].string_value());
c@5: 
c@5:         if (!j["sampleRate"].is_number()) {
c@5:             throw Failure("number expected for sample rate");
c@5:         }
c@5:         od.sampleRate = j["sampleRate"].number_value();
c@5:         od.hasDuration = j["hasDuration"].bool_value();
c@5: 
c@5:         if (j["binCount"].is_number() && j["binCount"].int_value() > 0) {
c@5:             od.hasFixedBinCount = true;
c@5:             od.binCount = j["binCount"].int_value();
c@5:             for (auto &n: j["binNames"].array_items()) {
c@5:                 if (!n.is_string()) {
c@5:                     throw Failure("string expected for bin name");
c@5:                 }
c@5:                 od.binNames.push_back(n.string_value());
c@5:             }
c@5:         } else {
c@5:             od.hasFixedBinCount = false;
c@5:         }
c@5: 
c@5:         bool extentsPresent = toValueExtents(j, od);
c@5:         od.hasKnownExtents = extentsPresent;
c@5: 
c@5:         if (j["quantizeStep"].is_number()) {
c@5:             od.isQuantized = true;
c@5:             od.quantizeStep = j["quantizeStep"].number_value();
c@5:         } else {
c@5:             od.isQuantized = false;
c@5:         }
c@5: 
c@5:         return od;
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromParameterDescriptor(const Vamp::PluginBase::ParameterDescriptor &desc) {
c@5: 
c@5:         json11::Json::object jo {
c@5:             { "basic", fromBasicDescriptor(desc) },
c@5:             { "unit", desc.unit },
c@5:             { "extents", fromValueExtents(desc) },
c@5:             { "defaultValue", desc.defaultValue },
c@5:             { "valueNames", json11::Json::array
c@5:                     (desc.valueNames.begin(), desc.valueNames.end()) }
c@5:         };
c@5:         if (desc.isQuantized) {
c@5:             jo["quantizeStep"] = desc.quantizeStep;
c@5:         }
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::PluginBase::ParameterDescriptor
c@5:     toParameterDescriptor(json11::Json j) {
c@5: 
c@5:         Vamp::PluginBase::ParameterDescriptor pd;
c@5:         if (!j.is_object()) {
c@5:             throw Failure("object expected for parameter descriptor");
c@5:         }
c@5:     
c@5:         toBasicDescriptor(j["basic"], pd);
c@5: 
c@5:         pd.unit = j["unit"].string_value();
c@5: 
c@5:         bool extentsPresent = toValueExtents(j, pd);
c@5:         if (!extentsPresent) {
c@5:             throw Failure("extents must be present in parameter descriptor");
c@5:         }
c@5:     
c@5:         if (!j["defaultValue"].is_number()) {
c@5:             throw Failure("number expected for default value");
c@5:         }
c@5:     
c@5:         pd.defaultValue = j["defaultValue"].number_value();
c@5: 
c@5:         pd.valueNames.clear();
c@5:         for (auto &n: j["valueNames"].array_items()) {
c@5:             if (!n.is_string()) {
c@5:                 throw Failure("string expected for value name");
c@5:             }
c@5:             pd.valueNames.push_back(n.string_value());
c@5:         }
c@5: 
c@5:         if (j["quantizeStep"].is_number()) {
c@5:             pd.isQuantized = true;
c@5:             pd.quantizeStep = j["quantizeStep"].number_value();
c@5:         } else {
c@5:             pd.isQuantized = false;
c@5:         }
c@5: 
c@5:         return pd;
c@5:     }
c@5: 
c@5:     static std::string
c@5:     fromFloatBuffer(const float *buffer, size_t nfloats) {
c@5:         // must use char pointers, otherwise the converter will only
c@5:         // encode every 4th byte (as it will count up in float* steps)
c@5:         const char *start = reinterpret_cast<const char *>(buffer);
c@5:         const char *end = reinterpret_cast<const char *>(buffer + nfloats);
c@5:         std::string encoded;
c@5:         bn::encode_b64(start, end, back_inserter(encoded));
c@5:         return encoded;
c@5:     }
c@5: 
c@5:     static std::vector<float>
c@5:     toFloatBuffer(std::string encoded) {
c@5:         std::string decoded;
c@5:         bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded));
c@5:         const float *buffer = reinterpret_cast<const float *>(decoded.c_str());
c@5:         size_t n = decoded.size() / sizeof(float);
c@5:         return std::vector<float>(buffer, buffer + n);
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromFeature(const Vamp::Plugin::Feature &f) {
c@5: 
c@5:         json11::Json::object jo;
c@5:         if (f.values.size() > 0) {
c@5:             jo["b64values"] = fromFloatBuffer(f.values.data(), f.values.size());
c@5:         }
c@5:         if (f.label != "") {
c@5:             jo["label"] = f.label;
c@5:         }
c@5:         if (f.hasTimestamp) {
c@5:             jo["timestamp"] = fromRealTime(f.timestamp);
c@5:         }
c@5:         if (f.hasDuration) {
c@5:             jo["duration"] = fromRealTime(f.duration);
c@5:         }
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::Feature
c@5:     toFeature(json11::Json j) {
c@5: 
c@5:         Vamp::Plugin::Feature f;
c@5:         if (!j.is_object()) {
c@5:             throw Failure("object expected for feature");
c@5:         }
c@5:         if (j["timestamp"].is_object()) {
c@5:             f.timestamp = toRealTime(j["timestamp"]);
c@5:             f.hasTimestamp = true;
c@5:         }
c@5:         if (j["duration"].is_object()) {
c@5:             f.duration = toRealTime(j["duration"]);
c@5:             f.hasDuration = true;
c@5:         }
c@5:         if (j["b64values"].is_string()) {
c@5:             f.values = toFloatBuffer(j["b64values"].string_value());
c@5:         } else if (j["values"].is_array()) {
c@5:             for (auto v : j["values"].array_items()) {
c@5:                 f.values.push_back(v.number_value());
c@5:             }
c@5:         }
c@5:         f.label = j["label"].string_value();
c@5:         return f;
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromFeatureSet(const Vamp::Plugin::FeatureSet &fs) {
c@5: 
c@5:         json11::Json::object jo;
c@5:         for (const auto &fsi : fs) {
c@5:             std::vector<json11::Json> fj;
c@5:             for (const Vamp::Plugin::Feature &f: fsi.second) {
c@5:                 fj.push_back(fromFeature(f));
c@5:             }
c@5:             std::stringstream sstr;
c@5:             sstr << fsi.first;
c@5:             std::string n = sstr.str();
c@5:             jo[n] = fj;
c@5:         }
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::FeatureList
c@5:     toFeatureList(json11::Json j) {
c@5: 
c@5:         Vamp::Plugin::FeatureList fl;
c@5:         if (!j.is_array()) {
c@5:             throw Failure("array expected for feature list");
c@5:         }
c@5:         for (const json11::Json &fj : j.array_items()) {
c@5:             fl.push_back(toFeature(fj));
c@5:         }
c@5:         return fl;
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::FeatureSet
c@5:     toFeatureSet(json11::Json j) {
c@5: 
c@5:         Vamp::Plugin::FeatureSet fs;
c@5:         if (!j.is_object()) {
c@5:             throw Failure("object expected for feature set");
c@5:         }
c@5:         for (auto &entry : j.object_items()) {
c@5:             std::string nstr = entry.first;
c@5:             size_t count = 0;
c@5:             int n = stoi(nstr, &count);
c@5:             if (n < 0 || fs.find(n) != fs.end() || count < nstr.size()) {
c@5:                 throw Failure("invalid or duplicate numerical index for output");
c@5:             }
c@5:             fs[n] = toFeatureList(entry.second);
c@5:         }
c@5:         return fs;
c@5:     }
c@5: 
c@5:     static std::string
c@5:     fromInputDomain(Vamp::Plugin::InputDomain domain) {
c@5: 
c@5:         switch (domain) {
c@5:         case Vamp::Plugin::TimeDomain:
c@5:             return "TimeDomain";
c@5:         case Vamp::Plugin::FrequencyDomain:
c@5:             return "FrequencyDomain";
c@5:         }
c@5:         return "";
c@5:     }
c@5: 
c@5:     static Vamp::Plugin::InputDomain
c@5:     toInputDomain(std::string text) {
c@5: 
c@5:         if (text == "TimeDomain") {
c@5:             return Vamp::Plugin::TimeDomain;
c@5:         } else if (text == "FrequencyDomain") {
c@5:             return Vamp::Plugin::FrequencyDomain;
c@5:         } else {
c@5:             throw Failure("invalid input domain string: " + text);
c@5:         }
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromPluginStaticData(const Vamp::HostExt::PluginStaticData &d) {
c@5: 
c@5:         json11::Json::object jo;
c@5:         jo["pluginKey"] = d.pluginKey;
c@5:         jo["basic"] = fromBasicDescriptor(d.basic);
c@5:         jo["maker"] = d.maker;
c@5:         jo["copyright"] = d.copyright;
c@5:         jo["pluginVersion"] = d.pluginVersion;
c@5: 
c@5:         json11::Json::array cat;
c@5:         for (const std::string &c: d.category) cat.push_back(c);
c@5:         jo["category"] = cat;
c@5: 
c@5:         jo["minChannelCount"] = d.minChannelCount;
c@5:         jo["maxChannelCount"] = d.maxChannelCount;
c@5: 
c@5:         json11::Json::array params;
c@5:         Vamp::PluginBase::ParameterList vparams = d.parameters;
c@5:         for (auto &p: vparams) params.push_back(fromParameterDescriptor(p));
c@5:         jo["parameters"] = params;
c@5: 
c@5:         json11::Json::array progs;
c@5:         Vamp::PluginBase::ProgramList vprogs = d.programs;
c@5:         for (auto &p: vprogs) progs.push_back(p);
c@5:         jo["programs"] = progs;
c@5: 
c@5:         jo["inputDomain"] = fromInputDomain(d.inputDomain);
c@5: 
c@5:         json11::Json::array outinfo;
c@5:         auto vouts = d.basicOutputInfo;
c@5:         for (auto &o: vouts) outinfo.push_back(fromBasicDescriptor(o));
c@5:         jo["basicOutputInfo"] = outinfo;
c@5:     
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::HostExt::PluginStaticData
c@5:     toPluginStaticData(json11::Json j) {
c@5: 
c@5:         std::string err;
c@5:         if (!j.has_shape({
c@5:                     { "pluginKey", json11::Json::STRING },
c@5:                     { "pluginVersion", json11::Json::NUMBER },
c@5:                     { "minChannelCount", json11::Json::NUMBER },
c@5:                     { "maxChannelCount", json11::Json::NUMBER },
c@5:                     { "inputDomain", json11::Json::STRING }}, err)) {
c@5:             throw Failure("malformed plugin static data: " + err);
c@5:         }
c@5: 
c@5:         if (!j["basicOutputInfo"].is_array()) {
c@5:             throw Failure("array expected for basic output info");
c@5:         }
c@5: 
c@5:         if (!j["maker"].is_null() &&
c@5:             !j["maker"].is_string()) {
c@5:             throw Failure("string expected for maker");
c@5:         }
c@5:         
c@5:         if (!j["copyright"].is_null() &&
c@5:             !j["copyright"].is_string()) {
c@5:             throw Failure("string expected for copyright");
c@5:         }
c@5: 
c@5:         if (!j["category"].is_null() &&
c@5:             !j["category"].is_array()) {
c@5:             throw Failure("array expected for category");
c@5:         }
c@5: 
c@5:         if (!j["parameters"].is_null() &&
c@5:             !j["parameters"].is_array()) {
c@5:             throw Failure("array expected for parameters");
c@5:         }
c@5: 
c@5:         if (!j["programs"].is_null() &&
c@5:             !j["programs"].is_array()) {
c@5:             throw Failure("array expected for programs");
c@5:         }
c@5: 
c@5:         if (!j["inputDomain"].is_null() &&
c@5:             !j["inputDomain"].is_string()) {
c@5:             throw Failure("string expected for inputDomain");
c@5:         }
c@5: 
c@5:         if (!j["basicOutputInfo"].is_null() &&
c@5:             !j["basicOutputInfo"].is_array()) {
c@5:             throw Failure("array expected for basicOutputInfo");
c@5:         }
c@5: 
c@5:         Vamp::HostExt::PluginStaticData psd;
c@5: 
c@5:         psd.pluginKey = j["pluginKey"].string_value();
c@5: 
c@5:         toBasicDescriptor(j["basic"], psd.basic);
c@5: 
c@5:         psd.maker = j["maker"].string_value();
c@5:         psd.copyright = j["copyright"].string_value();
c@5:         psd.pluginVersion = j["pluginVersion"].int_value();
c@5: 
c@5:         for (const auto &c : j["category"].array_items()) {
c@5:             if (!c.is_string()) {
c@5:                 throw Failure("strings expected in category array");
c@5:             }
c@5:             psd.category.push_back(c.string_value());
c@5:         }
c@5: 
c@5:         psd.minChannelCount = j["minChannelCount"].int_value();
c@5:         psd.maxChannelCount = j["maxChannelCount"].int_value();
c@5: 
c@5:         for (const auto &p : j["parameters"].array_items()) {
c@5:             auto pd = toParameterDescriptor(p);
c@5:             psd.parameters.push_back(pd);
c@5:         }
c@5: 
c@5:         for (const auto &p : j["programs"].array_items()) {
c@5:             if (!p.is_string()) {
c@5:                 throw Failure("strings expected in programs array");
c@5:             }
c@5:             psd.programs.push_back(p.string_value());
c@5:         }
c@5: 
c@5:         psd.inputDomain = toInputDomain(j["inputDomain"].string_value());
c@5: 
c@5:         for (const auto &bo : j["basicOutputInfo"].array_items()) {
c@5:             Vamp::HostExt::PluginStaticData::Basic b;
c@5:             toBasicDescriptor(bo, b);
c@5:             psd.basicOutputInfo.push_back(b);
c@5:         }
c@5: 
c@5:         return psd;
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromPluginConfiguration(const Vamp::HostExt::PluginConfiguration &c) {
c@5: 
c@5:         json11::Json::object jo;
c@5: 
c@5:         json11::Json::object paramValues;
c@5:         for (auto &vp: c.parameterValues) {
c@5:             paramValues[vp.first] = vp.second;
c@5:         }
c@5:         jo["parameterValues"] = paramValues;
c@5: 
c@5:         if (c.currentProgram != "") {
c@5:             jo["currentProgram"] = c.currentProgram;
c@5:         }
c@5: 
c@5:         jo["channelCount"] = c.channelCount;
c@5:         jo["stepSize"] = c.stepSize;
c@5:         jo["blockSize"] = c.blockSize;
c@5:     
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::HostExt::PluginConfiguration
c@5:     toPluginConfiguration(json11::Json j) {
c@5:         
c@5:         std::string err;
c@5:         if (!j.has_shape({
c@5:                     { "channelCount", json11::Json::NUMBER },
c@5:                     { "stepSize", json11::Json::NUMBER },
c@5:                     { "blockSize", json11::Json::NUMBER } }, err)) {
c@5:             throw Failure("malformed plugin configuration: " + err);
c@5:         }
c@5: 
c@5:         if (!j["parameterValues"].is_null() &&
c@5:             !j["parameterValues"].is_object()) {
c@5:             throw Failure("object expected for parameter values");
c@5:         }
c@5: 
c@5:         for (auto &pv : j["parameterValues"].object_items()) {
c@5:             if (!pv.second.is_number()) {
c@5:                 throw Failure("number expected for parameter value");
c@5:             }
c@5:         }
c@5:     
c@5:         if (!j["currentProgram"].is_null() &&
c@5:             !j["currentProgram"].is_string()) {
c@5:             throw Failure("string expected for program name");
c@5:         }
c@5: 
c@5:         Vamp::HostExt::PluginConfiguration config;
c@5: 
c@5:         config.channelCount = j["channelCount"].number_value();
c@5:         config.stepSize = j["stepSize"].number_value();
c@5:         config.blockSize = j["blockSize"].number_value();
c@5:         
c@5:         for (auto &pv : j["parameterValues"].object_items()) {
c@5:             config.parameterValues[pv.first] = pv.second.number_value();
c@5:         }
c@5: 
c@5:         if (j["currentProgram"].is_string()) {
c@5:             config.currentProgram = j["currentProgram"].string_value();
c@5:         }
c@5: 
c@5:         return config;
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromAdapterFlags(int flags) {
c@5: 
c@5:         json11::Json::array arr;
c@5: 
c@5:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN) {
c@5:             arr.push_back("AdaptInputDomain");
c@5:         }
c@5:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT) {
c@5:             arr.push_back("AdaptChannelCount");
c@5:         }
c@5:         if (flags & Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE) {
c@5:             arr.push_back("AdaptBufferSize");
c@5:         }
c@5: 
c@5:         return json11::Json(arr);
c@5:     }
c@5: 
c@5:     static Vamp::HostExt::PluginLoader::AdapterFlags
c@5:     toAdapterFlags(json11::Json j) {
c@5: 
c@5:         if (!j.is_array()) {
c@5:             throw Failure("array expected for adapter flags");
c@5:         }
c@5:         int flags = 0x0;
c@5: 
c@5:         for (auto &jj: j.array_items()) {
c@5:             if (!jj.is_string()) {
c@5:                 throw Failure("string expected for adapter flag");
c@5:             }
c@5:             std::string text = jj.string_value();
c@5:             if (text == "AdaptInputDomain") {
c@5:                 flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
c@5:             } else if (text == "AdaptChannelCount") {
c@5:                 flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
c@5:             } else if (text == "AdaptBufferSize") {
c@5:                 flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
c@5:             } else if (text == "AdaptAllSafe") {
c@5:                 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE;
c@5:             } else if (text == "AdaptAll") {
c@5:                 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL;
c@5:             } else {
c@5:                 throw Failure("invalid adapter flag string: " + text);
c@5:             }
c@5:         }
c@5: 
c@5:         return Vamp::HostExt::PluginLoader::AdapterFlags(flags);
c@5:     }
c@5: 
c@5:     static json11::Json
c@5:     fromLoadRequest(Vamp::HostExt::LoadRequest req) {
c@5: 
c@5:         json11::Json::object jo;
c@5:         jo["pluginKey"] = req.pluginKey;
c@5:         jo["inputSampleRate"] = req.inputSampleRate;
c@5:         jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags);
c@5:         return json11::Json(jo);
c@5:     }
c@5: 
c@5:     static Vamp::HostExt::LoadRequest
c@5:     toLoadRequest(json11::Json j) {
c@5:         
c@5:         std::string err;
c@5: 
c@5:         if (!j.has_shape({
c@5:                     { "pluginKey", json11::Json::STRING },
c@5:                     { "inputSampleRate", json11::Json::NUMBER },
c@5:                     { "adapterFlags", json11::Json::ARRAY } }, err)) {
c@5:             throw VampJson::Failure("malformed load request: " + err);
c@5:         }
c@5:     
c@5:         Vamp::HostExt::LoadRequest req;
c@5:         req.pluginKey = j["pluginKey"].string_value();
c@5:         req.inputSampleRate = j["inputSampleRate"].number_value();
c@5:         req.adapterFlags = toAdapterFlags(j["adapterFlags"]);
c@5:         return req;
c@5:     }
c@5: };
c@5: 
c@5: 
c@5: #endif