annotate vamp-json/VampJson.h @ 289:26027c3a99a0

Further wiring for ProgramParameters - should now be supported throughout
author Chris Cannam <cannam@all-day-breakfast.com>
date Wed, 08 Apr 2020 15:02:24 +0100
parents 292ec9b50280
children
rev   line source
c@75 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@75 2
c@75 3 /*
c@75 4 Piper C++
c@75 5
c@75 6 Centre for Digital Music, Queen Mary, University of London.
cannam@220 7 Copyright 2015-2017 QMUL.
c@75 8
c@75 9 Permission is hereby granted, free of charge, to any person
c@75 10 obtaining a copy of this software and associated documentation
c@75 11 files (the "Software"), to deal in the Software without
c@75 12 restriction, including without limitation the rights to use, copy,
c@75 13 modify, merge, publish, distribute, sublicense, and/or sell copies
c@75 14 of the Software, and to permit persons to whom the Software is
c@75 15 furnished to do so, subject to the following conditions:
c@75 16
c@75 17 The above copyright notice and this permission notice shall be
c@75 18 included in all copies or substantial portions of the Software.
c@75 19
c@75 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@75 21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@75 22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@75 23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@75 24 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@75 25 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@75 26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@75 27
c@75 28 Except as contained in this notice, the names of the Centre for
c@75 29 Digital Music; Queen Mary, University of London; and Chris Cannam
c@75 30 shall not be used in advertising or otherwise to promote the sale,
c@75 31 use or other dealings in this Software without prior written
c@75 32 authorization.
c@75 33 */
c@75 34
c@75 35 #ifndef PIPER_VAMP_JSON_H
c@75 36 #define PIPER_VAMP_JSON_H
c@75 37
c@75 38 #include <vector>
c@75 39 #include <string>
c@75 40 #include <sstream>
c@122 41 #include <iterator>
c@139 42 #include <cmath>
c@75 43
c@75 44 #include <json11/json11.hpp>
c@75 45 #include <base-n/include/basen.hpp>
c@75 46
c@75 47 #include <vamp-hostsdk/Plugin.h>
c@75 48 #include <vamp-hostsdk/PluginLoader.h>
c@97 49
cannam@220 50 #include "vamp-support/StaticOutputDescriptor.h"
c@97 51 #include "vamp-support/PluginStaticData.h"
c@97 52 #include "vamp-support/PluginConfiguration.h"
c@97 53 #include "vamp-support/RequestResponse.h"
c@75 54
c@75 55 #include "vamp-support/PluginHandleMapper.h"
c@75 56 #include "vamp-support/PluginOutputIdMapper.h"
c@75 57 #include "vamp-support/RequestResponseType.h"
c@75 58
c@97 59 namespace piper_vamp {
c@75 60
c@75 61 /**
c@75 62 * Convert the structures laid out in the Vamp SDK classes into JSON
c@75 63 * (and back again) following the schema in the vamp-json-schema
c@75 64 * project repo.
c@75 65 *
c@75 66 * Functions with names starting "from" convert from a Vamp SDK object
c@75 67 * to JSON output. Most of them return a json11::Json object, with a
c@75 68 * few exceptions for low-level utilities that return a string. These
c@75 69 * functions succeed all of the time.
c@75 70 *
c@75 71 * Functions with names starting "to" convert to a Vamp SDK object
c@75 72 * from JSON input. These functions all accept a json11::Json object
c@75 73 * as first argument, with a few exceptions for low-level utilities
c@75 74 * that accept a string. These functions all accept a string reference
c@75 75 * as a final argument and return an error string through it if the
c@75 76 * conversion fails. If conversion fails the return value is
c@75 77 * undefined, and any returned object may be incomplete or
c@75 78 * invalid. Callers should check for an empty error string (indicating
c@75 79 * success) before using the returned value.
c@75 80 */
c@75 81
c@75 82 class VampJson
c@75 83 {
c@75 84 public:
c@75 85 /** Serialisation format for arrays of floats (process input and
c@75 86 * feature values). Wherever such an array appears, it may
c@75 87 * alternatively be replaced by a single string containing a
c@75 88 * base-64 encoding of the IEEE float buffer. When parsing, if a
c@75 89 * string is found instead of an array in this case, it will be
c@75 90 * interpreted as a base-64 encoded buffer. Only array or base-64
c@75 91 * encoding may be provided, not both.
c@75 92 */
c@75 93 enum class BufferSerialisation {
c@75 94
c@75 95 /** Default JSON serialisation of values in array form. This
c@75 96 * is relatively slow to parse and serialise, and can take a
c@75 97 * lot of space.
c@75 98 */
c@75 99 Array,
c@75 100
c@75 101 /** Base64-encoded string of the raw data as packed
c@75 102 * little-endian IEEE 32-bit floats. Faster and more compact
c@75 103 * than the text encoding but more complicated to
c@75 104 * provide. Note that Base64 serialisations produced by this
c@75 105 * library do not including padding characters and so are not
c@75 106 * necessarily multiples of 4 characters long. You will need
c@75 107 * to pad them yourself if concatenating them or supplying to
c@75 108 * a consumer that expects padding.
c@75 109 */
c@75 110 Base64
c@75 111 };
c@75 112
c@75 113 static bool failed(const std::string &err) {
c@75 114 return err != "";
c@75 115 }
c@75 116
c@75 117 template <typename T>
c@75 118 static json11::Json
c@75 119 fromBasicDescriptor(const T &t) {
c@75 120 return json11::Json::object {
c@75 121 { "identifier", t.identifier },
c@75 122 { "name", t.name },
c@75 123 { "description", t.description }
c@75 124 };
c@75 125 }
c@75 126
c@75 127 template <typename T>
c@75 128 static void
c@75 129 toBasicDescriptor(json11::Json j, T &t, std::string &err) {
c@75 130 if (!j.is_object()) {
c@75 131 err = "object expected for basic descriptor content";
c@75 132 return;
c@75 133 }
c@75 134 if (!j["identifier"].is_string()) {
c@75 135 err = "string expected for identifier";
c@75 136 return;
c@75 137 }
c@75 138 t.identifier = j["identifier"].string_value();
c@75 139 t.name = j["name"].string_value();
c@75 140 t.description = j["description"].string_value();
c@75 141 }
c@75 142
c@75 143 template <typename T>
c@75 144 static json11::Json
c@75 145 fromValueExtents(const T &t) {
c@75 146 return json11::Json::object {
c@75 147 { "min", t.minValue },
c@75 148 { "max", t.maxValue }
c@75 149 };
c@75 150 }
c@75 151
c@75 152 template <typename T>
c@75 153 static bool
c@75 154 toValueExtents(json11::Json j, T &t, std::string &err) {
c@75 155 if (j["extents"].is_null()) {
c@75 156 return false;
c@75 157 } else if (j["extents"].is_object()) {
c@75 158 if (j["extents"]["min"].is_number() &&
c@75 159 j["extents"]["max"].is_number()) {
c@140 160 t.minValue = float(j["extents"]["min"].number_value());
c@140 161 t.maxValue = float(j["extents"]["max"].number_value());
c@75 162 return true;
c@75 163 } else {
c@75 164 err = "numbers expected for min and max";
c@75 165 return false;
c@75 166 }
c@75 167 } else {
c@75 168 err = "object expected for extents (if present)";
c@75 169 return false;
c@75 170 }
c@75 171 }
c@75 172
c@75 173 static json11::Json
c@75 174 fromRealTime(const Vamp::RealTime &r) {
c@75 175 return json11::Json::object {
c@75 176 { "s", r.sec },
c@75 177 { "n", r.nsec }
c@75 178 };
c@75 179 }
c@75 180
c@75 181 static Vamp::RealTime
c@75 182 toRealTime(json11::Json j, std::string &err) {
c@75 183 json11::Json sec = j["s"];
c@75 184 json11::Json nsec = j["n"];
c@75 185 if (!sec.is_number() || !nsec.is_number()) {
c@75 186 err = "invalid Vamp::RealTime object " + j.dump();
c@75 187 return {};
c@75 188 }
c@75 189 return Vamp::RealTime(sec.int_value(), nsec.int_value());
c@75 190 }
c@75 191
c@75 192 static std::string
c@75 193 fromSampleType(Vamp::Plugin::OutputDescriptor::SampleType type) {
c@75 194 switch (type) {
c@75 195 case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
c@75 196 return "OneSamplePerStep";
c@75 197 case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
c@75 198 return "FixedSampleRate";
c@75 199 case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
c@75 200 return "VariableSampleRate";
c@75 201 }
c@75 202 return "";
c@75 203 }
c@75 204
c@75 205 static Vamp::Plugin::OutputDescriptor::SampleType
c@75 206 toSampleType(std::string text, std::string &err) {
c@75 207 if (text == "OneSamplePerStep") {
c@75 208 return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
c@75 209 } else if (text == "FixedSampleRate") {
c@75 210 return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
c@75 211 } else if (text == "VariableSampleRate") {
c@75 212 return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
c@75 213 } else {
c@75 214 err = "invalid sample type string: " + text;
c@75 215 return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
c@75 216 }
c@75 217 }
c@75 218
c@75 219 static json11::Json
cannam@220 220 fromStaticOutputDescriptor(const StaticOutputDescriptor &sd) {
cannam@220 221 json11::Json::object jo;
cannam@220 222 if (sd.typeURI != "") {
cannam@220 223 jo["typeURI"] = sd.typeURI;
cannam@220 224 }
cannam@220 225 return json11::Json(jo);
cannam@220 226 }
cannam@220 227
cannam@220 228 static json11::Json
c@75 229 fromConfiguredOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
c@75 230 json11::Json::object jo {
c@75 231 { "unit", desc.unit },
c@75 232 { "sampleType", fromSampleType(desc.sampleType) },
c@75 233 { "sampleRate", desc.sampleRate },
c@75 234 { "hasDuration", desc.hasDuration }
c@75 235 };
c@75 236 if (desc.hasFixedBinCount) {
c@75 237 jo["binCount"] = int(desc.binCount);
c@75 238 jo["binNames"] = json11::Json::array
c@75 239 (desc.binNames.begin(), desc.binNames.end());
c@75 240 }
c@75 241 if (desc.hasKnownExtents) {
c@75 242 jo["extents"] = fromValueExtents(desc);
c@75 243 }
c@75 244 if (desc.isQuantized) {
c@75 245 jo["quantizeStep"] = desc.quantizeStep;
c@75 246 }
c@75 247 return json11::Json(jo);
c@75 248 }
c@75 249
c@75 250 static json11::Json
cannam@220 251 fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc,
cannam@220 252 const StaticOutputDescriptor &sd) {
c@75 253 json11::Json::object jo {
c@75 254 { "basic", fromBasicDescriptor(desc) },
cannam@220 255 { "static", fromStaticOutputDescriptor(sd) },
c@75 256 { "configured", fromConfiguredOutputDescriptor(desc) }
c@75 257 };
c@75 258 return json11::Json(jo);
c@75 259 }
cannam@220 260
cannam@220 261 static StaticOutputDescriptor
cannam@220 262 toStaticOutputDescriptor(json11::Json j, std::string &err) {
cannam@220 263
cannam@220 264 StaticOutputDescriptor sd;
cannam@220 265 if (!j.is_object()) {
cannam@220 266 err = "object expected for static output descriptor";
cannam@233 267 return sd;
cannam@220 268 }
cannam@220 269
cannam@220 270 sd.typeURI = j["typeURI"].string_value();
cannam@220 271 return sd;
cannam@220 272 }
c@75 273
c@75 274 static Vamp::Plugin::OutputDescriptor
c@75 275 toConfiguredOutputDescriptor(json11::Json j, std::string &err) {
c@75 276
c@75 277 Vamp::Plugin::OutputDescriptor od;
c@75 278 if (!j.is_object()) {
c@75 279 err = "object expected for output descriptor";
c@75 280 return {};
c@75 281 }
c@75 282
c@75 283 od.unit = j["unit"].string_value();
c@75 284
c@75 285 od.sampleType = toSampleType(j["sampleType"].string_value(), err);
c@75 286 if (failed(err)) return {};
c@75 287
c@75 288 if (!j["sampleRate"].is_number()) {
c@75 289 err = "number expected for sample rate";
c@75 290 return {};
c@75 291 }
c@139 292 od.sampleRate = float(j["sampleRate"].number_value());
c@75 293 od.hasDuration = j["hasDuration"].bool_value();
c@75 294
c@75 295 if (j["binCount"].is_number() && j["binCount"].int_value() > 0) {
c@75 296 od.hasFixedBinCount = true;
c@75 297 od.binCount = j["binCount"].int_value();
c@75 298 for (auto &n: j["binNames"].array_items()) {
c@75 299 if (!n.is_string()) {
c@75 300 err = "string expected for bin name";
c@75 301 return {};
c@75 302 }
c@75 303 od.binNames.push_back(n.string_value());
c@75 304 }
c@75 305 } else {
c@75 306 od.hasFixedBinCount = false;
c@75 307 }
c@75 308
c@75 309 bool extentsPresent = toValueExtents(j, od, err);
c@75 310 if (failed(err)) return {};
c@75 311
c@75 312 od.hasKnownExtents = extentsPresent;
c@75 313
c@75 314 if (j["quantizeStep"].is_number()) {
c@75 315 od.isQuantized = true;
c@139 316 od.quantizeStep = float(j["quantizeStep"].number_value());
c@75 317 } else {
c@75 318 od.isQuantized = false;
c@75 319 }
c@75 320
c@75 321 return od;
c@75 322 }
c@75 323
cannam@220 324 static std::pair<Vamp::Plugin::OutputDescriptor, StaticOutputDescriptor>
c@75 325 toOutputDescriptor(json11::Json j, std::string &err) {
c@75 326
c@75 327 Vamp::Plugin::OutputDescriptor od;
c@75 328 if (!j.is_object()) {
c@75 329 err = "object expected for output descriptor";
c@75 330 return {};
c@75 331 }
c@75 332
dev@175 333 od = toConfiguredOutputDescriptor(j["configured"], err);
c@75 334 if (failed(err)) return {};
c@75 335
c@75 336 toBasicDescriptor(j["basic"], od, err);
c@75 337 if (failed(err)) return {};
c@75 338
cannam@220 339 StaticOutputDescriptor sd;
cannam@220 340 if (j["static"] != json11::Json()) {
cannam@220 341 sd = toStaticOutputDescriptor(j["static"], err);
cannam@220 342 if (failed(err)) return {};
cannam@220 343 }
cannam@220 344
cannam@220 345 return { od, sd };
c@75 346 }
c@75 347
c@75 348 static json11::Json
c@75 349 fromParameterDescriptor(const Vamp::PluginBase::ParameterDescriptor &desc) {
c@75 350
c@75 351 json11::Json::object jo {
c@75 352 { "basic", fromBasicDescriptor(desc) },
c@75 353 { "unit", desc.unit },
c@75 354 { "extents", fromValueExtents(desc) },
c@75 355 { "defaultValue", desc.defaultValue },
c@75 356 { "valueNames", json11::Json::array
c@75 357 (desc.valueNames.begin(), desc.valueNames.end()) }
c@75 358 };
c@75 359 if (desc.isQuantized) {
c@75 360 jo["quantizeStep"] = desc.quantizeStep;
c@75 361 }
c@75 362 return json11::Json(jo);
c@75 363 }
c@75 364
c@75 365 static Vamp::PluginBase::ParameterDescriptor
c@75 366 toParameterDescriptor(json11::Json j, std::string &err) {
c@75 367
c@75 368 Vamp::PluginBase::ParameterDescriptor pd;
c@75 369 if (!j.is_object()) {
c@75 370 err = "object expected for parameter descriptor";
c@75 371 return {};
c@75 372 }
c@75 373
c@75 374 toBasicDescriptor(j["basic"], pd, err);
c@75 375 if (failed(err)) return {};
c@75 376
c@75 377 pd.unit = j["unit"].string_value();
c@75 378
c@75 379 bool extentsPresent = toValueExtents(j, pd, err);
c@75 380 if (failed(err)) return {};
c@75 381 if (!extentsPresent) {
c@75 382 err = "extents must be present in parameter descriptor";
c@75 383 return {};
c@75 384 }
c@75 385
c@75 386 if (!j["defaultValue"].is_number()) {
c@75 387 err = "number expected for default value";
c@75 388 return {};
c@75 389 }
c@75 390
c@139 391 pd.defaultValue = float(j["defaultValue"].number_value());
c@75 392
c@75 393 pd.valueNames.clear();
c@75 394 for (auto &n: j["valueNames"].array_items()) {
c@75 395 if (!n.is_string()) {
c@75 396 err = "string expected for value name";
c@75 397 return {};
c@75 398 }
c@75 399 pd.valueNames.push_back(n.string_value());
c@75 400 }
c@75 401
c@75 402 if (j["quantizeStep"].is_number()) {
c@75 403 pd.isQuantized = true;
c@139 404 pd.quantizeStep = float(j["quantizeStep"].number_value());
c@75 405 } else {
c@75 406 pd.isQuantized = false;
c@75 407 }
c@75 408
c@75 409 return pd;
c@75 410 }
c@75 411
c@75 412 static std::string
c@75 413 fromFloatBuffer(const float *buffer, size_t nfloats) {
c@75 414 // must use char pointers, otherwise the converter will only
c@75 415 // encode every 4th byte (as it will count up in float* steps)
c@75 416 const char *start = reinterpret_cast<const char *>(buffer);
c@75 417 const char *end = reinterpret_cast<const char *>(buffer + nfloats);
c@75 418 std::string encoded;
c@75 419 bn::encode_b64(start, end, back_inserter(encoded));
c@75 420 return encoded;
c@75 421 }
c@75 422
c@75 423 static std::vector<float>
c@75 424 toFloatBuffer(std::string encoded, std::string & /* err */) {
c@75 425 std::string decoded;
c@75 426 bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded));
c@75 427 const float *buffer = reinterpret_cast<const float *>(decoded.c_str());
c@75 428 size_t n = decoded.size() / sizeof(float);
c@75 429 return std::vector<float>(buffer, buffer + n);
c@75 430 }
c@75 431
c@75 432 static json11::Json
c@75 433 fromFeature(const Vamp::Plugin::Feature &f,
c@75 434 BufferSerialisation serialisation) {
c@75 435
c@75 436 json11::Json::object jo;
c@75 437 if (f.values.size() > 0) {
c@75 438 if (serialisation == BufferSerialisation::Array) {
c@75 439 jo["featureValues"] = json11::Json::array(f.values.begin(),
c@75 440 f.values.end());
c@75 441 } else {
c@75 442 jo["featureValues"] = fromFloatBuffer(f.values.data(),
c@75 443 f.values.size());
c@75 444 }
c@75 445 }
c@75 446 if (f.label != "") {
c@75 447 jo["label"] = f.label;
c@75 448 }
c@75 449 if (f.hasTimestamp) {
c@75 450 jo["timestamp"] = fromRealTime(f.timestamp);
c@75 451 }
c@75 452 if (f.hasDuration) {
c@75 453 jo["duration"] = fromRealTime(f.duration);
c@75 454 }
c@75 455 return json11::Json(jo);
c@75 456 }
c@75 457
c@75 458 static Vamp::Plugin::Feature
c@75 459 toFeature(json11::Json j, BufferSerialisation &serialisation, std::string &err) {
c@75 460
c@75 461 Vamp::Plugin::Feature f;
c@75 462 if (!j.is_object()) {
c@75 463 err = "object expected for feature";
c@75 464 return {};
c@75 465 }
c@75 466 if (j["timestamp"].is_object()) {
c@75 467 f.timestamp = toRealTime(j["timestamp"], err);
c@75 468 if (failed(err)) return {};
c@75 469 f.hasTimestamp = true;
c@75 470 }
c@75 471 if (j["duration"].is_object()) {
c@75 472 f.duration = toRealTime(j["duration"], err);
c@75 473 if (failed(err)) return {};
c@75 474 f.hasDuration = true;
c@75 475 }
c@75 476 if (j["featureValues"].is_string()) {
c@75 477 f.values = toFloatBuffer(j["featureValues"].string_value(), err);
c@75 478 if (failed(err)) return {};
c@75 479 serialisation = BufferSerialisation::Base64;
c@75 480 } else if (j["featureValues"].is_array()) {
c@75 481 for (auto v : j["featureValues"].array_items()) {
c@139 482 f.values.push_back(float(v.number_value()));
c@75 483 }
c@75 484 serialisation = BufferSerialisation::Array;
c@75 485 }
c@75 486 f.label = j["label"].string_value();
c@75 487 return f;
c@75 488 }
c@75 489
c@75 490 static json11::Json
c@75 491 fromFeatureSet(const Vamp::Plugin::FeatureSet &fs,
c@75 492 const PluginOutputIdMapper &omapper,
c@75 493 BufferSerialisation serialisation) {
c@75 494
c@75 495 json11::Json::object jo;
c@75 496 for (const auto &fsi : fs) {
c@75 497 std::vector<json11::Json> fj;
c@75 498 for (const Vamp::Plugin::Feature &f: fsi.second) {
c@75 499 fj.push_back(fromFeature(f, serialisation));
c@75 500 }
c@75 501 jo[omapper.indexToId(fsi.first)] = fj;
c@75 502 }
c@75 503 return json11::Json(jo);
c@75 504 }
c@75 505
c@75 506 static Vamp::Plugin::FeatureList
c@75 507 toFeatureList(json11::Json j,
c@75 508 BufferSerialisation &serialisation, std::string &err) {
c@75 509
c@75 510 Vamp::Plugin::FeatureList fl;
c@75 511 if (!j.is_array()) {
c@75 512 err = "array expected for feature list";
cannam@203 513 return fl;
c@75 514 }
c@75 515 for (const json11::Json &fj : j.array_items()) {
c@75 516 fl.push_back(toFeature(fj, serialisation, err));
cannam@203 517 if (failed(err)) return fl;
c@75 518 }
c@75 519 return fl;
c@75 520 }
c@75 521
c@75 522 static Vamp::Plugin::FeatureSet
c@75 523 toFeatureSet(json11::Json j,
c@75 524 const PluginOutputIdMapper &omapper,
c@75 525 BufferSerialisation &serialisation,
c@75 526 std::string &err) {
c@75 527
c@75 528 Vamp::Plugin::FeatureSet fs;
c@75 529 if (!j.is_object()) {
c@75 530 err = "object expected for feature set";
cannam@203 531 return fs;
c@75 532 }
c@75 533 for (auto &entry : j.object_items()) {
c@75 534 int n = omapper.idToIndex(entry.first);
c@75 535 if (fs.find(n) != fs.end()) {
c@75 536 err = "duplicate numerical index for output";
cannam@203 537 return fs;
c@75 538 }
c@75 539 fs[n] = toFeatureList(entry.second, serialisation, err);
cannam@203 540 if (failed(err)) return fs;
c@75 541 }
c@75 542 return fs;
c@75 543 }
c@75 544
c@75 545 static std::string
c@75 546 fromInputDomain(Vamp::Plugin::InputDomain domain) {
c@75 547
c@75 548 switch (domain) {
c@75 549 case Vamp::Plugin::TimeDomain:
c@75 550 return "TimeDomain";
c@75 551 case Vamp::Plugin::FrequencyDomain:
c@75 552 return "FrequencyDomain";
c@75 553 }
c@75 554 return "";
c@75 555 }
c@75 556
c@75 557 static Vamp::Plugin::InputDomain
c@75 558 toInputDomain(std::string text, std::string &err) {
c@75 559
c@75 560 if (text == "TimeDomain") {
c@75 561 return Vamp::Plugin::TimeDomain;
c@75 562 } else if (text == "FrequencyDomain") {
c@75 563 return Vamp::Plugin::FrequencyDomain;
c@75 564 } else {
c@75 565 err = "invalid input domain string: " + text;
c@75 566 return {};
c@75 567 }
c@75 568 }
c@75 569
c@75 570 static json11::Json
c@97 571 fromPluginStaticData(const PluginStaticData &d) {
c@75 572
c@75 573 json11::Json::object jo;
c@75 574 jo["key"] = d.pluginKey;
c@75 575 jo["basic"] = fromBasicDescriptor(d.basic);
c@75 576 jo["maker"] = d.maker;
cannam@216 577 jo["rights"] = d.copyright;
c@75 578 jo["version"] = d.pluginVersion;
c@75 579
c@75 580 json11::Json::array cat;
c@75 581 for (const std::string &c: d.category) cat.push_back(c);
c@75 582 jo["category"] = cat;
c@75 583
c@105 584 jo["minChannelCount"] = int(d.minChannelCount);
c@105 585 jo["maxChannelCount"] = int(d.maxChannelCount);
c@75 586
c@75 587 json11::Json::array params;
c@75 588 Vamp::PluginBase::ParameterList vparams = d.parameters;
c@75 589 for (auto &p: vparams) params.push_back(fromParameterDescriptor(p));
c@75 590 jo["parameters"] = params;
c@75 591
c@75 592 json11::Json::array progs;
c@75 593 Vamp::PluginBase::ProgramList vprogs = d.programs;
c@75 594 for (auto &p: vprogs) progs.push_back(p);
c@75 595 jo["programs"] = progs;
c@75 596
c@75 597 jo["inputDomain"] = fromInputDomain(d.inputDomain);
c@75 598
c@75 599 json11::Json::array outinfo;
c@75 600 auto vouts = d.basicOutputInfo;
c@75 601 for (auto &o: vouts) outinfo.push_back(fromBasicDescriptor(o));
c@75 602 jo["basicOutputInfo"] = outinfo;
cannam@220 603
cannam@220 604 json11::Json::object statinfo;
cannam@220 605 auto souts = d.staticOutputInfo;
cannam@221 606 for (auto &s: souts) {
cannam@221 607 statinfo[s.first] = fromStaticOutputDescriptor(s.second);
cannam@221 608 }
cannam@221 609 jo["staticOutputInfo"] = statinfo;
cannam@220 610
c@75 611 return json11::Json(jo);
c@75 612 }
c@75 613
cannam@221 614 static StaticOutputInfo
cannam@221 615 toStaticOutputInfo(json11::Json j, std::string &err) {
cannam@221 616
cannam@221 617 if (j == json11::Json()) return {};
cannam@221 618
cannam@221 619 if (!j.is_object()) {
cannam@221 620 err = "object expected for static output info";
cannam@221 621 return {};
cannam@221 622 }
cannam@221 623
cannam@221 624 StaticOutputInfo sinfo;
cannam@221 625 auto items = j.object_items();
cannam@221 626 for (auto i: items) {
cannam@221 627 sinfo[i.first] = toStaticOutputDescriptor(i.second, err);
cannam@221 628 if (failed(err)) return {};
cannam@221 629 }
cannam@221 630 return sinfo;
cannam@221 631 }
cannam@221 632
c@97 633 static PluginStaticData
c@75 634 toPluginStaticData(json11::Json j, std::string &err) {
c@75 635
c@75 636 if (!j.has_shape({
c@75 637 { "key", json11::Json::STRING },
c@75 638 { "version", json11::Json::NUMBER },
c@75 639 { "minChannelCount", json11::Json::NUMBER },
c@75 640 { "maxChannelCount", json11::Json::NUMBER },
c@75 641 { "inputDomain", json11::Json::STRING }}, err)) {
c@75 642
c@75 643 err = "malformed plugin static data: " + err;
c@75 644
c@75 645 } else if (!j["basicOutputInfo"].is_array()) {
c@75 646
c@75 647 err = "array expected for basic output info";
c@75 648
c@75 649 } else if (!j["maker"].is_null() &&
c@75 650 !j["maker"].is_string()) {
c@75 651
c@75 652 err = "string expected for maker";
c@75 653
cannam@216 654 } else if (!j["rights"].is_null() &&
cannam@216 655 !j["rights"].is_string()) {
cannam@230 656
cannam@216 657 err = "string expected for rights";
c@75 658
c@75 659 } else if (!j["category"].is_null() &&
c@75 660 !j["category"].is_array()) {
c@75 661
c@75 662 err = "array expected for category";
c@75 663
c@75 664 } else if (!j["parameters"].is_null() &&
c@75 665 !j["parameters"].is_array()) {
c@75 666
c@75 667 err = "array expected for parameters";
c@75 668
c@75 669 } else if (!j["programs"].is_null() &&
c@75 670 !j["programs"].is_array()) {
c@75 671
c@75 672 err = "array expected for programs";
c@75 673
c@75 674 } else if (!j["inputDomain"].is_null() &&
c@75 675 !j["inputDomain"].is_string()) {
c@75 676
c@75 677 err = "string expected for inputDomain";
c@75 678
cannam@221 679 } else if (!j["staticOutputInfo"].is_null() &&
cannam@221 680 !j["staticOutputInfo"].is_object()) {
cannam@221 681
cannam@221 682 err = "object expected for staticOutputInfo";
cannam@221 683
c@75 684 } else {
c@75 685
c@97 686 PluginStaticData psd;
c@75 687
c@75 688 psd.pluginKey = j["key"].string_value();
c@75 689
c@75 690 toBasicDescriptor(j["basic"], psd.basic, err);
c@75 691 if (failed(err)) return {};
c@75 692
c@75 693 psd.maker = j["maker"].string_value();
cannam@216 694 psd.copyright = j["rights"].string_value();
c@75 695 psd.pluginVersion = j["version"].int_value();
c@75 696
c@75 697 for (const auto &c : j["category"].array_items()) {
c@75 698 if (!c.is_string()) {
c@75 699 err = "strings expected in category array";
c@75 700 return {};
c@75 701 }
c@75 702 psd.category.push_back(c.string_value());
c@75 703 }
c@75 704
c@75 705 psd.minChannelCount = j["minChannelCount"].int_value();
c@75 706 psd.maxChannelCount = j["maxChannelCount"].int_value();
c@75 707
c@75 708 for (const auto &p : j["parameters"].array_items()) {
c@75 709 auto pd = toParameterDescriptor(p, err);
c@75 710 if (failed(err)) return {};
c@75 711 psd.parameters.push_back(pd);
c@75 712 }
c@75 713
c@75 714 for (const auto &p : j["programs"].array_items()) {
c@75 715 if (!p.is_string()) {
c@75 716 err = "strings expected in programs array";
c@75 717 return {};
c@75 718 }
c@75 719 psd.programs.push_back(p.string_value());
c@75 720 }
c@75 721
c@75 722 psd.inputDomain = toInputDomain(j["inputDomain"].string_value(), err);
c@75 723 if (failed(err)) return {};
c@75 724
c@75 725 for (const auto &bo : j["basicOutputInfo"].array_items()) {
c@97 726 PluginStaticData::Basic b;
c@75 727 toBasicDescriptor(bo, b, err);
c@75 728 if (failed(err)) return {};
c@75 729 psd.basicOutputInfo.push_back(b);
c@75 730 }
cannam@221 731
cannam@221 732 StaticOutputInfo sinfo =
cannam@221 733 toStaticOutputInfo(j["staticOutputInfo"], err);
cannam@221 734 if (failed(err)) return {};
cannam@221 735 psd.staticOutputInfo = sinfo;
cannam@221 736
c@75 737 return psd;
c@75 738 }
c@75 739
c@75 740 // fallthrough error case
c@75 741 return {};
c@75 742 }
c@75 743
c@75 744 static json11::Json
c@97 745 fromPluginConfiguration(const PluginConfiguration &c) {
c@75 746
c@75 747 json11::Json::object jo;
c@75 748
c@75 749 json11::Json::object paramValues;
c@75 750 for (auto &vp: c.parameterValues) {
c@75 751 paramValues[vp.first] = vp.second;
c@75 752 }
c@75 753 jo["parameterValues"] = paramValues;
c@75 754
c@75 755 if (c.currentProgram != "") {
c@75 756 jo["currentProgram"] = c.currentProgram;
c@75 757 }
c@75 758
c@75 759 jo["channelCount"] = c.channelCount;
cannam@185 760
cannam@185 761 json11::Json::object framing;
cannam@185 762 framing["stepSize"] = c.framing.stepSize;
cannam@185 763 framing["blockSize"] = c.framing.blockSize;
cannam@185 764 jo["framing"] = framing;
c@75 765
c@75 766 return json11::Json(jo);
c@75 767 }
c@75 768
c@97 769 static PluginConfiguration
c@75 770 toPluginConfiguration(json11::Json j, std::string &err) {
c@75 771
c@75 772 if (!j.has_shape({
cannam@185 773 { "channelCount", json11::Json::NUMBER } }, err)) {
c@75 774 err = "malformed plugin configuration: " + err;
c@75 775 return {};
c@75 776 }
c@75 777
cannam@185 778 if (!j["framing"].has_shape({
cannam@185 779 { "stepSize", json11::Json::NUMBER },
cannam@185 780 { "blockSize", json11::Json::NUMBER } }, err)) {
cannam@185 781 err = "malformed framing: " + err;
cannam@185 782 return {};
cannam@185 783 }
cannam@185 784
c@75 785 if (!j["parameterValues"].is_null() &&
c@75 786 !j["parameterValues"].is_object()) {
c@75 787 err = "object expected for parameter values";
c@75 788 return {};
c@75 789 }
c@75 790
c@75 791 for (auto &pv : j["parameterValues"].object_items()) {
c@75 792 if (!pv.second.is_number()) {
c@75 793 err = "number expected for parameter value";
c@75 794 return {};
c@75 795 }
c@75 796 }
c@75 797
c@75 798 if (!j["currentProgram"].is_null() &&
c@75 799 !j["currentProgram"].is_string()) {
c@75 800 err = "string expected for program name";
c@75 801 return {};
c@75 802 }
c@75 803
c@97 804 PluginConfiguration config;
c@75 805
c@139 806 config.channelCount = int(round(j["channelCount"].number_value()));
cannam@185 807 config.framing.stepSize = int(round(j["framing"]["stepSize"].number_value()));
cannam@185 808 config.framing.blockSize = int(round(j["framing"]["blockSize"].number_value()));
c@75 809
c@75 810 for (auto &pv : j["parameterValues"].object_items()) {
c@139 811 config.parameterValues[pv.first] = float(pv.second.number_value());
c@75 812 }
c@75 813
c@75 814 if (j["currentProgram"].is_string()) {
c@75 815 config.currentProgram = j["currentProgram"].string_value();
c@75 816 }
c@75 817
c@75 818 return config;
c@75 819 }
c@75 820
c@75 821 static json11::Json
c@75 822 fromAdapterFlags(int flags) {
c@75 823
c@75 824 json11::Json::array arr;
c@75 825
c@75 826 if (flags & Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN) {
c@75 827 arr.push_back("AdaptInputDomain");
c@75 828 }
c@75 829 if (flags & Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT) {
c@75 830 arr.push_back("AdaptChannelCount");
c@75 831 }
c@75 832 if (flags & Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE) {
c@75 833 arr.push_back("AdaptBufferSize");
c@75 834 }
c@75 835
c@75 836 return json11::Json(arr);
c@75 837 }
c@75 838
c@75 839 static Vamp::HostExt::PluginLoader::AdapterFlags
c@75 840 toAdapterFlags(json11::Json j, std::string &err) {
c@75 841
c@75 842 int flags = 0x0;
c@75 843
c@75 844 if (!j.is_array()) {
c@75 845
c@75 846 err = "array expected for adapter flags";
c@75 847
c@75 848 } else {
c@75 849
c@75 850 for (auto &jj: j.array_items()) {
c@75 851 if (!jj.is_string()) {
c@75 852 err = "string expected for adapter flag";
c@75 853 break;
c@75 854 }
c@75 855 std::string text = jj.string_value();
c@75 856 if (text == "AdaptInputDomain") {
c@75 857 flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
c@75 858 } else if (text == "AdaptChannelCount") {
c@75 859 flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
c@75 860 } else if (text == "AdaptBufferSize") {
c@75 861 flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
c@75 862 } else if (text == "AdaptAllSafe") {
c@75 863 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE;
c@75 864 } else if (text == "AdaptAll") {
c@75 865 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL;
c@75 866 } else {
c@75 867 err = "invalid adapter flag string: " + text;
c@75 868 break;
c@75 869 }
c@75 870 }
c@75 871 }
c@75 872
c@75 873 return Vamp::HostExt::PluginLoader::AdapterFlags(flags);
c@75 874 }
c@75 875
c@75 876 static json11::Json
cannam@289 877 fromProgramParameters(const PluginProgramParameters &programParameters) {
cannam@289 878
cannam@289 879 json11::Json::object jo;
cannam@289 880 for (const auto &pp: programParameters.programParameters) {
cannam@289 881 auto program = pp.first;
cannam@289 882 json11::Json::object po;
cannam@289 883 for (const auto &pv: pp.second) {
cannam@289 884 po[pv.first] = pv.second;
cannam@289 885 }
cannam@289 886 jo[program] = po;
cannam@289 887 }
cannam@289 888
cannam@289 889 return json11::Json(jo);
cannam@289 890 }
cannam@289 891
cannam@289 892 static PluginProgramParameters
cannam@289 893 toProgramParameters(json11::Json j, std::string &err) {
cannam@289 894
cannam@289 895 if (!j.is_object()) {
cannam@289 896 err = "object expected for program parameters";
cannam@289 897 return {};
cannam@289 898 }
cannam@289 899
cannam@289 900 PluginProgramParameters params;
cannam@289 901
cannam@289 902 auto pp = j.object_items();
cannam@289 903 for (auto program: pp) {
cannam@289 904 std::string name = program.first;
cannam@289 905 if (!program.second.is_object()) {
cannam@289 906 err = std::string("object expected for program parameter map ") +
cannam@289 907 "(in program \"" + name + "\")";
cannam@289 908 return {};
cannam@289 909 }
cannam@289 910 for (auto p: program.second.object_items()) {
cannam@289 911 if (!p.second.is_number()) {
cannam@289 912 err = std::string("number expected for program parameter value ") +
cannam@289 913 "(in program \"" + name + "\")";
cannam@289 914 return {};
cannam@289 915 }
cannam@289 916 params.programParameters[name][p.first] = p.second.number_value();
cannam@289 917 }
cannam@289 918 }
cannam@289 919
cannam@289 920 return params;
cannam@289 921 }
cannam@289 922
cannam@289 923 static json11::Json
c@127 924 fromListRequest(const ListRequest &req) {
c@127 925 json11::Json::object jo;
c@127 926 json11::Json::array arr;
c@127 927 for (const auto &f: req.from) {
c@127 928 arr.push_back(f);
c@127 929 }
c@127 930 jo["from"] = arr;
c@127 931 return json11::Json(jo);
c@127 932 }
c@127 933
c@127 934 static ListRequest
c@127 935 toListRequest(json11::Json j, std::string &err) {
c@127 936
c@127 937 ListRequest req;
c@127 938 if (!j["from"].is_null() &&
c@127 939 !j["from"].is_array()) {
c@127 940 err = "array expected for from field";
c@127 941 return {};
c@127 942 }
c@127 943 for (const auto &a: j["from"].array_items()) {
c@127 944 if (!a.is_string()) {
c@127 945 err = "string expected for element in from array";
c@127 946 return {};
c@127 947 }
c@127 948 req.from.push_back(a.string_value());
c@127 949 }
c@127 950 return req;
c@127 951 }
c@127 952
c@127 953 static json11::Json
c@127 954 fromListResponse(const ListResponse &resp) {
c@127 955
c@127 956 json11::Json::array arr;
c@127 957 for (const auto &a: resp.available) {
c@127 958 arr.push_back(fromPluginStaticData(a));
c@127 959 }
c@127 960 json11::Json::object jo;
c@127 961 jo["available"] = arr;
c@127 962
c@127 963 return json11::Json(jo);
c@127 964 }
c@127 965
c@127 966 static ListResponse
c@127 967 toListResponse(json11::Json j, std::string &err) {
c@127 968
c@127 969 ListResponse resp;
dev@177 970 for (const auto &a: j["available"].array_items()) {
c@127 971 resp.available.push_back(toPluginStaticData(a, err));
c@127 972 if (failed(err)) return {};
c@127 973 }
c@127 974 return resp;
c@127 975 }
c@127 976
c@127 977 static json11::Json
c@97 978 fromLoadRequest(const LoadRequest &req) {
c@75 979
c@75 980 json11::Json::object jo;
c@75 981 jo["key"] = req.pluginKey;
c@75 982 jo["inputSampleRate"] = req.inputSampleRate;
c@75 983 jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags);
c@75 984 return json11::Json(jo);
c@75 985 }
c@75 986
c@97 987 static LoadRequest
c@75 988 toLoadRequest(json11::Json j, std::string &err) {
c@75 989
c@75 990 if (!j.has_shape({
c@75 991 { "key", json11::Json::STRING },
c@75 992 { "inputSampleRate", json11::Json::NUMBER } }, err)) {
c@75 993 err = "malformed load request: " + err;
c@75 994 return {};
c@75 995 }
c@75 996
c@97 997 LoadRequest req;
c@75 998 req.pluginKey = j["key"].string_value();
c@139 999 req.inputSampleRate = float(j["inputSampleRate"].number_value());
c@75 1000 if (!j["adapterFlags"].is_null()) {
c@75 1001 req.adapterFlags = toAdapterFlags(j["adapterFlags"], err);
c@75 1002 if (failed(err)) return {};
c@75 1003 }
c@75 1004 return req;
c@75 1005 }
c@75 1006
c@75 1007 static json11::Json
c@97 1008 fromLoadResponse(const LoadResponse &resp,
c@75 1009 const PluginHandleMapper &pmapper) {
c@75 1010
c@75 1011 json11::Json::object jo;
c@75 1012 jo["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75 1013 jo["staticData"] = fromPluginStaticData(resp.staticData);
c@75 1014 jo["defaultConfiguration"] =
c@75 1015 fromPluginConfiguration(resp.defaultConfiguration);
cannam@289 1016 jo["programParameters"] =
cannam@289 1017 fromProgramParameters(resp.programParameters);
c@75 1018 return json11::Json(jo);
c@75 1019 }
c@75 1020
c@97 1021 static LoadResponse
c@75 1022 toLoadResponse(json11::Json j,
c@75 1023 const PluginHandleMapper &pmapper, std::string &err) {
c@75 1024
c@75 1025 if (!j.has_shape({
c@75 1026 { "handle", json11::Json::NUMBER },
c@75 1027 { "staticData", json11::Json::OBJECT },
c@75 1028 { "defaultConfiguration", json11::Json::OBJECT } }, err)) {
c@75 1029 err = "malformed load response: " + err;
c@75 1030 return {};
c@75 1031 }
c@75 1032
c@97 1033 LoadResponse resp;
cannam@275 1034 auto h = j["handle"].int_value();
cannam@275 1035 resp.plugin = pmapper.handleToPlugin(h);
c@75 1036 resp.staticData = toPluginStaticData(j["staticData"], err);
c@75 1037 if (failed(err)) return {};
cannam@289 1038 resp.defaultConfiguration =
cannam@289 1039 toPluginConfiguration(j["defaultConfiguration"], err);
c@75 1040 if (failed(err)) return {};
cannam@289 1041 if (j.object_items().find("programParameters") !=
cannam@289 1042 j.object_items().end()) {
cannam@289 1043 resp.programParameters =
cannam@289 1044 toProgramParameters(j["programParameters"], err);
cannam@289 1045 if (failed(err)) return {};
cannam@289 1046 }
c@75 1047 return resp;
c@75 1048 }
c@75 1049
c@75 1050 static json11::Json
c@97 1051 fromConfigurationRequest(const ConfigurationRequest &cr,
c@75 1052 const PluginHandleMapper &pmapper) {
c@75 1053
c@75 1054 json11::Json::object jo;
c@75 1055
c@86 1056 jo["handle"] = double(pmapper.pluginToHandle(cr.plugin));
c@75 1057 jo["configuration"] = fromPluginConfiguration(cr.configuration);
c@75 1058
c@75 1059 return json11::Json(jo);
c@75 1060 }
c@75 1061
c@97 1062 static ConfigurationRequest
c@75 1063 toConfigurationRequest(json11::Json j,
c@75 1064 const PluginHandleMapper &pmapper, std::string &err) {
c@75 1065
c@75 1066 if (!j.has_shape({
c@75 1067 { "handle", json11::Json::NUMBER },
c@75 1068 { "configuration", json11::Json::OBJECT } }, err)) {
c@75 1069 err = "malformed configuration request: " + err;
c@75 1070 return {};
c@75 1071 }
c@75 1072
c@97 1073 ConfigurationRequest cr;
cannam@275 1074 auto h = j["handle"].int_value();
cannam@275 1075 cr.plugin = pmapper.handleToPlugin(h);
c@75 1076 cr.configuration = toPluginConfiguration(j["configuration"], err);
c@75 1077 if (failed(err)) return {};
c@75 1078 return cr;
c@75 1079 }
c@75 1080
c@75 1081 static json11::Json
c@97 1082 fromConfigurationResponse(const ConfigurationResponse &cr,
c@75 1083 const PluginHandleMapper &pmapper) {
c@75 1084
c@75 1085 json11::Json::object jo;
c@75 1086
c@86 1087 jo["handle"] = double(pmapper.pluginToHandle(cr.plugin));
c@75 1088
c@75 1089 json11::Json::array outs;
c@75 1090 for (auto &d: cr.outputs) {
cannam@220 1091 auto id = d.identifier;
cannam@220 1092 StaticOutputDescriptor sd;
cannam@220 1093 if (cr.staticOutputInfo.find(id) != cr.staticOutputInfo.end()) {
cannam@220 1094 sd = cr.staticOutputInfo.at(id);
cannam@220 1095 }
cannam@220 1096 outs.push_back(fromOutputDescriptor(d, sd));
c@75 1097 }
c@75 1098 jo["outputList"] = outs;
cannam@185 1099
cannam@185 1100 json11::Json::object framing;
cannam@185 1101 framing["stepSize"] = cr.framing.stepSize;
cannam@185 1102 framing["blockSize"] = cr.framing.blockSize;
cannam@185 1103 jo["framing"] = framing;
c@75 1104
c@75 1105 return json11::Json(jo);
c@75 1106 }
c@75 1107
c@97 1108 static ConfigurationResponse
c@75 1109 toConfigurationResponse(json11::Json j,
c@75 1110 const PluginHandleMapper &pmapper, std::string &err) {
c@75 1111
c@97 1112 ConfigurationResponse cr;
c@75 1113
cannam@185 1114 if (!j["framing"].has_shape({
cannam@185 1115 { "stepSize", json11::Json::NUMBER },
cannam@185 1116 { "blockSize", json11::Json::NUMBER } }, err)) {
cannam@185 1117 err = "malformed framing: " + err;
cannam@185 1118 return {};
cannam@185 1119 }
c@75 1120
c@75 1121 if (!j["outputList"].is_array()) {
c@75 1122 err = "array expected for output list";
c@75 1123 return {};
c@75 1124 }
cannam@185 1125
cannam@275 1126 auto h = j["handle"].int_value();
cannam@275 1127 cr.plugin = pmapper.handleToPlugin(h);
c@75 1128
c@75 1129 for (const auto &o: j["outputList"].array_items()) {
cannam@220 1130 auto odpair = toOutputDescriptor(o, err);
c@75 1131 if (failed(err)) return {};
cannam@220 1132 cr.outputs.push_back(odpair.first);
cannam@220 1133 cr.staticOutputInfo[odpair.first.identifier] = odpair.second;
c@75 1134 }
c@75 1135
cannam@185 1136 cr.framing.stepSize = int(round(j["framing"]["stepSize"].number_value()));
cannam@185 1137 cr.framing.blockSize = int(round(j["framing"]["blockSize"].number_value()));
cannam@185 1138
c@75 1139 return cr;
c@75 1140 }
c@75 1141
c@75 1142 static json11::Json
c@97 1143 fromProcessRequest(const ProcessRequest &r,
c@75 1144 const PluginHandleMapper &pmapper,
c@75 1145 BufferSerialisation serialisation) {
c@75 1146
c@75 1147 json11::Json::object jo;
c@86 1148 jo["handle"] = double(pmapper.pluginToHandle(r.plugin));
c@75 1149
c@75 1150 json11::Json::object io;
c@75 1151 io["timestamp"] = fromRealTime(r.timestamp);
c@75 1152
c@75 1153 json11::Json::array chans;
c@75 1154 for (size_t i = 0; i < r.inputBuffers.size(); ++i) {
c@75 1155 if (serialisation == BufferSerialisation::Array) {
c@75 1156 chans.push_back(json11::Json::array(r.inputBuffers[i].begin(),
c@75 1157 r.inputBuffers[i].end()));
c@75 1158 } else {
c@75 1159 chans.push_back(fromFloatBuffer(r.inputBuffers[i].data(),
c@75 1160 r.inputBuffers[i].size()));
c@75 1161 }
c@75 1162 }
c@75 1163 io["inputBuffers"] = chans;
c@75 1164
c@75 1165 jo["processInput"] = io;
c@75 1166 return json11::Json(jo);
c@75 1167 }
c@75 1168
c@97 1169 static ProcessRequest
c@75 1170 toProcessRequest(json11::Json j,
c@75 1171 const PluginHandleMapper &pmapper,
c@75 1172 BufferSerialisation &serialisation, std::string &err) {
c@75 1173
c@75 1174 if (!j.has_shape({
c@75 1175 { "handle", json11::Json::NUMBER },
c@75 1176 { "processInput", json11::Json::OBJECT } }, err)) {
c@75 1177 err = "malformed process request: " + err;
c@75 1178 return {};
c@75 1179 }
c@75 1180
c@75 1181 auto input = j["processInput"];
c@75 1182
c@75 1183 if (!input.has_shape({
c@75 1184 { "timestamp", json11::Json::OBJECT },
c@75 1185 { "inputBuffers", json11::Json::ARRAY } }, err)) {
c@75 1186 err = "malformed process request: " + err;
c@75 1187 return {};
c@75 1188 }
c@75 1189
c@97 1190 ProcessRequest r;
cannam@275 1191 auto h = j["handle"].int_value();
cannam@275 1192 r.plugin = pmapper.handleToPlugin(h);
c@75 1193
c@75 1194 r.timestamp = toRealTime(input["timestamp"], err);
c@75 1195 if (failed(err)) return {};
c@75 1196
c@75 1197 for (const auto &a: input["inputBuffers"].array_items()) {
c@75 1198
c@75 1199 if (a.is_string()) {
c@75 1200 std::vector<float> buf = toFloatBuffer(a.string_value(),
c@75 1201 err);
c@75 1202 if (failed(err)) return {};
c@75 1203 r.inputBuffers.push_back(buf);
c@75 1204 serialisation = BufferSerialisation::Base64;
c@75 1205
c@75 1206 } else if (a.is_array()) {
c@75 1207 std::vector<float> buf;
c@75 1208 for (auto v : a.array_items()) {
c@139 1209 buf.push_back(float(v.number_value()));
c@75 1210 }
c@75 1211 r.inputBuffers.push_back(buf);
c@75 1212 serialisation = BufferSerialisation::Array;
c@75 1213
c@75 1214 } else {
c@75 1215 err = "expected arrays or strings in inputBuffers array";
c@75 1216 return {};
c@75 1217 }
c@75 1218 }
c@75 1219
c@75 1220 return r;
c@75 1221 }
c@75 1222
c@75 1223 private: // go private briefly for a couple of helper functions
c@75 1224
c@75 1225 static void
c@128 1226 checkRpcRequestType(json11::Json j, std::string expected, std::string &err) {
c@75 1227 if (!j["method"].is_string()) {
c@75 1228 err = "string expected for method";
c@75 1229 return;
c@75 1230 }
c@75 1231 if (j["method"].string_value() != expected) {
c@75 1232 err = "expected value \"" + expected + "\" for type";
c@75 1233 return;
c@75 1234 }
c@128 1235 if (!j["params"].is_null() &&
c@128 1236 !j["params"].is_object()) {
c@128 1237 err = "object expected for params";
c@128 1238 return;
c@128 1239 }
c@128 1240 if (!j["id"].is_null() &&
c@128 1241 !j["id"].is_number() &&
c@128 1242 !j["id"].is_string()) {
c@128 1243 err = "number or string expected for id";
c@128 1244 return;
c@128 1245 }
c@128 1246 if (!j["jsonrpc"].is_null() &&
c@128 1247 !j["jsonrpc"].is_string()) {
c@128 1248 err = "string expected for jsonrpc";
c@128 1249 return;
c@128 1250 }
c@128 1251 for (const auto &kv: j.object_items()) {
c@128 1252 if (kv.first != "method" &&
c@128 1253 kv.first != "params" &&
c@128 1254 kv.first != "id" &&
c@128 1255 kv.first != "jsonrpc") {
c@128 1256 err = "unexpected field \"" + kv.first + "\" in rpc request object";
c@128 1257 return;
c@128 1258 }
c@128 1259 }
c@75 1260 }
c@75 1261
c@75 1262 static bool
c@75 1263 successful(json11::Json j, std::string &err) {
dev@183 1264 const bool hasResult = j["result"].is_object();
dev@183 1265 const bool hasError = j["error"].is_object();
dev@183 1266 if (hasResult && hasError) {
dev@183 1267 err = "valid response may contain only one of result and error objects";
dev@183 1268 return false;
dev@183 1269 } else if (hasError) {
dev@183 1270 return false;
dev@183 1271 } else if (!hasResult) {
dev@183 1272 err = "either a result or an error object is required for a valid response";
dev@180 1273 return false;
dev@180 1274 } else {
dev@183 1275 return true;
c@75 1276 }
c@75 1277 }
c@75 1278
c@75 1279 static void
c@75 1280 markRPC(json11::Json::object &jo) {
c@75 1281 jo["jsonrpc"] = "2.0";
c@75 1282 }
c@75 1283
c@75 1284 static void
c@75 1285 addId(json11::Json::object &jo, const json11::Json &id) {
c@75 1286 if (!id.is_null()) {
c@75 1287 jo["id"] = id;
c@75 1288 }
c@75 1289 }
c@75 1290
c@75 1291 public:
c@75 1292
c@75 1293 static json11::Json
c@127 1294 fromRpcRequest_List(const ListRequest &req,
c@127 1295 const json11::Json &id) {
c@75 1296
c@75 1297 json11::Json::object jo;
c@75 1298 markRPC(jo);
c@75 1299
c@75 1300 jo["method"] = "list";
c@127 1301 jo["params"] = fromListRequest(req);
c@75 1302 addId(jo, id);
c@75 1303 return json11::Json(jo);
c@75 1304 }
c@75 1305
c@75 1306 static json11::Json
c@97 1307 fromRpcResponse_List(const ListResponse &resp,
c@75 1308 const json11::Json &id) {
c@75 1309
c@75 1310 json11::Json::object jo;
c@75 1311 markRPC(jo);
c@75 1312
c@75 1313 jo["method"] = "list";
c@127 1314 jo["result"] = fromListResponse(resp);
c@75 1315 addId(jo, id);
c@75 1316 return json11::Json(jo);
c@75 1317 }
c@75 1318
c@75 1319 static json11::Json
c@97 1320 fromRpcRequest_Load(const LoadRequest &req,
c@75 1321 const json11::Json &id) {
c@75 1322
c@75 1323 json11::Json::object jo;
c@75 1324 markRPC(jo);
c@75 1325
c@75 1326 jo["method"] = "load";
c@75 1327 jo["params"] = fromLoadRequest(req);
c@75 1328 addId(jo, id);
c@75 1329 return json11::Json(jo);
c@75 1330 }
c@75 1331
c@75 1332 static json11::Json
c@97 1333 fromRpcResponse_Load(const LoadResponse &resp,
c@75 1334 const PluginHandleMapper &pmapper,
c@75 1335 const json11::Json &id) {
c@75 1336
c@75 1337 if (resp.plugin) {
c@75 1338
c@75 1339 json11::Json::object jo;
c@75 1340 markRPC(jo);
c@75 1341
c@75 1342 jo["method"] = "load";
c@75 1343 jo["result"] = fromLoadResponse(resp, pmapper);
c@75 1344 addId(jo, id);
c@75 1345 return json11::Json(jo);
c@75 1346
c@75 1347 } else {
c@75 1348 return fromError("Failed to load plugin", RRType::Load, id);
c@75 1349 }
c@75 1350 }
c@75 1351
c@75 1352 static json11::Json
c@97 1353 fromRpcRequest_Configure(const ConfigurationRequest &req,
c@75 1354 const PluginHandleMapper &pmapper,
c@75 1355 const json11::Json &id) {
c@75 1356
c@75 1357 json11::Json::object jo;
c@75 1358 markRPC(jo);
c@75 1359
c@75 1360 jo["method"] = "configure";
c@75 1361 jo["params"] = fromConfigurationRequest(req, pmapper);
c@75 1362 addId(jo, id);
c@75 1363 return json11::Json(jo);
c@75 1364 }
c@75 1365
c@75 1366 static json11::Json
c@97 1367 fromRpcResponse_Configure(const ConfigurationResponse &resp,
c@75 1368 const PluginHandleMapper &pmapper,
c@75 1369 const json11::Json &id) {
c@75 1370
c@75 1371 if (!resp.outputs.empty()) {
c@75 1372
c@75 1373 json11::Json::object jo;
c@75 1374 markRPC(jo);
c@75 1375
c@75 1376 jo["method"] = "configure";
c@75 1377 jo["result"] = fromConfigurationResponse(resp, pmapper);
c@75 1378 addId(jo, id);
c@75 1379 return json11::Json(jo);
c@75 1380
c@75 1381 } else {
c@75 1382 return fromError("Failed to configure plugin", RRType::Configure, id);
c@75 1383 }
c@75 1384 }
c@75 1385
c@75 1386 static json11::Json
c@97 1387 fromRpcRequest_Process(const ProcessRequest &req,
c@75 1388 const PluginHandleMapper &pmapper,
c@75 1389 BufferSerialisation serialisation,
c@75 1390 const json11::Json &id) {
c@75 1391
c@75 1392 json11::Json::object jo;
c@75 1393 markRPC(jo);
c@75 1394
c@75 1395 jo["method"] = "process";
c@75 1396 jo["params"] = fromProcessRequest(req, pmapper, serialisation);
c@75 1397 addId(jo, id);
c@75 1398 return json11::Json(jo);
c@75 1399 }
c@75 1400
c@75 1401 static json11::Json
c@97 1402 fromRpcResponse_Process(const ProcessResponse &resp,
c@75 1403 const PluginHandleMapper &pmapper,
c@75 1404 BufferSerialisation serialisation,
c@75 1405 const json11::Json &id) {
c@75 1406
c@75 1407 json11::Json::object jo;
c@75 1408 markRPC(jo);
c@75 1409
c@75 1410 json11::Json::object po;
c@86 1411 po["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75 1412 po["features"] = fromFeatureSet(resp.features,
c@75 1413 *pmapper.pluginToOutputIdMapper(resp.plugin),
c@75 1414 serialisation);
c@75 1415 jo["method"] = "process";
c@75 1416 jo["result"] = po;
c@75 1417 addId(jo, id);
c@75 1418 return json11::Json(jo);
c@75 1419 }
c@75 1420
c@75 1421 static json11::Json
c@97 1422 fromRpcRequest_Finish(const FinishRequest &req,
c@75 1423 const PluginHandleMapper &pmapper,
c@75 1424 const json11::Json &id) {
c@75 1425
c@75 1426 json11::Json::object jo;
c@75 1427 markRPC(jo);
c@75 1428
c@75 1429 json11::Json::object fo;
c@86 1430 fo["handle"] = double(pmapper.pluginToHandle(req.plugin));
c@75 1431
c@75 1432 jo["method"] = "finish";
c@75 1433 jo["params"] = fo;
c@75 1434 addId(jo, id);
c@75 1435 return json11::Json(jo);
c@75 1436 }
c@75 1437
c@75 1438 static json11::Json
c@97 1439 fromRpcResponse_Finish(const FinishResponse &resp,
c@75 1440 const PluginHandleMapper &pmapper,
c@75 1441 BufferSerialisation serialisation,
c@75 1442 const json11::Json &id) {
c@75 1443
c@75 1444 json11::Json::object jo;
c@75 1445 markRPC(jo);
c@75 1446
c@75 1447 json11::Json::object po;
c@86 1448 po["handle"] = double(pmapper.pluginToHandle(resp.plugin));
c@75 1449 po["features"] = fromFeatureSet(resp.features,
c@75 1450 *pmapper.pluginToOutputIdMapper(resp.plugin),
c@75 1451 serialisation);
c@75 1452 jo["method"] = "finish";
c@75 1453 jo["result"] = po;
c@75 1454 addId(jo, id);
c@75 1455 return json11::Json(jo);
c@75 1456 }
c@75 1457
c@75 1458 static json11::Json
c@75 1459 fromError(std::string errorText,
c@75 1460 RRType responseType,
dev@176 1461 const json11::Json &id,
dev@176 1462 bool writeVerbatimError = false) {
c@75 1463
c@75 1464 json11::Json::object jo;
c@75 1465 markRPC(jo);
c@75 1466
c@75 1467 std::string type;
c@75 1468
c@75 1469 if (responseType == RRType::List) type = "list";
c@75 1470 else if (responseType == RRType::Load) type = "load";
c@75 1471 else if (responseType == RRType::Configure) type = "configure";
c@75 1472 else if (responseType == RRType::Process) type = "process";
c@75 1473 else if (responseType == RRType::Finish) type = "finish";
c@75 1474 else type = "invalid";
c@75 1475
c@75 1476 json11::Json::object eo;
c@75 1477 eo["code"] = 0;
cannam@158 1478
dev@176 1479 if (responseType == RRType::NotValid || writeVerbatimError) {
cannam@158 1480 eo["message"] = errorText;
cannam@158 1481 } else {
cannam@158 1482 eo["message"] =
cannam@158 1483 std::string("error in ") + type + " request: " + errorText;
cannam@158 1484 }
c@75 1485
c@75 1486 jo["method"] = type;
c@75 1487 jo["error"] = eo;
c@75 1488 addId(jo, id);
c@75 1489 return json11::Json(jo);
c@75 1490 }
c@75 1491
c@75 1492 static RRType
c@75 1493 getRequestResponseType(json11::Json j, std::string &err) {
c@75 1494
c@75 1495 if (!j["method"].is_string()) {
c@75 1496 err = "string expected for method";
c@75 1497 return RRType::NotValid;
c@75 1498 }
c@75 1499
c@75 1500 std::string type = j["method"].string_value();
c@75 1501
c@75 1502 if (type == "list") return RRType::List;
c@75 1503 else if (type == "load") return RRType::Load;
c@75 1504 else if (type == "configure") return RRType::Configure;
c@75 1505 else if (type == "process") return RRType::Process;
c@75 1506 else if (type == "finish") return RRType::Finish;
c@75 1507 else if (type == "invalid") return RRType::NotValid;
c@75 1508 else {
c@75 1509 err = "unknown or unexpected request/response type \"" + type + "\"";
c@75 1510 return RRType::NotValid;
c@75 1511 }
c@75 1512 }
c@75 1513
c@127 1514 static ListRequest
c@75 1515 toRpcRequest_List(json11::Json j, std::string &err) {
c@127 1516
c@128 1517 checkRpcRequestType(j, "list", err);
c@127 1518 if (failed(err)) return {};
c@127 1519 return toListRequest(j["params"], err);
c@75 1520 }
c@75 1521
c@97 1522 static ListResponse
c@75 1523 toRpcResponse_List(json11::Json j, std::string &err) {
c@75 1524
c@97 1525 ListResponse resp;
c@75 1526 if (successful(j, err) && !failed(err)) {
c@127 1527 resp = toListResponse(j["result"], err);
c@75 1528 }
c@75 1529 return resp;
c@75 1530 }
c@75 1531
c@97 1532 static LoadRequest
c@75 1533 toRpcRequest_Load(json11::Json j, std::string &err) {
c@75 1534
c@128 1535 checkRpcRequestType(j, "load", err);
c@75 1536 if (failed(err)) return {};
c@75 1537 return toLoadRequest(j["params"], err);
c@75 1538 }
c@75 1539
c@97 1540 static LoadResponse
c@75 1541 toRpcResponse_Load(json11::Json j,
c@75 1542 const PluginHandleMapper &pmapper,
c@75 1543 std::string &err) {
c@75 1544
c@97 1545 LoadResponse resp;
c@75 1546 if (successful(j, err) && !failed(err)) {
c@75 1547 resp = toLoadResponse(j["result"], pmapper, err);
c@75 1548 }
c@75 1549 return resp;
c@75 1550 }
c@75 1551
c@97 1552 static ConfigurationRequest
c@75 1553 toRpcRequest_Configure(json11::Json j,
c@75 1554 const PluginHandleMapper &pmapper,
c@75 1555 std::string &err) {
c@75 1556
c@128 1557 checkRpcRequestType(j, "configure", err);
c@75 1558 if (failed(err)) return {};
c@75 1559 return toConfigurationRequest(j["params"], pmapper, err);
c@75 1560 }
c@75 1561
c@97 1562 static ConfigurationResponse
c@75 1563 toRpcResponse_Configure(json11::Json j,
c@75 1564 const PluginHandleMapper &pmapper,
c@75 1565 std::string &err) {
c@75 1566
c@97 1567 ConfigurationResponse resp;
c@75 1568 if (successful(j, err) && !failed(err)) {
c@75 1569 resp = toConfigurationResponse(j["result"], pmapper, err);
c@75 1570 }
c@75 1571 return resp;
c@75 1572 }
c@75 1573
c@97 1574 static ProcessRequest
c@75 1575 toRpcRequest_Process(json11::Json j, const PluginHandleMapper &pmapper,
c@75 1576 BufferSerialisation &serialisation, std::string &err) {
c@75 1577
c@128 1578 checkRpcRequestType(j, "process", err);
c@75 1579 if (failed(err)) return {};
c@75 1580 return toProcessRequest(j["params"], pmapper, serialisation, err);
c@75 1581 }
c@75 1582
c@97 1583 static ProcessResponse
c@75 1584 toRpcResponse_Process(json11::Json j,
c@75 1585 const PluginHandleMapper &pmapper,
c@75 1586 BufferSerialisation &serialisation, std::string &err) {
c@75 1587
c@97 1588 ProcessResponse resp;
c@75 1589 if (successful(j, err) && !failed(err)) {
c@75 1590 auto jc = j["result"];
c@75 1591 auto h = jc["handle"].int_value();
c@75 1592 resp.plugin = pmapper.handleToPlugin(h);
c@75 1593 resp.features = toFeatureSet(jc["features"],
c@75 1594 *pmapper.handleToOutputIdMapper(h),
c@75 1595 serialisation, err);
c@75 1596 }
c@75 1597 return resp;
c@75 1598 }
c@75 1599
c@97 1600 static FinishRequest
c@75 1601 toRpcRequest_Finish(json11::Json j, const PluginHandleMapper &pmapper,
c@75 1602 std::string &err) {
c@75 1603
c@128 1604 checkRpcRequestType(j, "finish", err);
c@75 1605 if (failed(err)) return {};
c@97 1606 FinishRequest req;
cannam@275 1607 auto h = j["params"]["handle"].int_value();
cannam@275 1608 req.plugin = pmapper.handleToPlugin(h);
c@75 1609 return req;
c@75 1610 }
c@75 1611
c@97 1612 static FinishResponse
c@75 1613 toRpcResponse_Finish(json11::Json j,
c@97 1614 const PluginHandleMapper &pmapper,
c@97 1615 BufferSerialisation &serialisation, std::string &err) {
c@75 1616
c@97 1617 FinishResponse resp;
c@75 1618 if (successful(j, err) && !failed(err)) {
c@75 1619 auto jc = j["result"];
c@75 1620 auto h = jc["handle"].int_value();
c@75 1621 resp.plugin = pmapper.handleToPlugin(h);
c@75 1622 resp.features = toFeatureSet(jc["features"],
c@75 1623 *pmapper.handleToOutputIdMapper(h),
c@75 1624 serialisation, err);
c@75 1625 }
c@75 1626 return resp;
c@75 1627 }
c@75 1628 };
c@75 1629
c@75 1630 }
c@75 1631
c@75 1632 #endif