comparison json/VampJson.h @ 65:2d866edd79d5

Merge from noexcept branch
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 23 Sep 2016 14:23:10 +0100
parents 85ec33975434 8a4bcb3dc3a6
children 6f160dee1192
comparison
equal deleted inserted replaced
64:85ec33975434 65:2d866edd79d5
36 #define VAMP_JSON_H 36 #define VAMP_JSON_H
37 37
38 #include <vector> 38 #include <vector>
39 #include <string> 39 #include <string>
40 #include <sstream> 40 #include <sstream>
41 #include <stdexcept>
42 41
43 #include <json11/json11.hpp> 42 #include <json11/json11.hpp>
44 #include <base-n/include/basen.hpp> 43 #include <base-n/include/basen.hpp>
45 44
46 #include <vamp-hostsdk/Plugin.h> 45 #include <vamp-hostsdk/Plugin.h>
54 53
55 /** 54 /**
56 * Convert the structures laid out in the Vamp SDK classes into JSON 55 * Convert the structures laid out in the Vamp SDK classes into JSON
57 * (and back again) following the schema in the vamp-json-schema 56 * (and back again) following the schema in the vamp-json-schema
58 * project repo. 57 * project repo.
58 *
59 * Functions with names starting "from" convert from a Vamp SDK object
60 * to JSON output. Most of them return a json11::Json object, with a
61 * few exceptions for low-level utilities that return a string. These
62 * functions succeed all of the time.
63 *
64 * Functions with names starting "to" convert to a Vamp SDK object
65 * from JSON input. These functions all accept a json11::Json object
66 * as first argument, with a few exceptions for low-level utilities
67 * that accept a string. These functions all accept a string reference
68 * as a final argument and return an error string through it if the
69 * conversion fails. If conversion fails the return value is
70 * undefined, and any returned object may be incomplete or
71 * invalid. Callers should check for an empty error string (indicating
72 * success) before using the returned value.
59 */ 73 */
74
75 //!!! todo: convert pmapper to err style
76
60 class VampJson 77 class VampJson
61 { 78 {
62 public: 79 public:
63 /** Serialisation format for arrays of floats (process input and 80 /** Serialisation format for arrays of floats (process input and
64 * feature values). Structures that can be serialised in more 81 * feature values). Structures that can be serialised in more
86 * a consumer that expects padding. 103 * a consumer that expects padding.
87 */ 104 */
88 Base64 105 Base64
89 }; 106 };
90 107
91 class Failure : virtual public std::runtime_error { 108 static bool failed(const std::string &err) {
92 public: 109 return err != "";
93 Failure(std::string s) : runtime_error(s) { } 110 }
94 };
95 111
96 template <typename T> 112 template <typename T>
97 static json11::Json 113 static json11::Json
98 fromBasicDescriptor(const T &t) { 114 fromBasicDescriptor(const T &t) {
99 return json11::Json::object { 115 return json11::Json::object {
103 }; 119 };
104 } 120 }
105 121
106 template <typename T> 122 template <typename T>
107 static void 123 static void
108 toBasicDescriptor(json11::Json j, T &t) { 124 toBasicDescriptor(json11::Json j, T &t, std::string &err) {
109 if (!j.is_object()) { 125 if (!j.is_object()) {
110 throw Failure("object expected for basic descriptor content"); 126 err = "object expected for basic descriptor content";
127 return;
111 } 128 }
112 if (!j["identifier"].is_string()) { 129 if (!j["identifier"].is_string()) {
113 throw Failure("string expected for identifier"); 130 err = "string expected for identifier";
131 return;
114 } 132 }
115 t.identifier = j["identifier"].string_value(); 133 t.identifier = j["identifier"].string_value();
116 t.name = j["name"].string_value(); 134 t.name = j["name"].string_value();
117 t.description = j["description"].string_value(); 135 t.description = j["description"].string_value();
118 } 136 }
126 }; 144 };
127 } 145 }
128 146
129 template <typename T> 147 template <typename T>
130 static bool 148 static bool
131 toValueExtents(json11::Json j, T &t) { 149 toValueExtents(json11::Json j, T &t, std::string &err) {
132 if (j["extents"].is_null()) { 150 if (j["extents"].is_null()) {
133 return false; 151 return false;
134 } else if (j["extents"].is_object()) { 152 } else if (j["extents"].is_object()) {
135 if (j["extents"]["min"].is_number() && 153 if (j["extents"]["min"].is_number() &&
136 j["extents"]["max"].is_number()) { 154 j["extents"]["max"].is_number()) {
137 t.minValue = j["extents"]["min"].number_value(); 155 t.minValue = j["extents"]["min"].number_value();
138 t.maxValue = j["extents"]["max"].number_value(); 156 t.maxValue = j["extents"]["max"].number_value();
139 return true; 157 return true;
140 } else { 158 } else {
141 throw Failure("numbers expected for min and max"); 159 err = "numbers expected for min and max";
160 return false;
142 } 161 }
143 } else { 162 } else {
144 throw Failure("object expected for extents (if present)"); 163 err = "object expected for extents (if present)";
164 return false;
145 } 165 }
146 } 166 }
147 167
148 static json11::Json 168 static json11::Json
149 fromRealTime(const Vamp::RealTime &r) { 169 fromRealTime(const Vamp::RealTime &r) {
152 { "n", r.nsec } 172 { "n", r.nsec }
153 }; 173 };
154 } 174 }
155 175
156 static Vamp::RealTime 176 static Vamp::RealTime
157 toRealTime(json11::Json j) { 177 toRealTime(json11::Json j, std::string &err) {
158 json11::Json sec = j["s"]; 178 json11::Json sec = j["s"];
159 json11::Json nsec = j["n"]; 179 json11::Json nsec = j["n"];
160 if (!sec.is_number() || !nsec.is_number()) { 180 if (!sec.is_number() || !nsec.is_number()) {
161 throw Failure("invalid Vamp::RealTime object " + j.dump()); 181 err = "invalid Vamp::RealTime object " + j.dump();
182 return {};
162 } 183 }
163 return Vamp::RealTime(sec.int_value(), nsec.int_value()); 184 return Vamp::RealTime(sec.int_value(), nsec.int_value());
164 } 185 }
165 186
166 static std::string 187 static std::string
175 } 196 }
176 return ""; 197 return "";
177 } 198 }
178 199
179 static Vamp::Plugin::OutputDescriptor::SampleType 200 static Vamp::Plugin::OutputDescriptor::SampleType
180 toSampleType(std::string text) { 201 toSampleType(std::string text, std::string &err) {
181 if (text == "OneSamplePerStep") { 202 if (text == "OneSamplePerStep") {
182 return Vamp::Plugin::OutputDescriptor::OneSamplePerStep; 203 return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
183 } else if (text == "FixedSampleRate") { 204 } else if (text == "FixedSampleRate") {
184 return Vamp::Plugin::OutputDescriptor::FixedSampleRate; 205 return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
185 } else if (text == "VariableSampleRate") { 206 } else if (text == "VariableSampleRate") {
186 return Vamp::Plugin::OutputDescriptor::VariableSampleRate; 207 return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
187 } else { 208 } else {
188 throw Failure("invalid sample type string: " + text); 209 err = "invalid sample type string: " + text;
210 return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
189 } 211 }
190 } 212 }
191 213
192 static json11::Json 214 static json11::Json
193 fromConfiguredOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) { 215 fromConfiguredOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
219 }; 241 };
220 return json11::Json(jo); 242 return json11::Json(jo);
221 } 243 }
222 244
223 static Vamp::Plugin::OutputDescriptor 245 static Vamp::Plugin::OutputDescriptor
224 toConfiguredOutputDescriptor(json11::Json j) { 246 toConfiguredOutputDescriptor(json11::Json j, std::string &err) {
225 247
226 Vamp::Plugin::OutputDescriptor od; 248 Vamp::Plugin::OutputDescriptor od;
227 if (!j.is_object()) { 249 if (!j.is_object()) {
228 throw Failure("object expected for output descriptor"); 250 err = "object expected for output descriptor";
251 return {};
229 } 252 }
230 253
231 od.unit = j["unit"].string_value(); 254 od.unit = j["unit"].string_value();
232 255
233 od.sampleType = toSampleType(j["sampleType"].string_value()); 256 od.sampleType = toSampleType(j["sampleType"].string_value(), err);
257 if (failed(err)) return {};
234 258
235 if (!j["sampleRate"].is_number()) { 259 if (!j["sampleRate"].is_number()) {
236 throw Failure("number expected for sample rate"); 260 err = "number expected for sample rate";
261 return {};
237 } 262 }
238 od.sampleRate = j["sampleRate"].number_value(); 263 od.sampleRate = j["sampleRate"].number_value();
239 od.hasDuration = j["hasDuration"].bool_value(); 264 od.hasDuration = j["hasDuration"].bool_value();
240 265
241 if (j["binCount"].is_number() && j["binCount"].int_value() > 0) { 266 if (j["binCount"].is_number() && j["binCount"].int_value() > 0) {
242 od.hasFixedBinCount = true; 267 od.hasFixedBinCount = true;
243 od.binCount = j["binCount"].int_value(); 268 od.binCount = j["binCount"].int_value();
244 for (auto &n: j["binNames"].array_items()) { 269 for (auto &n: j["binNames"].array_items()) {
245 if (!n.is_string()) { 270 if (!n.is_string()) {
246 throw Failure("string expected for bin name"); 271 err = "string expected for bin name";
272 return {};
247 } 273 }
248 od.binNames.push_back(n.string_value()); 274 od.binNames.push_back(n.string_value());
249 } 275 }
250 } else { 276 } else {
251 od.hasFixedBinCount = false; 277 od.hasFixedBinCount = false;
252 } 278 }
253 279
254 bool extentsPresent = toValueExtents(j, od); 280 bool extentsPresent = toValueExtents(j, od, err);
281 if (failed(err)) return {};
282
255 od.hasKnownExtents = extentsPresent; 283 od.hasKnownExtents = extentsPresent;
256 284
257 if (j["quantizeStep"].is_number()) { 285 if (j["quantizeStep"].is_number()) {
258 od.isQuantized = true; 286 od.isQuantized = true;
259 od.quantizeStep = j["quantizeStep"].number_value(); 287 od.quantizeStep = j["quantizeStep"].number_value();
263 291
264 return od; 292 return od;
265 } 293 }
266 294
267 static Vamp::Plugin::OutputDescriptor 295 static Vamp::Plugin::OutputDescriptor
268 toOutputDescriptor(json11::Json j) { 296 toOutputDescriptor(json11::Json j, std::string &err) {
269 297
270 Vamp::Plugin::OutputDescriptor od; 298 Vamp::Plugin::OutputDescriptor od;
271 if (!j.is_object()) { 299 if (!j.is_object()) {
272 throw Failure("object expected for output descriptor"); 300 err = "object expected for output descriptor";
273 } 301 return {};
274 302 }
275 od = toConfiguredOutputDescriptor(j); 303
276 304 od = toConfiguredOutputDescriptor(j, err);
277 toBasicDescriptor(j["basic"], od); 305 if (failed(err)) return {};
306
307 toBasicDescriptor(j["basic"], od, err);
308 if (failed(err)) return {};
278 309
279 return od; 310 return od;
280 } 311 }
281 312
282 static json11::Json 313 static json11::Json
295 } 326 }
296 return json11::Json(jo); 327 return json11::Json(jo);
297 } 328 }
298 329
299 static Vamp::PluginBase::ParameterDescriptor 330 static Vamp::PluginBase::ParameterDescriptor
300 toParameterDescriptor(json11::Json j) { 331 toParameterDescriptor(json11::Json j, std::string &err) {
301 332
302 Vamp::PluginBase::ParameterDescriptor pd; 333 Vamp::PluginBase::ParameterDescriptor pd;
303 if (!j.is_object()) { 334 if (!j.is_object()) {
304 throw Failure("object expected for parameter descriptor"); 335 err = "object expected for parameter descriptor";
305 } 336 return {};
306 337 }
307 toBasicDescriptor(j["basic"], pd); 338
339 toBasicDescriptor(j["basic"], pd, err);
340 if (failed(err)) return {};
308 341
309 pd.unit = j["unit"].string_value(); 342 pd.unit = j["unit"].string_value();
310 343
311 bool extentsPresent = toValueExtents(j, pd); 344 bool extentsPresent = toValueExtents(j, pd, err);
345 if (failed(err)) return {};
312 if (!extentsPresent) { 346 if (!extentsPresent) {
313 throw Failure("extents must be present in parameter descriptor"); 347 err = "extents must be present in parameter descriptor";
348 return {};
314 } 349 }
315 350
316 if (!j["defaultValue"].is_number()) { 351 if (!j["defaultValue"].is_number()) {
317 throw Failure("number expected for default value"); 352 err = "number expected for default value";
353 return {};
318 } 354 }
319 355
320 pd.defaultValue = j["defaultValue"].number_value(); 356 pd.defaultValue = j["defaultValue"].number_value();
321 357
322 pd.valueNames.clear(); 358 pd.valueNames.clear();
323 for (auto &n: j["valueNames"].array_items()) { 359 for (auto &n: j["valueNames"].array_items()) {
324 if (!n.is_string()) { 360 if (!n.is_string()) {
325 throw Failure("string expected for value name"); 361 err = "string expected for value name";
362 return {};
326 } 363 }
327 pd.valueNames.push_back(n.string_value()); 364 pd.valueNames.push_back(n.string_value());
328 } 365 }
329 366
330 if (j["quantizeStep"].is_number()) { 367 if (j["quantizeStep"].is_number()) {
347 bn::encode_b64(start, end, back_inserter(encoded)); 384 bn::encode_b64(start, end, back_inserter(encoded));
348 return encoded; 385 return encoded;
349 } 386 }
350 387
351 static std::vector<float> 388 static std::vector<float>
352 toFloatBuffer(std::string encoded) { 389 toFloatBuffer(std::string encoded, std::string & /* err */) {
353 std::string decoded; 390 std::string decoded;
354 bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded)); 391 bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded));
355 const float *buffer = reinterpret_cast<const float *>(decoded.c_str()); 392 const float *buffer = reinterpret_cast<const float *>(decoded.c_str());
356 size_t n = decoded.size() / sizeof(float); 393 size_t n = decoded.size() / sizeof(float);
357 return std::vector<float>(buffer, buffer + n); 394 return std::vector<float>(buffer, buffer + n);
382 } 419 }
383 return json11::Json(jo); 420 return json11::Json(jo);
384 } 421 }
385 422
386 static Vamp::Plugin::Feature 423 static Vamp::Plugin::Feature
387 toFeature(json11::Json j, 424 toFeature(json11::Json j, BufferSerialisation &serialisation, std::string &err) {
388 BufferSerialisation &serialisation) {
389 425
390 Vamp::Plugin::Feature f; 426 Vamp::Plugin::Feature f;
391 if (!j.is_object()) { 427 if (!j.is_object()) {
392 throw Failure("object expected for feature"); 428 err = "object expected for feature";
429 return {};
393 } 430 }
394 if (j["timestamp"].is_object()) { 431 if (j["timestamp"].is_object()) {
395 f.timestamp = toRealTime(j["timestamp"]); 432 f.timestamp = toRealTime(j["timestamp"], err);
433 if (failed(err)) return {};
396 f.hasTimestamp = true; 434 f.hasTimestamp = true;
397 } 435 }
398 if (j["duration"].is_object()) { 436 if (j["duration"].is_object()) {
399 f.duration = toRealTime(j["duration"]); 437 f.duration = toRealTime(j["duration"], err);
438 if (failed(err)) return {};
400 f.hasDuration = true; 439 f.hasDuration = true;
401 } 440 }
402 if (j["b64values"].is_string()) { 441 if (j["b64values"].is_string()) {
403 f.values = toFloatBuffer(j["b64values"].string_value()); 442 f.values = toFloatBuffer(j["b64values"].string_value(), err);
443 if (failed(err)) return {};
404 serialisation = BufferSerialisation::Base64; 444 serialisation = BufferSerialisation::Base64;
405 } else if (j["values"].is_array()) { 445 } else if (j["values"].is_array()) {
406 for (auto v : j["values"].array_items()) { 446 for (auto v : j["values"].array_items()) {
407 f.values.push_back(v.number_value()); 447 f.values.push_back(v.number_value());
408 } 448 }
428 return json11::Json(jo); 468 return json11::Json(jo);
429 } 469 }
430 470
431 static Vamp::Plugin::FeatureList 471 static Vamp::Plugin::FeatureList
432 toFeatureList(json11::Json j, 472 toFeatureList(json11::Json j,
433 BufferSerialisation &serialisation) { 473 BufferSerialisation &serialisation, std::string &err) {
434 474
435 Vamp::Plugin::FeatureList fl; 475 Vamp::Plugin::FeatureList fl;
436 if (!j.is_array()) { 476 if (!j.is_array()) {
437 throw Failure("array expected for feature list"); 477 err = "array expected for feature list";
478 return {};
438 } 479 }
439 for (const json11::Json &fj : j.array_items()) { 480 for (const json11::Json &fj : j.array_items()) {
440 fl.push_back(toFeature(fj, serialisation)); 481 fl.push_back(toFeature(fj, serialisation, err));
482 if (failed(err)) return {};
441 } 483 }
442 return fl; 484 return fl;
443 } 485 }
444 486
445 static Vamp::Plugin::FeatureSet 487 static Vamp::Plugin::FeatureSet
446 toFeatureSet(json11::Json j, 488 toFeatureSet(json11::Json j,
447 const PluginOutputIdMapper &omapper, 489 const PluginOutputIdMapper &omapper,
448 BufferSerialisation &serialisation) { 490 BufferSerialisation &serialisation,
491 std::string &err) {
449 492
450 Vamp::Plugin::FeatureSet fs; 493 Vamp::Plugin::FeatureSet fs;
451 if (!j.is_object()) { 494 if (!j.is_object()) {
452 throw Failure("object expected for feature set"); 495 err = "object expected for feature set";
496 return {};
453 } 497 }
454 for (auto &entry : j.object_items()) { 498 for (auto &entry : j.object_items()) {
455 int n = omapper.idToIndex(entry.first); 499 int n = omapper.idToIndex(entry.first);
456 if (fs.find(n) != fs.end()) { 500 if (fs.find(n) != fs.end()) {
457 throw Failure("duplicate numerical index for output"); 501 err = "duplicate numerical index for output";
458 } 502 return {};
459 fs[n] = toFeatureList(entry.second, serialisation); 503 }
504 fs[n] = toFeatureList(entry.second, serialisation, err);
505 if (failed(err)) return {};
460 } 506 }
461 return fs; 507 return fs;
462 } 508 }
463 509
464 static std::string 510 static std::string
472 } 518 }
473 return ""; 519 return "";
474 } 520 }
475 521
476 static Vamp::Plugin::InputDomain 522 static Vamp::Plugin::InputDomain
477 toInputDomain(std::string text) { 523 toInputDomain(std::string text, std::string &err) {
478 524
479 if (text == "TimeDomain") { 525 if (text == "TimeDomain") {
480 return Vamp::Plugin::TimeDomain; 526 return Vamp::Plugin::TimeDomain;
481 } else if (text == "FrequencyDomain") { 527 } else if (text == "FrequencyDomain") {
482 return Vamp::Plugin::FrequencyDomain; 528 return Vamp::Plugin::FrequencyDomain;
483 } else { 529 } else {
484 throw Failure("invalid input domain string: " + text); 530 err = "invalid input domain string: " + text;
531 return {};
485 } 532 }
486 } 533 }
487 534
488 static json11::Json 535 static json11::Json
489 fromPluginStaticData(const Vamp::HostExt::PluginStaticData &d) { 536 fromPluginStaticData(const Vamp::HostExt::PluginStaticData &d) {
521 568
522 return json11::Json(jo); 569 return json11::Json(jo);
523 } 570 }
524 571
525 static Vamp::HostExt::PluginStaticData 572 static Vamp::HostExt::PluginStaticData
526 toPluginStaticData(json11::Json j) { 573 toPluginStaticData(json11::Json j, std::string &err) {
527 574
528 std::string err;
529 if (!j.has_shape({ 575 if (!j.has_shape({
530 { "pluginKey", json11::Json::STRING }, 576 { "pluginKey", json11::Json::STRING },
531 { "pluginVersion", json11::Json::NUMBER }, 577 { "pluginVersion", json11::Json::NUMBER },
532 { "minChannelCount", json11::Json::NUMBER }, 578 { "minChannelCount", json11::Json::NUMBER },
533 { "maxChannelCount", json11::Json::NUMBER }, 579 { "maxChannelCount", json11::Json::NUMBER },
534 { "inputDomain", json11::Json::STRING }}, err)) { 580 { "inputDomain", json11::Json::STRING }}, err)) {
535 throw Failure("malformed plugin static data: " + err); 581
536 } 582 err = "malformed plugin static data: " + err;
537 583
538 if (!j["basicOutputInfo"].is_array()) { 584 } else if (!j["basicOutputInfo"].is_array()) {
539 throw Failure("array expected for basic output info"); 585
540 } 586 err = "array expected for basic output info";
541 587
542 if (!j["maker"].is_null() && 588 } else if (!j["maker"].is_null() &&
543 !j["maker"].is_string()) { 589 !j["maker"].is_string()) {
544 throw Failure("string expected for maker"); 590
545 } 591 err = "string expected for maker";
546 592
547 if (!j["copyright"].is_null() && 593 } else if (!j["copyright"].is_null() &&
548 !j["copyright"].is_string()) { 594 !j["copyright"].is_string()) {
549 throw Failure("string expected for copyright"); 595 err = "string expected for copyright";
550 } 596
551 597 } else if (!j["category"].is_null() &&
552 if (!j["category"].is_null() && 598 !j["category"].is_array()) {
553 !j["category"].is_array()) { 599
554 throw Failure("array expected for category"); 600 err = "array expected for category";
555 } 601
556 602 } else if (!j["parameters"].is_null() &&
557 if (!j["parameters"].is_null() && 603 !j["parameters"].is_array()) {
558 !j["parameters"].is_array()) { 604
559 throw Failure("array expected for parameters"); 605 err = "array expected for parameters";
560 } 606
561 607 } else if (!j["programs"].is_null() &&
562 if (!j["programs"].is_null() && 608 !j["programs"].is_array()) {
563 !j["programs"].is_array()) { 609
564 throw Failure("array expected for programs"); 610 err = "array expected for programs";
565 } 611
566 612 } else if (!j["inputDomain"].is_null() &&
567 if (!j["inputDomain"].is_null() && 613 !j["inputDomain"].is_string()) {
568 !j["inputDomain"].is_string()) { 614
569 throw Failure("string expected for inputDomain"); 615 err = "string expected for inputDomain";
570 } 616
571 617 } else if (!j["basicOutputInfo"].is_null() &&
572 if (!j["basicOutputInfo"].is_null() && 618 !j["basicOutputInfo"].is_array()) {
573 !j["basicOutputInfo"].is_array()) { 619
574 throw Failure("array expected for basicOutputInfo"); 620 err = "array expected for basicOutputInfo";
575 } 621
576 622 } else {
577 Vamp::HostExt::PluginStaticData psd; 623
578 624 Vamp::HostExt::PluginStaticData psd;
579 psd.pluginKey = j["pluginKey"].string_value(); 625
580 626 psd.pluginKey = j["pluginKey"].string_value();
581 toBasicDescriptor(j["basic"], psd.basic); 627
582 628 toBasicDescriptor(j["basic"], psd.basic, err);
583 psd.maker = j["maker"].string_value(); 629 if (failed(err)) return {};
584 psd.copyright = j["copyright"].string_value(); 630
585 psd.pluginVersion = j["pluginVersion"].int_value(); 631 psd.maker = j["maker"].string_value();
586 632 psd.copyright = j["copyright"].string_value();
587 for (const auto &c : j["category"].array_items()) { 633 psd.pluginVersion = j["pluginVersion"].int_value();
588 if (!c.is_string()) { 634
589 throw Failure("strings expected in category array"); 635 for (const auto &c : j["category"].array_items()) {
590 } 636 if (!c.is_string()) {
591 psd.category.push_back(c.string_value()); 637 err = "strings expected in category array";
592 } 638 return {};
593 639 }
594 psd.minChannelCount = j["minChannelCount"].int_value(); 640 psd.category.push_back(c.string_value());
595 psd.maxChannelCount = j["maxChannelCount"].int_value(); 641 }
596 642
597 for (const auto &p : j["parameters"].array_items()) { 643 psd.minChannelCount = j["minChannelCount"].int_value();
598 auto pd = toParameterDescriptor(p); 644 psd.maxChannelCount = j["maxChannelCount"].int_value();
599 psd.parameters.push_back(pd); 645
600 } 646 for (const auto &p : j["parameters"].array_items()) {
601 647 auto pd = toParameterDescriptor(p, err);
602 for (const auto &p : j["programs"].array_items()) { 648 if (failed(err)) return {};
603 if (!p.is_string()) { 649 psd.parameters.push_back(pd);
604 throw Failure("strings expected in programs array"); 650 }
605 } 651
606 psd.programs.push_back(p.string_value()); 652 for (const auto &p : j["programs"].array_items()) {
607 } 653 if (!p.is_string()) {
608 654 err = "strings expected in programs array";
609 psd.inputDomain = toInputDomain(j["inputDomain"].string_value()); 655 return {};
610 656 }
611 for (const auto &bo : j["basicOutputInfo"].array_items()) { 657 psd.programs.push_back(p.string_value());
612 Vamp::HostExt::PluginStaticData::Basic b; 658 }
613 toBasicDescriptor(bo, b); 659
614 psd.basicOutputInfo.push_back(b); 660 psd.inputDomain = toInputDomain(j["inputDomain"].string_value(), err);
615 } 661 if (failed(err)) return {};
616 662
617 return psd; 663 for (const auto &bo : j["basicOutputInfo"].array_items()) {
664 Vamp::HostExt::PluginStaticData::Basic b;
665 toBasicDescriptor(bo, b, err);
666 if (failed(err)) return {};
667 psd.basicOutputInfo.push_back(b);
668 }
669
670 return psd;
671 }
672
673 // fallthrough error case
674 return {};
618 } 675 }
619 676
620 static json11::Json 677 static json11::Json
621 fromPluginConfiguration(const Vamp::HostExt::PluginConfiguration &c) { 678 fromPluginConfiguration(const Vamp::HostExt::PluginConfiguration &c) {
622 679
638 695
639 return json11::Json(jo); 696 return json11::Json(jo);
640 } 697 }
641 698
642 static Vamp::HostExt::PluginConfiguration 699 static Vamp::HostExt::PluginConfiguration
643 toPluginConfiguration(json11::Json j) { 700 toPluginConfiguration(json11::Json j, std::string &err) {
644 701
645 std::string err;
646 if (!j.has_shape({ 702 if (!j.has_shape({
647 { "channelCount", json11::Json::NUMBER }, 703 { "channelCount", json11::Json::NUMBER },
648 { "stepSize", json11::Json::NUMBER }, 704 { "stepSize", json11::Json::NUMBER },
649 { "blockSize", json11::Json::NUMBER } }, err)) { 705 { "blockSize", json11::Json::NUMBER } }, err)) {
650 throw Failure("malformed plugin configuration: " + err); 706 err = "malformed plugin configuration: " + err;
707 return {};
651 } 708 }
652 709
653 if (!j["parameterValues"].is_null() && 710 if (!j["parameterValues"].is_null() &&
654 !j["parameterValues"].is_object()) { 711 !j["parameterValues"].is_object()) {
655 throw Failure("object expected for parameter values"); 712 err = "object expected for parameter values";
713 return {};
656 } 714 }
657 715
658 for (auto &pv : j["parameterValues"].object_items()) { 716 for (auto &pv : j["parameterValues"].object_items()) {
659 if (!pv.second.is_number()) { 717 if (!pv.second.is_number()) {
660 throw Failure("number expected for parameter value"); 718 err = "number expected for parameter value";
719 return {};
661 } 720 }
662 } 721 }
663 722
664 if (!j["currentProgram"].is_null() && 723 if (!j["currentProgram"].is_null() &&
665 !j["currentProgram"].is_string()) { 724 !j["currentProgram"].is_string()) {
666 throw Failure("string expected for program name"); 725 err = "string expected for program name";
726 return {};
667 } 727 }
668 728
669 Vamp::HostExt::PluginConfiguration config; 729 Vamp::HostExt::PluginConfiguration config;
670 730
671 config.channelCount = j["channelCount"].number_value(); 731 config.channelCount = j["channelCount"].number_value();
700 760
701 return json11::Json(arr); 761 return json11::Json(arr);
702 } 762 }
703 763
704 static Vamp::HostExt::PluginLoader::AdapterFlags 764 static Vamp::HostExt::PluginLoader::AdapterFlags
705 toAdapterFlags(json11::Json j) { 765 toAdapterFlags(json11::Json j, std::string &err) {
766
767 int flags = 0x0;
706 768
707 if (!j.is_array()) { 769 if (!j.is_array()) {
708 throw Failure("array expected for adapter flags"); 770
709 } 771 err = "array expected for adapter flags";
710 int flags = 0x0; 772
711 773 } else {
712 for (auto &jj: j.array_items()) { 774
713 if (!jj.is_string()) { 775 for (auto &jj: j.array_items()) {
714 throw Failure("string expected for adapter flag"); 776 if (!jj.is_string()) {
715 } 777 err = "string expected for adapter flag";
716 std::string text = jj.string_value(); 778 break;
717 if (text == "AdaptInputDomain") { 779 }
718 flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN; 780 std::string text = jj.string_value();
719 } else if (text == "AdaptChannelCount") { 781 if (text == "AdaptInputDomain") {
720 flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT; 782 flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
721 } else if (text == "AdaptBufferSize") { 783 } else if (text == "AdaptChannelCount") {
722 flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE; 784 flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
723 } else if (text == "AdaptAllSafe") { 785 } else if (text == "AdaptBufferSize") {
724 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE; 786 flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
725 } else if (text == "AdaptAll") { 787 } else if (text == "AdaptAllSafe") {
726 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL; 788 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE;
727 } else { 789 } else if (text == "AdaptAll") {
728 throw Failure("invalid adapter flag string: " + text); 790 flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL;
791 } else {
792 err = "invalid adapter flag string: " + text;
793 break;
794 }
729 } 795 }
730 } 796 }
731 797
732 return Vamp::HostExt::PluginLoader::AdapterFlags(flags); 798 return Vamp::HostExt::PluginLoader::AdapterFlags(flags);
733 } 799 }
741 jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags); 807 jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags);
742 return json11::Json(jo); 808 return json11::Json(jo);
743 } 809 }
744 810
745 static Vamp::HostExt::LoadRequest 811 static Vamp::HostExt::LoadRequest
746 toLoadRequest(json11::Json j) { 812 toLoadRequest(json11::Json j, std::string &err) {
747 813
748 std::string err;
749
750 if (!j.has_shape({ 814 if (!j.has_shape({
751 { "pluginKey", json11::Json::STRING }, 815 { "pluginKey", json11::Json::STRING },
752 { "inputSampleRate", json11::Json::NUMBER } }, err)) { 816 { "inputSampleRate", json11::Json::NUMBER } }, err)) {
753 throw Failure("malformed load request: " + err); 817 err = "malformed load request: " + err;
818 return {};
754 } 819 }
755 820
756 Vamp::HostExt::LoadRequest req; 821 Vamp::HostExt::LoadRequest req;
757 req.pluginKey = j["pluginKey"].string_value(); 822 req.pluginKey = j["pluginKey"].string_value();
758 req.inputSampleRate = j["inputSampleRate"].number_value(); 823 req.inputSampleRate = j["inputSampleRate"].number_value();
759 if (!j["adapterFlags"].is_null()) { 824 if (!j["adapterFlags"].is_null()) {
760 req.adapterFlags = toAdapterFlags(j["adapterFlags"]); 825 req.adapterFlags = toAdapterFlags(j["adapterFlags"], err);
826 if (failed(err)) return {};
761 } 827 }
762 return req; 828 return req;
763 } 829 }
764 830
765 static json11::Json 831 static json11::Json
774 return json11::Json(jo); 840 return json11::Json(jo);
775 } 841 }
776 842
777 static Vamp::HostExt::LoadResponse 843 static Vamp::HostExt::LoadResponse
778 toLoadResponse(json11::Json j, 844 toLoadResponse(json11::Json j,
779 const PluginHandleMapper &pmapper) { 845 const PluginHandleMapper &pmapper, std::string &err) {
780
781 std::string err;
782 846
783 if (!j.has_shape({ 847 if (!j.has_shape({
784 { "pluginHandle", json11::Json::NUMBER }, 848 { "pluginHandle", json11::Json::NUMBER },
785 { "staticData", json11::Json::OBJECT }, 849 { "staticData", json11::Json::OBJECT },
786 { "defaultConfiguration", json11::Json::OBJECT } }, err)) { 850 { "defaultConfiguration", json11::Json::OBJECT } }, err)) {
787 throw Failure("malformed load response: " + err); 851 err = "malformed load response: " + err;
852 return {};
788 } 853 }
789 854
790 Vamp::HostExt::LoadResponse resp; 855 Vamp::HostExt::LoadResponse resp;
791 resp.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value()); 856 resp.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value());
792 resp.staticData = toPluginStaticData(j["staticData"]); 857 resp.staticData = toPluginStaticData(j["staticData"], err);
793 resp.defaultConfiguration = toPluginConfiguration(j["defaultConfiguration"]); 858 if (failed(err)) return {};
859 resp.defaultConfiguration = toPluginConfiguration(j["defaultConfiguration"],
860 err);
861 if (failed(err)) return {};
794 return resp; 862 return resp;
795 } 863 }
796 864
797 static json11::Json 865 static json11::Json
798 fromConfigurationRequest(const Vamp::HostExt::ConfigurationRequest &cr, 866 fromConfigurationRequest(const Vamp::HostExt::ConfigurationRequest &cr,
806 return json11::Json(jo); 874 return json11::Json(jo);
807 } 875 }
808 876
809 static Vamp::HostExt::ConfigurationRequest 877 static Vamp::HostExt::ConfigurationRequest
810 toConfigurationRequest(json11::Json j, 878 toConfigurationRequest(json11::Json j,
811 const PluginHandleMapper &pmapper) { 879 const PluginHandleMapper &pmapper, std::string &err) {
812
813 std::string err;
814 880
815 if (!j.has_shape({ 881 if (!j.has_shape({
816 { "pluginHandle", json11::Json::NUMBER }, 882 { "pluginHandle", json11::Json::NUMBER },
817 { "configuration", json11::Json::OBJECT } }, err)) { 883 { "configuration", json11::Json::OBJECT } }, err)) {
818 throw Failure("malformed configuration request: " + err); 884 err = "malformed configuration request: " + err;
885 return {};
819 } 886 }
820 887
821 Vamp::HostExt::ConfigurationRequest cr; 888 Vamp::HostExt::ConfigurationRequest cr;
822 cr.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value()); 889 cr.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value());
823 cr.configuration = toPluginConfiguration(j["configuration"]); 890 cr.configuration = toPluginConfiguration(j["configuration"], err);
891 if (failed(err)) return {};
824 return cr; 892 return cr;
825 } 893 }
826 894
827 static json11::Json 895 static json11::Json
828 fromConfigurationResponse(const Vamp::HostExt::ConfigurationResponse &cr, 896 fromConfigurationResponse(const Vamp::HostExt::ConfigurationResponse &cr,
841 return json11::Json(jo); 909 return json11::Json(jo);
842 } 910 }
843 911
844 static Vamp::HostExt::ConfigurationResponse 912 static Vamp::HostExt::ConfigurationResponse
845 toConfigurationResponse(json11::Json j, 913 toConfigurationResponse(json11::Json j,
846 const PluginHandleMapper &pmapper) { 914 const PluginHandleMapper &pmapper, std::string &err) {
847 915
848 Vamp::HostExt::ConfigurationResponse cr; 916 Vamp::HostExt::ConfigurationResponse cr;
849 917
850 cr.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value()); 918 cr.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value());
851 919
852 if (!j["outputList"].is_array()) { 920 if (!j["outputList"].is_array()) {
853 throw Failure("array expected for output list"); 921 err = "array expected for output list";
922 return {};
854 } 923 }
855 924
856 for (const auto &o: j["outputList"].array_items()) { 925 for (const auto &o: j["outputList"].array_items()) {
857 cr.outputs.push_back(toOutputDescriptor(o)); 926 cr.outputs.push_back(toOutputDescriptor(o, err));
927 if (failed(err)) return {};
858 } 928 }
859 929
860 return cr; 930 return cr;
861 } 931 }
862 932
890 } 960 }
891 961
892 static Vamp::HostExt::ProcessRequest 962 static Vamp::HostExt::ProcessRequest
893 toProcessRequest(json11::Json j, 963 toProcessRequest(json11::Json j,
894 const PluginHandleMapper &pmapper, 964 const PluginHandleMapper &pmapper,
895 BufferSerialisation &serialisation) { 965 BufferSerialisation &serialisation, std::string &err) {
896
897 std::string err;
898 966
899 if (!j.has_shape({ 967 if (!j.has_shape({
900 { "pluginHandle", json11::Json::NUMBER }, 968 { "pluginHandle", json11::Json::NUMBER },
901 { "processInput", json11::Json::OBJECT } }, err)) { 969 { "processInput", json11::Json::OBJECT } }, err)) {
902 throw Failure("malformed process request: " + err); 970 err = "malformed process request: " + err;
971 return {};
903 } 972 }
904 973
905 auto input = j["processInput"]; 974 auto input = j["processInput"];
906 975
907 if (!input.has_shape({ 976 if (!input.has_shape({
908 { "timestamp", json11::Json::OBJECT }, 977 { "timestamp", json11::Json::OBJECT },
909 { "inputBuffers", json11::Json::ARRAY } }, err)) { 978 { "inputBuffers", json11::Json::ARRAY } }, err)) {
910 throw Failure("malformed process request: " + err); 979 err = "malformed process request: " + err;
980 return {};
911 } 981 }
912 982
913 Vamp::HostExt::ProcessRequest r; 983 Vamp::HostExt::ProcessRequest r;
914 r.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value()); 984 r.plugin = pmapper.handleToPlugin(j["pluginHandle"].int_value());
915 985
916 r.timestamp = toRealTime(input["timestamp"]); 986 r.timestamp = toRealTime(input["timestamp"], err);
987 if (failed(err)) return {};
917 988
918 for (auto a: input["inputBuffers"].array_items()) { 989 for (auto a: input["inputBuffers"].array_items()) {
919 990
920 if (a["b64values"].is_string()) { 991 if (a["b64values"].is_string()) {
921 std::vector<float> buf = toFloatBuffer(a["b64values"].string_value()); 992 std::vector<float> buf = toFloatBuffer(a["b64values"].string_value(),
993 err);
994 if (failed(err)) return {};
922 r.inputBuffers.push_back(buf); 995 r.inputBuffers.push_back(buf);
923 serialisation = BufferSerialisation::Base64; 996 serialisation = BufferSerialisation::Base64;
924 997
925 } else if (a["values"].is_array()) { 998 } else if (a["values"].is_array()) {
926 std::vector<float> buf; 999 std::vector<float> buf;
929 } 1002 }
930 r.inputBuffers.push_back(buf); 1003 r.inputBuffers.push_back(buf);
931 serialisation = BufferSerialisation::Text; 1004 serialisation = BufferSerialisation::Text;
932 1005
933 } else { 1006 } else {
934 throw Failure("expected values or b64values in inputBuffers object"); 1007 err = "expected values or b64values in inputBuffers object";
1008 return {};
935 } 1009 }
936 } 1010 }
937 1011
938 return r; 1012 return r;
939 } 1013 }
1082 jo["type"] = type; 1156 jo["type"] = type;
1083 jo["success"] = false; 1157 jo["success"] = false;
1084 jo["errorText"] = std::string("error in ") + type + " request: " + errorText; 1158 jo["errorText"] = std::string("error in ") + type + " request: " + errorText;
1085 return json11::Json(jo); 1159 return json11::Json(jo);
1086 } 1160 }
1087
1088 static json11::Json
1089 fromException(const std::exception &e, RRType responseType) {
1090
1091 return fromError(e.what(), responseType);
1092 }
1093 1161
1094 private: // go private briefly for a couple of helper functions 1162 private: // go private briefly for a couple of helper functions
1095 1163
1096 static void 1164 static void
1097 checkTypeField(json11::Json j, std::string expected) { 1165 checkTypeField(json11::Json j, std::string expected, std::string &err) {
1098 if (!j["type"].is_string()) { 1166 if (!j["type"].is_string()) {
1099 throw Failure("string expected for type"); 1167 err = "string expected for type";
1168 return;
1100 } 1169 }
1101 if (j["type"].string_value() != expected) { 1170 if (j["type"].string_value() != expected) {
1102 throw Failure("expected value \"" + expected + "\" for type"); 1171 err = "expected value \"" + expected + "\" for type";
1172 return;
1103 } 1173 }
1104 } 1174 }
1105 1175
1106 static bool 1176 static bool
1107 successful(json11::Json j) { 1177 successful(json11::Json j, std::string &err) {
1108 if (!j["success"].is_bool()) { 1178 if (!j["success"].is_bool()) {
1109 throw Failure("bool expected for success"); 1179 err = "bool expected for success";
1180 return false;
1110 } 1181 }
1111 return j["success"].bool_value(); 1182 return j["success"].bool_value();
1112 } 1183 }
1113 1184
1114 public: 1185 public:
1115 static RRType 1186 static RRType
1116 getRequestResponseType(json11::Json j) { 1187 getRequestResponseType(json11::Json j, std::string &err) {
1117 1188
1118 if (!j["type"].is_string()) { 1189 if (!j["type"].is_string()) {
1119 throw Failure("string expected for type"); 1190 err = "string expected for type";
1191 return RRType::NotValid;
1120 } 1192 }
1121 1193
1122 std::string type = j["type"].string_value(); 1194 std::string type = j["type"].string_value();
1123 1195
1124 if (type == "list") return RRType::List; 1196 if (type == "list") return RRType::List;
1125 else if (type == "load") return RRType::Load; 1197 else if (type == "load") return RRType::Load;
1126 else if (type == "configure") return RRType::Configure; 1198 else if (type == "configure") return RRType::Configure;
1127 else if (type == "process") return RRType::Process; 1199 else if (type == "process") return RRType::Process;
1128 else if (type == "finish") return RRType::Finish; 1200 else if (type == "finish") return RRType::Finish;
1201 else if (type == "invalid") return RRType::NotValid;
1129 else { 1202 else {
1130 throw Failure("unknown or unexpected request/response type \"" + 1203 err = "unknown or unexpected request/response type \"" + type + "\"";
1131 type + "\""); 1204 return RRType::NotValid;
1132 } 1205 }
1133 } 1206 }
1134 1207
1135 static void 1208 static void
1136 toVampRequest_List(json11::Json j) { 1209 toVampRequest_List(json11::Json j, std::string &err) {
1137 1210 checkTypeField(j, "list", err);
1138 checkTypeField(j, "list");
1139 } 1211 }
1140 1212
1141 static Vamp::HostExt::ListResponse 1213 static Vamp::HostExt::ListResponse
1142 toVampResponse_List(json11::Json j) { 1214 toVampResponse_List(json11::Json j, std::string &err) {
1143 1215
1144 Vamp::HostExt::ListResponse resp; 1216 Vamp::HostExt::ListResponse resp;
1145 if (successful(j)) { 1217 if (successful(j, err) && !failed(err)) {
1146 for (const auto &a: j["content"]["plugins"].array_items()) { 1218 for (const auto &a: j["content"]["plugins"].array_items()) {
1147 resp.plugins.push_back(toPluginStaticData(a)); 1219 resp.plugins.push_back(toPluginStaticData(a, err));
1148 } 1220 if (failed(err)) return {};
1149 } 1221 }
1222 }
1223
1150 return resp; 1224 return resp;
1151 } 1225 }
1152 1226
1153 static Vamp::HostExt::LoadRequest 1227 static Vamp::HostExt::LoadRequest
1154 toVampRequest_Load(json11::Json j) { 1228 toVampRequest_Load(json11::Json j, std::string &err) {
1155 1229
1156 checkTypeField(j, "load"); 1230 checkTypeField(j, "load", err);
1157 return toLoadRequest(j["content"]); 1231 if (failed(err)) return {};
1232 return toLoadRequest(j["content"], err);
1158 } 1233 }
1159 1234
1160 static Vamp::HostExt::LoadResponse 1235 static Vamp::HostExt::LoadResponse
1161 toVampResponse_Load(json11::Json j, const PluginHandleMapper &pmapper) { 1236 toVampResponse_Load(json11::Json j,
1237 const PluginHandleMapper &pmapper,
1238 std::string &err) {
1162 1239
1163 Vamp::HostExt::LoadResponse resp; 1240 Vamp::HostExt::LoadResponse resp;
1164 if (successful(j)) { 1241 if (successful(j, err) && !failed(err)) {
1165 resp = toLoadResponse(j["content"], pmapper); 1242 resp = toLoadResponse(j["content"], pmapper, err);
1166 } 1243 }
1167 return resp; 1244 return resp;
1168 } 1245 }
1169 1246
1170 static Vamp::HostExt::ConfigurationRequest 1247 static Vamp::HostExt::ConfigurationRequest
1171 toVampRequest_Configure(json11::Json j, const PluginHandleMapper &pmapper) { 1248 toVampRequest_Configure(json11::Json j,
1172 1249 const PluginHandleMapper &pmapper,
1173 checkTypeField(j, "configure"); 1250 std::string &err) {
1174 return toConfigurationRequest(j["content"], pmapper); 1251
1252 checkTypeField(j, "configure", err);
1253 if (failed(err)) return {};
1254 return toConfigurationRequest(j["content"], pmapper, err);
1175 } 1255 }
1176 1256
1177 static Vamp::HostExt::ConfigurationResponse 1257 static Vamp::HostExt::ConfigurationResponse
1178 toVampResponse_Configure(json11::Json j, const PluginHandleMapper &pmapper) { 1258 toVampResponse_Configure(json11::Json j,
1259 const PluginHandleMapper &pmapper,
1260 std::string &err) {
1179 1261
1180 Vamp::HostExt::ConfigurationResponse resp; 1262 Vamp::HostExt::ConfigurationResponse resp;
1181 if (successful(j)) { 1263 if (successful(j, err) && !failed(err)) {
1182 resp = toConfigurationResponse(j["content"], pmapper); 1264 resp = toConfigurationResponse(j["content"], pmapper, err);
1183 } 1265 }
1184 return resp; 1266 return resp;
1185 } 1267 }
1186 1268
1187 static Vamp::HostExt::ProcessRequest 1269 static Vamp::HostExt::ProcessRequest
1188 toVampRequest_Process(json11::Json j, const PluginHandleMapper &pmapper, 1270 toVampRequest_Process(json11::Json j, const PluginHandleMapper &pmapper,
1189 BufferSerialisation &serialisation) { 1271 BufferSerialisation &serialisation, std::string &err) {
1190 1272
1191 checkTypeField(j, "process"); 1273 checkTypeField(j, "process", err);
1192 return toProcessRequest(j["content"], pmapper, serialisation); 1274 if (failed(err)) return {};
1275 return toProcessRequest(j["content"], pmapper, serialisation, err);
1193 } 1276 }
1194 1277
1195 static Vamp::HostExt::ProcessResponse 1278 static Vamp::HostExt::ProcessResponse
1196 toVampResponse_Process(json11::Json j, 1279 toVampResponse_Process(json11::Json j,
1197 const PluginHandleMapper &pmapper, 1280 const PluginHandleMapper &pmapper,
1198 BufferSerialisation &serialisation) { 1281 BufferSerialisation &serialisation, std::string &err) {
1199 1282
1200 Vamp::HostExt::ProcessResponse resp; 1283 Vamp::HostExt::ProcessResponse resp;
1201 if (successful(j)) { 1284 if (successful(j, err) && !failed(err)) {
1202 auto jc = j["content"]; 1285 auto jc = j["content"];
1203 auto h = jc["pluginHandle"].int_value(); 1286 auto h = jc["pluginHandle"].int_value();
1204 resp.plugin = pmapper.handleToPlugin(h); 1287 resp.plugin = pmapper.handleToPlugin(h);
1205 resp.features = toFeatureSet(jc["features"], 1288 resp.features = toFeatureSet(jc["features"],
1206 *pmapper.handleToOutputIdMapper(h), 1289 *pmapper.handleToOutputIdMapper(h),
1207 serialisation); 1290 serialisation, err);
1208 } 1291 }
1209 return resp; 1292 return resp;
1210 } 1293 }
1211 1294
1212 static Vamp::HostExt::FinishRequest 1295 static Vamp::HostExt::FinishRequest
1213 toVampRequest_Finish(json11::Json j, const PluginHandleMapper &pmapper) { 1296 toVampRequest_Finish(json11::Json j, const PluginHandleMapper &pmapper,
1214 1297 std::string &err) {
1215 checkTypeField(j, "finish"); 1298
1299 checkTypeField(j, "finish", err);
1300 if (failed(err)) return {};
1216 Vamp::HostExt::FinishRequest req; 1301 Vamp::HostExt::FinishRequest req;
1217 req.plugin = pmapper.handleToPlugin 1302 req.plugin = pmapper.handleToPlugin
1218 (j["content"]["pluginHandle"].int_value()); 1303 (j["content"]["pluginHandle"].int_value());
1219 return req; 1304 return req;
1220 } 1305 }
1221 1306
1222 static Vamp::HostExt::ProcessResponse 1307 static Vamp::HostExt::ProcessResponse
1223 toVampResponse_Finish(json11::Json j, 1308 toVampResponse_Finish(json11::Json j,
1224 const PluginHandleMapper &pmapper, 1309 const PluginHandleMapper &pmapper,
1225 BufferSerialisation &serialisation) { 1310 BufferSerialisation &serialisation, std::string &err) {
1226 1311
1227 Vamp::HostExt::ProcessResponse resp; 1312 Vamp::HostExt::ProcessResponse resp;
1228 if (successful(j)) { 1313 if (successful(j, err) && !failed(err)) {
1229 auto jc = j["content"]; 1314 auto jc = j["content"];
1230 auto h = jc["pluginHandle"].int_value(); 1315 auto h = jc["pluginHandle"].int_value();
1231 resp.plugin = pmapper.handleToPlugin(h); 1316 resp.plugin = pmapper.handleToPlugin(h);
1232 resp.features = toFeatureSet(jc["features"], 1317 resp.features = toFeatureSet(jc["features"],
1233 *pmapper.handleToOutputIdMapper(h), 1318 *pmapper.handleToOutputIdMapper(h),
1234 serialisation); 1319 serialisation, err);
1235 } 1320 }
1236 return resp; 1321 return resp;
1237 } 1322 }
1238 }; 1323 };
1239 1324