changeset 220:02de5df3a884

Add static output descriptor, pass through type URI Note that the server doesn't actually populate any type URIs yet, so this is only half-tested.
author Chris Cannam <cannam@all-day-breakfast.com>
date Thu, 08 Jun 2017 16:17:03 +0100
parents db929669e7d3
children 9aca3267809c
files README.md vamp-capnp/VampnProto.h vamp-capnp/piper.capnp.c++ vamp-capnp/piper.capnp.h vamp-json/VampJson.h vamp-server/test.sh vamp-support/PluginStaticData.h vamp-support/RequestResponse.h vamp-support/StaticOutputDescriptor.h
diffstat 9 files changed, 826 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/README.md	Mon May 22 08:57:02 2017 +0100
+++ b/README.md	Thu Jun 08 16:17:03 2017 +0100
@@ -4,8 +4,6 @@
 Supporting code in C++ for the Piper audio feature extractor protocol,
 primarily for making Vamp plugins work with the Piper protocol.
 
-_This is pre-1.0 code and the API may change at any time_
-
 ## Contents
 
  * code to adapt Piper messages to the classes used in the Vamp SDK
--- a/vamp-capnp/VampnProto.h	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-capnp/VampnProto.h	Thu Jun 08 16:17:03 2017 +0100
@@ -4,7 +4,7 @@
     Piper C++
 
     Centre for Digital Music, Queen Mary, University of London.
-    Copyright 2015-2016 QMUL.
+    Copyright 2015-2017 QMUL.
   
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation
@@ -131,6 +131,12 @@
     }
 
     static void
+    buildStaticOutputDescriptor(piper::StaticOutputDescriptor::Builder &b,
+                                const StaticOutputDescriptor &sd) {
+        b.setTypeURI(sd.typeURI);
+    }
+    
+    static void
     buildConfiguredOutputDescriptor(piper::ConfiguredOutputDescriptor::Builder &b,
                                     const Vamp::Plugin::OutputDescriptor &od) {
 
@@ -163,13 +169,23 @@
 
     static void
     buildOutputDescriptor(piper::OutputDescriptor::Builder &b,
-                          const Vamp::Plugin::OutputDescriptor &od) {
+                          const Vamp::Plugin::OutputDescriptor &od,
+                          const StaticOutputDescriptor &sd) {
 
         auto basic = b.initBasic();
         buildBasicDescriptor(basic, od);
 
         auto configured = b.initConfigured();
         buildConfiguredOutputDescriptor(configured, od);
+
+        auto statc = b.initStatic();
+        buildStaticOutputDescriptor(statc, sd);
+    }
+
+    static void
+    readStaticOutputDescriptor(StaticOutputDescriptor &sd,
+                               const piper::StaticOutputDescriptor::Reader &r) {
+        sd.typeURI = r.getTypeURI();
     }
     
     static void
@@ -205,9 +221,11 @@
 
     static void
     readOutputDescriptor(Vamp::Plugin::OutputDescriptor &od,
+                         StaticOutputDescriptor &sd,
                          const piper::OutputDescriptor::Reader &r) {
 
         readBasicDescriptor(od, r.getBasic());
+        readStaticOutputDescriptor(sd, r.getStatic());
         readConfiguredOutputDescriptor(od, r.getConfigured());
     }
 
@@ -414,6 +432,17 @@
             auto od = olist[i];
             buildBasicDescriptor(od, vouts[i]);
         }
+
+        const auto &vstatic = d.staticOutputInfo;
+        auto slist = b.initStaticOutputInfo(unsigned(vstatic.size()));
+        int i = 0;
+        for (const auto &vi: vstatic) {
+            auto spair = slist[i];
+            spair.setOutput(vi.first);
+            auto sdata = spair.initStatic();
+            sdata.setTypeURI(vi.second.typeURI);
+            ++i;
+        }
     }
 
     static void
@@ -460,6 +489,14 @@
             readBasicDescriptor(b, o);
             d.basicOutputInfo.push_back(b);
         }
+
+        d.staticOutputInfo.clear();
+        auto sp = r.getStaticOutputInfo();
+        for (auto s: sp) {
+            std::string id = s.getOutput();
+            std::string typeURI = s.getStatic().getTypeURI();
+            d.staticOutputInfo[id] = { typeURI };
+        }
     }
 
     static void
@@ -640,10 +677,19 @@
 
         b.setHandle(pmapper.pluginToHandle(cr.plugin));
         auto olist = b.initOutputs(unsigned(cr.outputs.size()));
+
         for (int i = 0; i < int(cr.outputs.size()); ++i) {
+
+            auto id = cr.outputs[i].identifier;
+            StaticOutputDescriptor sd;
+            if (cr.staticOutputInfo.find(id) != cr.staticOutputInfo.end()) {
+                sd = cr.staticOutputInfo.at(id);
+            }
+
             auto od = olist[i];
-            buildOutputDescriptor(od, cr.outputs[i]);
+            buildOutputDescriptor(od, cr.outputs[i], sd);
         }
+            
         auto framing = b.initFraming();
         framing.setStepSize(cr.framing.stepSize);
         framing.setBlockSize(cr.framing.blockSize);
@@ -656,11 +702,16 @@
 
         cr.plugin = pmapper.handleToPlugin(r.getHandle());
         cr.outputs.clear();
+        cr.staticOutputInfo.clear();
         auto oo = r.getOutputs();
         for (const auto &o: oo) {
             Vamp::Plugin::OutputDescriptor desc;
-            readOutputDescriptor(desc, o);
+            StaticOutputDescriptor sd;
+            readOutputDescriptor(desc, sd, o);
             cr.outputs.push_back(desc);
+            if (sd.typeURI != "") {
+                cr.staticOutputInfo[desc.identifier] = { sd.typeURI };
+            }
         }
         cr.framing.stepSize = r.getFraming().getStepSize();
         cr.framing.blockSize = r.getFraming().getBlockSize();
--- a/vamp-capnp/piper.capnp.c++	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-capnp/piper.capnp.c++	Thu Jun 08 16:17:03 2017 +0100
@@ -287,6 +287,51 @@
 };
 #endif  // !CAPNP_LITE
 CAPNP_DEFINE_ENUM(SampleType_eca23e4a04bdfeb2, eca23e4a04bdfeb2);
+static const ::capnp::_::AlignedData<34> b_a88eab3ec4264cba = {
+  {   0,   0,   0,   0,   5,   0,   6,   0,
+    186,  76,  38, 196,  62, 171, 142, 168,
+     12,   0,   0,   0,   1,   0,   0,   0,
+      6, 146, 153,  76, 196, 198, 177, 196,
+      1,   0,   7,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     21,   0,   0,   0,  26,   1,   0,   0,
+     37,   0,   0,   0,   7,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     33,   0,   0,   0,  63,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    112, 105, 112, 101, 114,  46,  99,  97,
+    112, 110, 112,  58,  83, 116,  97, 116,
+    105,  99,  79, 117, 116, 112, 117, 116,
+     68, 101, 115,  99, 114, 105, 112, 116,
+    111, 114,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   1,   0,   1,   0,
+      4,   0,   0,   0,   3,   0,   4,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   1,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     13,   0,   0,   0,  66,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      8,   0,   0,   0,   3,   0,   1,   0,
+     20,   0,   0,   0,   2,   0,   1,   0,
+    116, 121, 112, 101,  85,  82,  73,   0,
+     12,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     12,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0, }
+};
+::capnp::word const* const bp_a88eab3ec4264cba = b_a88eab3ec4264cba.words;
+#if !CAPNP_LITE
+static const uint16_t m_a88eab3ec4264cba[] = {0};
+static const uint16_t i_a88eab3ec4264cba[] = {0};
+const ::capnp::_::RawSchema s_a88eab3ec4264cba = {
+  0xa88eab3ec4264cba, b_a88eab3ec4264cba.words, 34, nullptr, m_a88eab3ec4264cba,
+  0, 1, i_a88eab3ec4264cba, nullptr, nullptr, { &s_a88eab3ec4264cba, nullptr, nullptr, 0, 0, nullptr }
+};
+#endif  // !CAPNP_LITE
 static const ::capnp::_::AlignedData<215> b_b2d0c825aac8249c = {
   {   0,   0,   0,   0,   5,   0,   6,   0,
     156,  36, 200, 170,  37, 200, 208, 178,
@@ -516,17 +561,17 @@
   1, 12, i_b2d0c825aac8249c, nullptr, nullptr, { &s_b2d0c825aac8249c, nullptr, nullptr, 0, 0, nullptr }
 };
 #endif  // !CAPNP_LITE
-static const ::capnp::_::AlignedData<49> b_902c6065e1be824a = {
+static const ::capnp::_::AlignedData<64> b_902c6065e1be824a = {
   {   0,   0,   0,   0,   5,   0,   6,   0,
      74, 130, 190, 225, 101,  96,  44, 144,
      12,   0,   0,   0,   1,   0,   0,   0,
       6, 146, 153,  76, 196, 198, 177, 196,
-      2,   0,   7,   0,   0,   0,   0,   0,
+      3,   0,   7,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
      21,   0,   0,   0, 234,   0,   0,   0,
      33,   0,   0,   0,   7,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     29,   0,   0,   0, 119,   0,   0,   0,
+     29,   0,   0,   0, 175,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
     112, 105, 112, 101, 114,  46,  99,  97,
@@ -534,21 +579,28 @@
     117, 116,  68, 101, 115,  99, 114, 105,
     112, 116, 111, 114,   0,   0,   0,   0,
       0,   0,   0,   0,   1,   0,   1,   0,
-      8,   0,   0,   0,   3,   0,   4,   0,
+     12,   0,   0,   0,   3,   0,   4,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   1,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     41,   0,   0,   0,  50,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     36,   0,   0,   0,   3,   0,   1,   0,
-     48,   0,   0,   0,   2,   0,   1,   0,
+     69,   0,   0,   0,  50,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     64,   0,   0,   0,   3,   0,   1,   0,
+     76,   0,   0,   0,   2,   0,   1,   0,
       1,   0,   0,   0,   1,   0,   0,   0,
       0,   0,   1,   0,   1,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     45,   0,   0,   0,  90,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     44,   0,   0,   0,   3,   0,   1,   0,
-     56,   0,   0,   0,   2,   0,   1,   0,
+     73,   0,   0,   0,  90,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     72,   0,   0,   0,   3,   0,   1,   0,
+     84,   0,   0,   0,   2,   0,   1,   0,
+      2,   0,   0,   0,   2,   0,   0,   0,
+      0,   0,   1,   0,   2,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     81,   0,   0,   0,  58,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     76,   0,   0,   0,   3,   0,   1,   0,
+     88,   0,   0,   0,   2,   0,   1,   0,
      98,  97, 115, 105,  99,   0,   0,   0,
      16,   0,   0,   0,   0,   0,   0,   0,
      63,  37,  44,  34,  99, 202, 145, 180,
@@ -565,19 +617,28 @@
       0,   0,   0,   0,   0,   0,   0,   0,
      16,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    115, 116,  97, 116, 105,  99,   0,   0,
+     16,   0,   0,   0,   0,   0,   0,   0,
+    186,  76,  38, 196,  62, 171, 142, 168,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     16,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0, }
 };
 ::capnp::word const* const bp_902c6065e1be824a = b_902c6065e1be824a.words;
 #if !CAPNP_LITE
 static const ::capnp::_::RawSchema* const d_902c6065e1be824a[] = {
+  &s_a88eab3ec4264cba,
   &s_b2d0c825aac8249c,
   &s_b491ca63222c253f,
 };
-static const uint16_t m_902c6065e1be824a[] = {0, 1};
-static const uint16_t i_902c6065e1be824a[] = {0, 1};
+static const uint16_t m_902c6065e1be824a[] = {0, 1, 2};
+static const uint16_t i_902c6065e1be824a[] = {0, 1, 2};
 const ::capnp::_::RawSchema s_902c6065e1be824a = {
-  0x902c6065e1be824a, b_902c6065e1be824a.words, 49, d_902c6065e1be824a, m_902c6065e1be824a,
-  2, 2, i_902c6065e1be824a, nullptr, nullptr, { &s_902c6065e1be824a, nullptr, nullptr, 0, 0, nullptr }
+  0x902c6065e1be824a, b_902c6065e1be824a.words, 64, d_902c6065e1be824a, m_902c6065e1be824a,
+  3, 3, i_902c6065e1be824a, nullptr, nullptr, { &s_902c6065e1be824a, nullptr, nullptr, 0, 0, nullptr }
 };
 #endif  // !CAPNP_LITE
 static const ::capnp::_::AlignedData<27> b_f50fb3b9c1bf75f4 = {
@@ -618,109 +679,119 @@
 };
 #endif  // !CAPNP_LITE
 CAPNP_DEFINE_ENUM(InputDomain_f50fb3b9c1bf75f4, f50fb3b9c1bf75f4);
-static const ::capnp::_::AlignedData<221> b_b83ac85463e6caa1 = {
+static const ::capnp::_::AlignedData<245> b_b83ac85463e6caa1 = {
   {   0,   0,   0,   0,   5,   0,   6,   0,
     161, 202, 230,  99,  84, 200,  58, 184,
      12,   0,   0,   0,   1,   0,   2,   0,
       6, 146, 153,  76, 196, 198, 177, 196,
-      8,   0,   7,   0,   0,   0,   0,   0,
+      9,   0,   7,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
      21,   0,   0,   0,   2,   1,   0,   0,
-     33,   0,   0,   0,   7,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     29,   0,   0,   0, 167,   2,   0,   0,
+     33,   0,   0,   0,  23,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     41,   0,   0,   0, 223,   2,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
     112, 105, 112, 101, 114,  46,  99,  97,
     112, 110, 112,  58,  69, 120, 116, 114,
      97,  99, 116, 111, 114,  83, 116,  97,
     116, 105,  99,  68,  97, 116,  97,   0,
-      0,   0,   0,   0,   1,   0,   1,   0,
-     48,   0,   0,   0,   3,   0,   4,   0,
+      4,   0,   0,   0,   1,   0,   1,   0,
+     28, 110, 231,  31,  55, 160, 201, 196,
+      1,   0,   0,   0,  58,   0,   0,   0,
+     83,  79,  80,  97, 105, 114,   0,   0,
+     52,   0,   0,   0,   3,   0,   4,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   1,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     65,   1,   0,   0,  34,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     60,   1,   0,   0,   3,   0,   1,   0,
-     72,   1,   0,   0,   2,   0,   1,   0,
+     93,   1,   0,   0,  34,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     88,   1,   0,   0,   3,   0,   1,   0,
+    100,   1,   0,   0,   2,   0,   1,   0,
       1,   0,   0,   0,   1,   0,   0,   0,
       0,   0,   1,   0,   1,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     69,   1,   0,   0,  50,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     64,   1,   0,   0,   3,   0,   1,   0,
-     76,   1,   0,   0,   2,   0,   1,   0,
+     97,   1,   0,   0,  50,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     92,   1,   0,   0,   3,   0,   1,   0,
+    104,   1,   0,   0,   2,   0,   1,   0,
       2,   0,   0,   0,   2,   0,   0,   0,
       0,   0,   1,   0,   2,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     73,   1,   0,   0,  50,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     68,   1,   0,   0,   3,   0,   1,   0,
-     80,   1,   0,   0,   2,   0,   1,   0,
+    101,   1,   0,   0,  50,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     96,   1,   0,   0,   3,   0,   1,   0,
+    108,   1,   0,   0,   2,   0,   1,   0,
       3,   0,   0,   0,   3,   0,   0,   0,
       0,   0,   1,   0,   3,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     77,   1,   0,   0,  58,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     72,   1,   0,   0,   3,   0,   1,   0,
-     84,   1,   0,   0,   2,   0,   1,   0,
+    105,   1,   0,   0,  58,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    100,   1,   0,   0,   3,   0,   1,   0,
+    112,   1,   0,   0,   2,   0,   1,   0,
       4,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   1,   0,   4,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     81,   1,   0,   0,  66,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     76,   1,   0,   0,   3,   0,   1,   0,
-     88,   1,   0,   0,   2,   0,   1,   0,
+    109,   1,   0,   0,  66,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    104,   1,   0,   0,   3,   0,   1,   0,
+    116,   1,   0,   0,   2,   0,   1,   0,
       5,   0,   0,   0,   4,   0,   0,   0,
       0,   0,   1,   0,   5,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-     85,   1,   0,   0,  74,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-     84,   1,   0,   0,   3,   0,   1,   0,
-    112,   1,   0,   0,   2,   0,   1,   0,
+    113,   1,   0,   0,  74,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    112,   1,   0,   0,   3,   0,   1,   0,
+    140,   1,   0,   0,   2,   0,   1,   0,
       6,   0,   0,   0,   1,   0,   0,   0,
       0,   0,   1,   0,   6,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    109,   1,   0,   0, 130,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    108,   1,   0,   0,   3,   0,   1,   0,
-    120,   1,   0,   0,   2,   0,   1,   0,
+    137,   1,   0,   0, 130,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    136,   1,   0,   0,   3,   0,   1,   0,
+    148,   1,   0,   0,   2,   0,   1,   0,
       7,   0,   0,   0,   2,   0,   0,   0,
       0,   0,   1,   0,   7,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    117,   1,   0,   0, 130,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    116,   1,   0,   0,   3,   0,   1,   0,
-    128,   1,   0,   0,   2,   0,   1,   0,
+    145,   1,   0,   0, 130,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    144,   1,   0,   0,   3,   0,   1,   0,
+    156,   1,   0,   0,   2,   0,   1,   0,
       8,   0,   0,   0,   5,   0,   0,   0,
       0,   0,   1,   0,   8,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    125,   1,   0,   0,  90,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    124,   1,   0,   0,   3,   0,   1,   0,
-    152,   1,   0,   0,   2,   0,   1,   0,
+    153,   1,   0,   0,  90,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    152,   1,   0,   0,   3,   0,   1,   0,
+    180,   1,   0,   0,   2,   0,   1,   0,
       9,   0,   0,   0,   6,   0,   0,   0,
       0,   0,   1,   0,   9,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    149,   1,   0,   0,  74,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    148,   1,   0,   0,   3,   0,   1,   0,
-    176,   1,   0,   0,   2,   0,   1,   0,
+    177,   1,   0,   0,  74,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    176,   1,   0,   0,   3,   0,   1,   0,
+    204,   1,   0,   0,   2,   0,   1,   0,
      10,   0,   0,   0,   6,   0,   0,   0,
       0,   0,   1,   0,  10,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    173,   1,   0,   0,  98,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    172,   1,   0,   0,   3,   0,   1,   0,
-    184,   1,   0,   0,   2,   0,   1,   0,
+    201,   1,   0,   0,  98,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    200,   1,   0,   0,   3,   0,   1,   0,
+    212,   1,   0,   0,   2,   0,   1,   0,
      11,   0,   0,   0,   7,   0,   0,   0,
       0,   0,   1,   0,  11,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
-    181,   1,   0,   0, 130,   0,   0,   0,
-      0,   0,   0,   0,   0,   0,   0,   0,
-    180,   1,   0,   0,   3,   0,   1,   0,
-    208,   1,   0,   0,   2,   0,   1,   0,
+    209,   1,   0,   0, 130,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    208,   1,   0,   0,   3,   0,   1,   0,
+    236,   1,   0,   0,   2,   0,   1,   0,
+     12,   0,   0,   0,   8,   0,   0,   0,
+      0,   0,   1,   0,  12,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    233,   1,   0,   0, 138,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    236,   1,   0,   0,   3,   0,   1,   0,
+      8,   2,   0,   0,   2,   0,   1,   0,
     107, 101, 121,   0,   0,   0,   0,   0,
      12,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
@@ -839,20 +910,98 @@
       0,   0,   0,   0,   0,   0,   0,   0,
      14,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    115, 116,  97, 116, 105,  99,  79, 117,
+    116, 112, 117, 116,  73, 110, 102, 111,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     14,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   3,   0,   1,   0,
+     16,   0,   0,   0,   0,   0,   0,   0,
+     28, 110, 231,  31,  55, 160, 201, 196,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     14,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
       0,   0,   0,   0,   0,   0,   0,   0, }
 };
 ::capnp::word const* const bp_b83ac85463e6caa1 = b_b83ac85463e6caa1.words;
 #if !CAPNP_LITE
 static const ::capnp::_::RawSchema* const d_b83ac85463e6caa1[] = {
   &s_b491ca63222c253f,
+  &s_c4c9a0371fe76e1c,
   &s_f50fb3b9c1bf75f4,
   &s_f8fda10bef70a97d,
 };
-static const uint16_t m_b83ac85463e6caa1[] = {1, 11, 5, 10, 0, 2, 7, 6, 8, 9, 3, 4};
-static const uint16_t i_b83ac85463e6caa1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+static const uint16_t m_b83ac85463e6caa1[] = {1, 11, 5, 10, 0, 2, 7, 6, 8, 9, 3, 12, 4};
+static const uint16_t i_b83ac85463e6caa1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
 const ::capnp::_::RawSchema s_b83ac85463e6caa1 = {
-  0xb83ac85463e6caa1, b_b83ac85463e6caa1.words, 221, d_b83ac85463e6caa1, m_b83ac85463e6caa1,
-  3, 12, i_b83ac85463e6caa1, nullptr, nullptr, { &s_b83ac85463e6caa1, nullptr, nullptr, 0, 0, nullptr }
+  0xb83ac85463e6caa1, b_b83ac85463e6caa1.words, 245, d_b83ac85463e6caa1, m_b83ac85463e6caa1,
+  4, 13, i_b83ac85463e6caa1, nullptr, nullptr, { &s_b83ac85463e6caa1, nullptr, nullptr, 0, 0, nullptr }
+};
+#endif  // !CAPNP_LITE
+static const ::capnp::_::AlignedData<49> b_c4c9a0371fe76e1c = {
+  {   0,   0,   0,   0,   5,   0,   6,   0,
+     28, 110, 231,  31,  55, 160, 201, 196,
+     32,   0,   0,   0,   1,   0,   0,   0,
+    161, 202, 230,  99,  84, 200,  58, 184,
+      2,   0,   7,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     21,   0,   0,   0,  58,   1,   0,   0,
+     37,   0,   0,   0,   7,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     33,   0,   0,   0, 119,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    112, 105, 112, 101, 114,  46,  99,  97,
+    112, 110, 112,  58,  69, 120, 116, 114,
+     97,  99, 116, 111, 114,  83, 116,  97,
+    116, 105,  99,  68,  97, 116,  97,  46,
+     83,  79,  80,  97, 105, 114,   0,   0,
+      0,   0,   0,   0,   1,   0,   1,   0,
+      8,   0,   0,   0,   3,   0,   4,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   1,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     41,   0,   0,   0,  58,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     36,   0,   0,   0,   3,   0,   1,   0,
+     48,   0,   0,   0,   2,   0,   1,   0,
+      1,   0,   0,   0,   1,   0,   0,   0,
+      0,   0,   1,   0,   1,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     45,   0,   0,   0,  58,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     40,   0,   0,   0,   3,   0,   1,   0,
+     52,   0,   0,   0,   2,   0,   1,   0,
+    111, 117, 116, 112, 117, 116,   0,   0,
+     12,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     12,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+    115, 116,  97, 116, 105,  99,   0,   0,
+     16,   0,   0,   0,   0,   0,   0,   0,
+    186,  76,  38, 196,  62, 171, 142, 168,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+     16,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0,
+      0,   0,   0,   0,   0,   0,   0,   0, }
+};
+::capnp::word const* const bp_c4c9a0371fe76e1c = b_c4c9a0371fe76e1c.words;
+#if !CAPNP_LITE
+static const ::capnp::_::RawSchema* const d_c4c9a0371fe76e1c[] = {
+  &s_a88eab3ec4264cba,
+};
+static const uint16_t m_c4c9a0371fe76e1c[] = {0, 1};
+static const uint16_t i_c4c9a0371fe76e1c[] = {0, 1};
+const ::capnp::_::RawSchema s_c4c9a0371fe76e1c = {
+  0xc4c9a0371fe76e1c, b_c4c9a0371fe76e1c.words, 49, d_c4c9a0371fe76e1c, m_c4c9a0371fe76e1c,
+  1, 2, i_c4c9a0371fe76e1c, nullptr, nullptr, { &s_c4c9a0371fe76e1c, nullptr, nullptr, 0, 0, nullptr }
 };
 #endif  // !CAPNP_LITE
 static const ::capnp::_::AlignedData<47> b_84d515888a427d07 = {
@@ -2794,6 +2943,14 @@
 constexpr ::capnp::_::RawSchema const* ParameterDescriptor::_capnpPrivate::schema;
 #endif  // !CAPNP_LITE
 
+// StaticOutputDescriptor
+constexpr uint16_t StaticOutputDescriptor::_capnpPrivate::dataWordSize;
+constexpr uint16_t StaticOutputDescriptor::_capnpPrivate::pointerCount;
+#if !CAPNP_LITE
+constexpr ::capnp::Kind StaticOutputDescriptor::_capnpPrivate::kind;
+constexpr ::capnp::_::RawSchema const* StaticOutputDescriptor::_capnpPrivate::schema;
+#endif  // !CAPNP_LITE
+
 // ConfiguredOutputDescriptor
 constexpr uint16_t ConfiguredOutputDescriptor::_capnpPrivate::dataWordSize;
 constexpr uint16_t ConfiguredOutputDescriptor::_capnpPrivate::pointerCount;
@@ -2818,6 +2975,14 @@
 constexpr ::capnp::_::RawSchema const* ExtractorStaticData::_capnpPrivate::schema;
 #endif  // !CAPNP_LITE
 
+// ExtractorStaticData::SOPair
+constexpr uint16_t ExtractorStaticData::SOPair::_capnpPrivate::dataWordSize;
+constexpr uint16_t ExtractorStaticData::SOPair::_capnpPrivate::pointerCount;
+#if !CAPNP_LITE
+constexpr ::capnp::Kind ExtractorStaticData::SOPair::_capnpPrivate::kind;
+constexpr ::capnp::_::RawSchema const* ExtractorStaticData::SOPair::_capnpPrivate::schema;
+#endif  // !CAPNP_LITE
+
 // RealTime
 constexpr uint16_t RealTime::_capnpPrivate::dataWordSize;
 constexpr uint16_t RealTime::_capnpPrivate::pointerCount;
--- a/vamp-capnp/piper.capnp.h	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-capnp/piper.capnp.h	Thu Jun 08 16:17:03 2017 +0100
@@ -6,7 +6,7 @@
 
 #include <capnp/generated-header-support.h>
 
-#if CAPNP_VERSION != 6000
+#if CAPNP_VERSION != 7000
 #error "Version mismatch between generated code and library headers.  You must use the same version of the Cap'n Proto compiler and library."
 #endif
 
@@ -23,6 +23,7 @@
   VARIABLE_SAMPLE_RATE,
 };
 CAPNP_DECLARE_ENUM(SampleType, eca23e4a04bdfeb2);
+CAPNP_DECLARE_SCHEMA(a88eab3ec4264cba);
 CAPNP_DECLARE_SCHEMA(b2d0c825aac8249c);
 CAPNP_DECLARE_SCHEMA(902c6065e1be824a);
 CAPNP_DECLARE_SCHEMA(f50fb3b9c1bf75f4);
@@ -32,6 +33,7 @@
 };
 CAPNP_DECLARE_ENUM(InputDomain, f50fb3b9c1bf75f4);
 CAPNP_DECLARE_SCHEMA(b83ac85463e6caa1);
+CAPNP_DECLARE_SCHEMA(c4c9a0371fe76e1c);
 CAPNP_DECLARE_SCHEMA(84d515888a427d07);
 CAPNP_DECLARE_SCHEMA(c6f3f05f2bc614ba);
 CAPNP_DECLARE_SCHEMA(d6a172208c9a1760);
@@ -104,6 +106,21 @@
 
 typedef ::capnp::schemas::SampleType_eca23e4a04bdfeb2 SampleType;
 
+struct StaticOutputDescriptor {
+  StaticOutputDescriptor() = delete;
+
+  class Reader;
+  class Builder;
+  class Pipeline;
+
+  struct _capnpPrivate {
+    CAPNP_DECLARE_STRUCT_HEADER(a88eab3ec4264cba, 0, 1)
+    #if !CAPNP_LITE
+    static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
+    #endif  // !CAPNP_LITE
+  };
+};
+
 struct ConfiguredOutputDescriptor {
   ConfiguredOutputDescriptor() = delete;
 
@@ -127,7 +144,7 @@
   class Pipeline;
 
   struct _capnpPrivate {
-    CAPNP_DECLARE_STRUCT_HEADER(902c6065e1be824a, 0, 2)
+    CAPNP_DECLARE_STRUCT_HEADER(902c6065e1be824a, 0, 3)
     #if !CAPNP_LITE
     static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
     #endif  // !CAPNP_LITE
@@ -142,9 +159,25 @@
   class Reader;
   class Builder;
   class Pipeline;
+  struct SOPair;
 
   struct _capnpPrivate {
-    CAPNP_DECLARE_STRUCT_HEADER(b83ac85463e6caa1, 2, 8)
+    CAPNP_DECLARE_STRUCT_HEADER(b83ac85463e6caa1, 2, 9)
+    #if !CAPNP_LITE
+    static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
+    #endif  // !CAPNP_LITE
+  };
+};
+
+struct ExtractorStaticData::SOPair {
+  SOPair() = delete;
+
+  class Reader;
+  class Builder;
+  class Pipeline;
+
+  struct _capnpPrivate {
+    CAPNP_DECLARE_STRUCT_HEADER(c4c9a0371fe76e1c, 0, 2)
     #if !CAPNP_LITE
     static constexpr ::capnp::_::RawBrandedSchema const* brand() { return &schema->defaultBrand; }
     #endif  // !CAPNP_LITE
@@ -792,6 +825,87 @@
 };
 #endif  // !CAPNP_LITE
 
+class StaticOutputDescriptor::Reader {
+public:
+  typedef StaticOutputDescriptor Reads;
+
+  Reader() = default;
+  inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
+
+  inline ::capnp::MessageSize totalSize() const {
+    return _reader.totalSize().asPublic();
+  }
+
+#if !CAPNP_LITE
+  inline ::kj::StringTree toString() const {
+    return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
+  }
+#endif  // !CAPNP_LITE
+
+  inline bool hasTypeURI() const;
+  inline  ::capnp::Text::Reader getTypeURI() const;
+
+private:
+  ::capnp::_::StructReader _reader;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::_::PointerHelpers;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::List;
+  friend class ::capnp::MessageBuilder;
+  friend class ::capnp::Orphanage;
+};
+
+class StaticOutputDescriptor::Builder {
+public:
+  typedef StaticOutputDescriptor Builds;
+
+  Builder() = delete;  // Deleted to discourage incorrect usage.
+                       // You can explicitly initialize to nullptr instead.
+  inline Builder(decltype(nullptr)) {}
+  inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
+  inline operator Reader() const { return Reader(_builder.asReader()); }
+  inline Reader asReader() const { return *this; }
+
+  inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
+#if !CAPNP_LITE
+  inline ::kj::StringTree toString() const { return asReader().toString(); }
+#endif  // !CAPNP_LITE
+
+  inline bool hasTypeURI();
+  inline  ::capnp::Text::Builder getTypeURI();
+  inline void setTypeURI( ::capnp::Text::Reader value);
+  inline  ::capnp::Text::Builder initTypeURI(unsigned int size);
+  inline void adoptTypeURI(::capnp::Orphan< ::capnp::Text>&& value);
+  inline ::capnp::Orphan< ::capnp::Text> disownTypeURI();
+
+private:
+  ::capnp::_::StructBuilder _builder;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+  friend class ::capnp::Orphanage;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::_::PointerHelpers;
+};
+
+#if !CAPNP_LITE
+class StaticOutputDescriptor::Pipeline {
+public:
+  typedef StaticOutputDescriptor Pipelines;
+
+  inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
+  inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
+      : _typeless(kj::mv(typeless)) {}
+
+private:
+  ::capnp::AnyPointer::Pipeline _typeless;
+  friend class ::capnp::PipelineHook;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+};
+#endif  // !CAPNP_LITE
+
 class ConfiguredOutputDescriptor::Reader {
 public:
   typedef ConfiguredOutputDescriptor Reads;
@@ -957,6 +1071,9 @@
   inline bool hasConfigured() const;
   inline  ::piper::ConfiguredOutputDescriptor::Reader getConfigured() const;
 
+  inline bool hasStatic() const;
+  inline  ::piper::StaticOutputDescriptor::Reader getStatic() const;
+
 private:
   ::capnp::_::StructReader _reader;
   template <typename, ::capnp::Kind>
@@ -999,6 +1116,13 @@
   inline void adoptConfigured(::capnp::Orphan< ::piper::ConfiguredOutputDescriptor>&& value);
   inline ::capnp::Orphan< ::piper::ConfiguredOutputDescriptor> disownConfigured();
 
+  inline bool hasStatic();
+  inline  ::piper::StaticOutputDescriptor::Builder getStatic();
+  inline void setStatic( ::piper::StaticOutputDescriptor::Reader value);
+  inline  ::piper::StaticOutputDescriptor::Builder initStatic();
+  inline void adoptStatic(::capnp::Orphan< ::piper::StaticOutputDescriptor>&& value);
+  inline ::capnp::Orphan< ::piper::StaticOutputDescriptor> disownStatic();
+
 private:
   ::capnp::_::StructBuilder _builder;
   template <typename, ::capnp::Kind>
@@ -1019,6 +1143,7 @@
 
   inline  ::piper::Basic::Pipeline getBasic();
   inline  ::piper::ConfiguredOutputDescriptor::Pipeline getConfigured();
+  inline  ::piper::StaticOutputDescriptor::Pipeline getStatic();
 private:
   ::capnp::AnyPointer::Pipeline _typeless;
   friend class ::capnp::PipelineHook;
@@ -1076,6 +1201,9 @@
   inline bool hasBasicOutputInfo() const;
   inline  ::capnp::List< ::piper::Basic>::Reader getBasicOutputInfo() const;
 
+  inline bool hasStaticOutputInfo() const;
+  inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Reader getStaticOutputInfo() const;
+
 private:
   ::capnp::_::StructReader _reader;
   template <typename, ::capnp::Kind>
@@ -1174,6 +1302,13 @@
   inline void adoptBasicOutputInfo(::capnp::Orphan< ::capnp::List< ::piper::Basic>>&& value);
   inline ::capnp::Orphan< ::capnp::List< ::piper::Basic>> disownBasicOutputInfo();
 
+  inline bool hasStaticOutputInfo();
+  inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Builder getStaticOutputInfo();
+  inline void setStaticOutputInfo( ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Reader value);
+  inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Builder initStaticOutputInfo(unsigned int size);
+  inline void adoptStaticOutputInfo(::capnp::Orphan< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>&& value);
+  inline ::capnp::Orphan< ::capnp::List< ::piper::ExtractorStaticData::SOPair>> disownStaticOutputInfo();
+
 private:
   ::capnp::_::StructBuilder _builder;
   template <typename, ::capnp::Kind>
@@ -1201,6 +1336,98 @@
 };
 #endif  // !CAPNP_LITE
 
+class ExtractorStaticData::SOPair::Reader {
+public:
+  typedef SOPair Reads;
+
+  Reader() = default;
+  inline explicit Reader(::capnp::_::StructReader base): _reader(base) {}
+
+  inline ::capnp::MessageSize totalSize() const {
+    return _reader.totalSize().asPublic();
+  }
+
+#if !CAPNP_LITE
+  inline ::kj::StringTree toString() const {
+    return ::capnp::_::structString(_reader, *_capnpPrivate::brand());
+  }
+#endif  // !CAPNP_LITE
+
+  inline bool hasOutput() const;
+  inline  ::capnp::Text::Reader getOutput() const;
+
+  inline bool hasStatic() const;
+  inline  ::piper::StaticOutputDescriptor::Reader getStatic() const;
+
+private:
+  ::capnp::_::StructReader _reader;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::_::PointerHelpers;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::List;
+  friend class ::capnp::MessageBuilder;
+  friend class ::capnp::Orphanage;
+};
+
+class ExtractorStaticData::SOPair::Builder {
+public:
+  typedef SOPair Builds;
+
+  Builder() = delete;  // Deleted to discourage incorrect usage.
+                       // You can explicitly initialize to nullptr instead.
+  inline Builder(decltype(nullptr)) {}
+  inline explicit Builder(::capnp::_::StructBuilder base): _builder(base) {}
+  inline operator Reader() const { return Reader(_builder.asReader()); }
+  inline Reader asReader() const { return *this; }
+
+  inline ::capnp::MessageSize totalSize() const { return asReader().totalSize(); }
+#if !CAPNP_LITE
+  inline ::kj::StringTree toString() const { return asReader().toString(); }
+#endif  // !CAPNP_LITE
+
+  inline bool hasOutput();
+  inline  ::capnp::Text::Builder getOutput();
+  inline void setOutput( ::capnp::Text::Reader value);
+  inline  ::capnp::Text::Builder initOutput(unsigned int size);
+  inline void adoptOutput(::capnp::Orphan< ::capnp::Text>&& value);
+  inline ::capnp::Orphan< ::capnp::Text> disownOutput();
+
+  inline bool hasStatic();
+  inline  ::piper::StaticOutputDescriptor::Builder getStatic();
+  inline void setStatic( ::piper::StaticOutputDescriptor::Reader value);
+  inline  ::piper::StaticOutputDescriptor::Builder initStatic();
+  inline void adoptStatic(::capnp::Orphan< ::piper::StaticOutputDescriptor>&& value);
+  inline ::capnp::Orphan< ::piper::StaticOutputDescriptor> disownStatic();
+
+private:
+  ::capnp::_::StructBuilder _builder;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+  friend class ::capnp::Orphanage;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::_::PointerHelpers;
+};
+
+#if !CAPNP_LITE
+class ExtractorStaticData::SOPair::Pipeline {
+public:
+  typedef SOPair Pipelines;
+
+  inline Pipeline(decltype(nullptr)): _typeless(nullptr) {}
+  inline explicit Pipeline(::capnp::AnyPointer::Pipeline&& typeless)
+      : _typeless(kj::mv(typeless)) {}
+
+  inline  ::piper::StaticOutputDescriptor::Pipeline getStatic();
+private:
+  ::capnp::AnyPointer::Pipeline _typeless;
+  friend class ::capnp::PipelineHook;
+  template <typename, ::capnp::Kind>
+  friend struct ::capnp::ToDynamic_;
+};
+#endif  // !CAPNP_LITE
+
 class RealTime::Reader {
 public:
   typedef RealTime Reads;
@@ -3839,6 +4066,40 @@
       ::capnp::bounded<2>() * ::capnp::POINTERS));
 }
 
+inline bool StaticOutputDescriptor::Reader::hasTypeURI() const {
+  return !_reader.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
+}
+inline bool StaticOutputDescriptor::Builder::hasTypeURI() {
+  return !_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
+}
+inline  ::capnp::Text::Reader StaticOutputDescriptor::Reader::getTypeURI() const {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+inline  ::capnp::Text::Builder StaticOutputDescriptor::Builder::getTypeURI() {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+inline void StaticOutputDescriptor::Builder::setTypeURI( ::capnp::Text::Reader value) {
+  ::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), value);
+}
+inline  ::capnp::Text::Builder StaticOutputDescriptor::Builder::initTypeURI(unsigned int size) {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), size);
+}
+inline void StaticOutputDescriptor::Builder::adoptTypeURI(
+    ::capnp::Orphan< ::capnp::Text>&& value) {
+  ::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
+}
+inline ::capnp::Orphan< ::capnp::Text> StaticOutputDescriptor::Builder::disownTypeURI() {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+
 inline bool ConfiguredOutputDescriptor::Reader::hasUnit() const {
   return !_reader.getPointerField(
       ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
@@ -4131,6 +4392,45 @@
       ::capnp::bounded<1>() * ::capnp::POINTERS));
 }
 
+inline bool OutputDescriptor::Reader::hasStatic() const {
+  return !_reader.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS).isNull();
+}
+inline bool OutputDescriptor::Builder::hasStatic() {
+  return !_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS).isNull();
+}
+inline  ::piper::StaticOutputDescriptor::Reader OutputDescriptor::Reader::getStatic() const {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::get(_reader.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS));
+}
+inline  ::piper::StaticOutputDescriptor::Builder OutputDescriptor::Builder::getStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::get(_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS));
+}
+#if !CAPNP_LITE
+inline  ::piper::StaticOutputDescriptor::Pipeline OutputDescriptor::Pipeline::getStatic() {
+  return  ::piper::StaticOutputDescriptor::Pipeline(_typeless.getPointerField(2));
+}
+#endif  // !CAPNP_LITE
+inline void OutputDescriptor::Builder::setStatic( ::piper::StaticOutputDescriptor::Reader value) {
+  ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::set(_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS), value);
+}
+inline  ::piper::StaticOutputDescriptor::Builder OutputDescriptor::Builder::initStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::init(_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS));
+}
+inline void OutputDescriptor::Builder::adoptStatic(
+    ::capnp::Orphan< ::piper::StaticOutputDescriptor>&& value) {
+  ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::adopt(_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS), kj::mv(value));
+}
+inline ::capnp::Orphan< ::piper::StaticOutputDescriptor> OutputDescriptor::Builder::disownStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::disown(_builder.getPointerField(
+      ::capnp::bounded<2>() * ::capnp::POINTERS));
+}
+
 inline bool ExtractorStaticData::Reader::hasKey() const {
   return !_reader.getPointerField(
       ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
@@ -4472,6 +4772,113 @@
       ::capnp::bounded<7>() * ::capnp::POINTERS));
 }
 
+inline bool ExtractorStaticData::Reader::hasStaticOutputInfo() const {
+  return !_reader.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS).isNull();
+}
+inline bool ExtractorStaticData::Builder::hasStaticOutputInfo() {
+  return !_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS).isNull();
+}
+inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Reader ExtractorStaticData::Reader::getStaticOutputInfo() const {
+  return ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::get(_reader.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS));
+}
+inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Builder ExtractorStaticData::Builder::getStaticOutputInfo() {
+  return ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::get(_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS));
+}
+inline void ExtractorStaticData::Builder::setStaticOutputInfo( ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Reader value) {
+  ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::set(_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS), value);
+}
+inline  ::capnp::List< ::piper::ExtractorStaticData::SOPair>::Builder ExtractorStaticData::Builder::initStaticOutputInfo(unsigned int size) {
+  return ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::init(_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS), size);
+}
+inline void ExtractorStaticData::Builder::adoptStaticOutputInfo(
+    ::capnp::Orphan< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>&& value) {
+  ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::adopt(_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS), kj::mv(value));
+}
+inline ::capnp::Orphan< ::capnp::List< ::piper::ExtractorStaticData::SOPair>> ExtractorStaticData::Builder::disownStaticOutputInfo() {
+  return ::capnp::_::PointerHelpers< ::capnp::List< ::piper::ExtractorStaticData::SOPair>>::disown(_builder.getPointerField(
+      ::capnp::bounded<8>() * ::capnp::POINTERS));
+}
+
+inline bool ExtractorStaticData::SOPair::Reader::hasOutput() const {
+  return !_reader.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
+}
+inline bool ExtractorStaticData::SOPair::Builder::hasOutput() {
+  return !_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS).isNull();
+}
+inline  ::capnp::Text::Reader ExtractorStaticData::SOPair::Reader::getOutput() const {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_reader.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+inline  ::capnp::Text::Builder ExtractorStaticData::SOPair::Builder::getOutput() {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::get(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+inline void ExtractorStaticData::SOPair::Builder::setOutput( ::capnp::Text::Reader value) {
+  ::capnp::_::PointerHelpers< ::capnp::Text>::set(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), value);
+}
+inline  ::capnp::Text::Builder ExtractorStaticData::SOPair::Builder::initOutput(unsigned int size) {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::init(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), size);
+}
+inline void ExtractorStaticData::SOPair::Builder::adoptOutput(
+    ::capnp::Orphan< ::capnp::Text>&& value) {
+  ::capnp::_::PointerHelpers< ::capnp::Text>::adopt(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS), kj::mv(value));
+}
+inline ::capnp::Orphan< ::capnp::Text> ExtractorStaticData::SOPair::Builder::disownOutput() {
+  return ::capnp::_::PointerHelpers< ::capnp::Text>::disown(_builder.getPointerField(
+      ::capnp::bounded<0>() * ::capnp::POINTERS));
+}
+
+inline bool ExtractorStaticData::SOPair::Reader::hasStatic() const {
+  return !_reader.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
+}
+inline bool ExtractorStaticData::SOPair::Builder::hasStatic() {
+  return !_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS).isNull();
+}
+inline  ::piper::StaticOutputDescriptor::Reader ExtractorStaticData::SOPair::Reader::getStatic() const {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::get(_reader.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS));
+}
+inline  ::piper::StaticOutputDescriptor::Builder ExtractorStaticData::SOPair::Builder::getStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::get(_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS));
+}
+#if !CAPNP_LITE
+inline  ::piper::StaticOutputDescriptor::Pipeline ExtractorStaticData::SOPair::Pipeline::getStatic() {
+  return  ::piper::StaticOutputDescriptor::Pipeline(_typeless.getPointerField(1));
+}
+#endif  // !CAPNP_LITE
+inline void ExtractorStaticData::SOPair::Builder::setStatic( ::piper::StaticOutputDescriptor::Reader value) {
+  ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::set(_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS), value);
+}
+inline  ::piper::StaticOutputDescriptor::Builder ExtractorStaticData::SOPair::Builder::initStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::init(_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS));
+}
+inline void ExtractorStaticData::SOPair::Builder::adoptStatic(
+    ::capnp::Orphan< ::piper::StaticOutputDescriptor>&& value) {
+  ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::adopt(_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS), kj::mv(value));
+}
+inline ::capnp::Orphan< ::piper::StaticOutputDescriptor> ExtractorStaticData::SOPair::Builder::disownStatic() {
+  return ::capnp::_::PointerHelpers< ::piper::StaticOutputDescriptor>::disown(_builder.getPointerField(
+      ::capnp::bounded<1>() * ::capnp::POINTERS));
+}
+
 inline  ::int32_t RealTime::Reader::getSec() const {
   return _reader.getDataField< ::int32_t>(
       ::capnp::bounded<0>() * ::capnp::ELEMENTS);
--- a/vamp-json/VampJson.h	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-json/VampJson.h	Thu Jun 08 16:17:03 2017 +0100
@@ -4,7 +4,7 @@
     Piper C++
 
     Centre for Digital Music, Queen Mary, University of London.
-    Copyright 2015-2016 QMUL.
+    Copyright 2015-2017 QMUL.
   
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation
@@ -47,6 +47,7 @@
 #include <vamp-hostsdk/Plugin.h>
 #include <vamp-hostsdk/PluginLoader.h>
 
+#include "vamp-support/StaticOutputDescriptor.h"
 #include "vamp-support/PluginStaticData.h"
 #include "vamp-support/PluginConfiguration.h"
 #include "vamp-support/RequestResponse.h"
@@ -216,6 +217,15 @@
     }
 
     static json11::Json
+    fromStaticOutputDescriptor(const StaticOutputDescriptor &sd) {
+        json11::Json::object jo;
+        if (sd.typeURI != "") {
+            jo["typeURI"] = sd.typeURI;
+        }
+        return json11::Json(jo);
+    }
+    
+    static json11::Json
     fromConfiguredOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
         json11::Json::object jo {
             { "unit", desc.unit },
@@ -238,13 +248,28 @@
     }
     
     static json11::Json
-    fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
+    fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc,
+                         const StaticOutputDescriptor &sd) {
         json11::Json::object jo {
             { "basic", fromBasicDescriptor(desc) },
+            { "static", fromStaticOutputDescriptor(sd) },
             { "configured", fromConfiguredOutputDescriptor(desc) }
         };
         return json11::Json(jo);
     }
+
+    static StaticOutputDescriptor
+    toStaticOutputDescriptor(json11::Json j, std::string &err) {
+
+        StaticOutputDescriptor sd;
+        if (!j.is_object()) {
+            err = "object expected for static output descriptor";
+            return {};
+        }
+
+        sd.typeURI = j["typeURI"].string_value();
+        return sd;
+    }
     
     static Vamp::Plugin::OutputDescriptor
     toConfiguredOutputDescriptor(json11::Json j, std::string &err) {
@@ -296,7 +321,7 @@
         return od;
     }
     
-    static Vamp::Plugin::OutputDescriptor
+    static std::pair<Vamp::Plugin::OutputDescriptor, StaticOutputDescriptor>
     toOutputDescriptor(json11::Json j, std::string &err) {
 
         Vamp::Plugin::OutputDescriptor od;
@@ -311,7 +336,13 @@
         toBasicDescriptor(j["basic"], od, err);
         if (failed(err)) return {};
 
-        return od;
+        StaticOutputDescriptor sd;
+        if (j["static"] != json11::Json()) {
+            sd = toStaticOutputDescriptor(j["static"], err);
+            if (failed(err)) return {};
+        }
+
+        return { od, sd };
     }
 
     static json11::Json
@@ -569,7 +600,11 @@
         auto vouts = d.basicOutputInfo;
         for (auto &o: vouts) outinfo.push_back(fromBasicDescriptor(o));
         jo["basicOutputInfo"] = outinfo;
-    
+
+        json11::Json::object statinfo;
+        auto souts = d.staticOutputInfo;
+        for (auto &s: souts) jo[s.first] = fromStaticOutputDescriptor(s.second);
+        
         return json11::Json(jo);
     }
 
@@ -968,7 +1003,12 @@
         
         json11::Json::array outs;
         for (auto &d: cr.outputs) {
-            outs.push_back(fromOutputDescriptor(d));
+            auto id = d.identifier;
+            StaticOutputDescriptor sd;
+            if (cr.staticOutputInfo.find(id) != cr.staticOutputInfo.end()) {
+                sd = cr.staticOutputInfo.at(id);
+            }
+            outs.push_back(fromOutputDescriptor(d, sd));
         }
         jo["outputList"] = outs;
 
@@ -1001,8 +1041,10 @@
         cr.plugin = pmapper.handleToPlugin(j["handle"].int_value());
 
         for (const auto &o: j["outputList"].array_items()) {
-            cr.outputs.push_back(toOutputDescriptor(o, err));
+            auto odpair = toOutputDescriptor(o, err);
             if (failed(err)) return {};
+            cr.outputs.push_back(odpair.first);
+            cr.staticOutputInfo[odpair.first.identifier] = odpair.second;
         }
 
         cr.framing.stepSize = int(round(j["framing"]["stepSize"].number_value()));
--- a/vamp-server/test.sh	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-server/test.sh	Thu Jun 08 16:17:03 2017 +0100
@@ -30,6 +30,12 @@
 expected_less_strict="$tmpdir/expected-less-strict"
 obtained="$tmpdir/obtained"
 
+fail() {
+    local msg="$1"
+    echo " !! $msg!" 1>&2
+    exit 1
+}
+
 validate() {
     local file="$1"
     local schemaname="$2"
@@ -37,7 +43,7 @@
 	echo " * validating against schema $schemaname... " 1>&2
         jsonschema -i "$file" "$schemadir/$schemaname.json" 1>&2 && \
             echo " -> validated against schema $schemaname" 1>&2 || \
-            echo " !! failed to validate $schemaname!" 1>&2
+            fail "failed to validate $schemaname"
     else
         echo "(schema directory $schemadir not found, skipping validation)" 1>&2
     fi
@@ -76,7 +82,7 @@
 cat > "$expected" <<EOF
 {"id": 6, "jsonrpc": "2.0", "method": "load", "result": {"defaultConfiguration": {"channelCount": 1, "framing": {"blockSize": 1024, "stepSize": 1024}, "parameterValues": {"sensitivity": 40, "threshold": 3}}, "handle": 1, "staticData": {"basic": {"description": "Detect percussive note onsets by identifying broadband energy rises", "identifier": "percussiononsets", "name": "Simple Percussion Onset Detector"}, "basicOutputInfo": [{"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}], "category": ["Time", "Onsets"], "inputDomain": "TimeDomain", "key": "vamp-example-plugins:percussiononsets", "maker": "Vamp SDK Example Plugins", "maxChannelCount": 1, "minChannelCount": 1, "parameters": [{"basic": {"description": "Energy rise within a frequency bin necessary to count toward broadband total", "identifier": "threshold", "name": "Energy rise threshold"}, "defaultValue": 3, "extents": {"max": 20, "min": 0}, "unit": "dB", "valueNames": []}, {"basic": {"description": "Sensitivity of peak detector applied to broadband detection function", "identifier": "sensitivity", "name": "Sensitivity"}, "defaultValue": 40, "extents": {"max": 100, "min": 0}, "unit": "%", "valueNames": []}], "programs": [], "rights": "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005.  Freely redistributable (BSD license)", "version": 2}}}
 {"error": {"code": 0, "message": "error in process request: plugin has not been configured"}, "jsonrpc": "2.0", "method": "process"}
-{"id": "weevil", "jsonrpc": "2.0", "method": "configure", "result": {"framing": {"blockSize": 8, "stepSize": 8}, "handle": 1, "outputList": [{"basic": {"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, "configured": {"binCount": 0, "binNames": [], "hasDuration": false, "sampleRate": 44100, "sampleType": "VariableSampleRate", "unit": ""}}, {"basic": {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}, "configured": {"binCount": 1, "binNames": [""], "hasDuration": false, "quantizeStep": 1, "sampleRate": 86.1328125, "sampleType": "FixedSampleRate", "unit": ""}}]}}
+{"id": "weevil", "jsonrpc": "2.0", "method": "configure", "result": {"framing": {"blockSize": 8, "stepSize": 8}, "handle": 1, "outputList": [{"basic": {"description": "Percussive note onset locations", "identifier": "onsets", "name": "Onsets"}, "configured": {"binCount": 0, "binNames": [], "hasDuration": false, "sampleRate": 44100, "sampleType": "VariableSampleRate", "unit": ""}, "static": {}}, {"basic": {"description": "Broadband energy rise detection function", "identifier": "detectionfunction", "name": "Detection Function"}, "configured": {"binCount": 1, "binNames": [""], "hasDuration": false, "quantizeStep": 1, "sampleRate": 86.1328125, "sampleType": "FixedSampleRate", "unit": ""}, "static": {}}]}}
 {"error": {"code": 0, "message": "error in configure request: unknown plugin handle supplied to configure"}, "id": 9, "jsonrpc": "2.0", "method": "configure"}
 {"jsonrpc": "2.0", "method": "process", "result": {"features": {}, "handle": 1}}
 {"jsonrpc": "2.0", "method": "finish", "result": {"features": {"detectionfunction": [{"featureValues": [0], "timestamp": {"n": 11609977, "s": 0}}]}, "handle": 1}}
--- a/vamp-support/PluginStaticData.h	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-support/PluginStaticData.h	Thu Jun 08 16:17:03 2017 +0100
@@ -4,7 +4,7 @@
     Piper C++
 
     Centre for Digital Music, Queen Mary, University of London.
-    Copyright 2006-2016 Chris Cannam and QMUL.
+    Copyright 2006-2017 Chris Cannam and QMUL.
   
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation
@@ -37,6 +37,8 @@
 
 #include <vamp-hostsdk/Plugin.h>
 
+#include "StaticOutputDescriptor.h"
+
 namespace piper_vamp {
 
 /**
@@ -62,7 +64,7 @@
 	std::string description;
     };
     typedef std::vector<Basic> BasicList;
-
+    
     PluginStaticData() : // invalid static data by default
 	pluginVersion(0), minChannelCount(0), maxChannelCount(0),
 	inputDomain(Vamp::Plugin::TimeDomain) { }
@@ -72,13 +74,18 @@
     std::string maker;
     std::string copyright;
     int pluginVersion;
-    std::vector<std::string> category;
+    std::vector<std::string> category; // not found in the plugin, may
+                                       // come from accompanying
+                                       // metadata
     size_t minChannelCount;
     size_t maxChannelCount;
     Vamp::PluginBase::ParameterList parameters;
     Vamp::PluginBase::ProgramList programs;
     Vamp::Plugin::InputDomain inputDomain;
     BasicList basicOutputInfo;
+    StaticOutputInfo staticOutputInfo; // not found in the plugin, may
+                                       // come from accompanying
+                                       // (RDF?) metadata
 
     static PluginStaticData
     fromPlugin(std::string pluginKey,
--- a/vamp-support/RequestResponse.h	Mon May 22 08:57:02 2017 +0100
+++ b/vamp-support/RequestResponse.h	Thu Jun 08 16:17:03 2017 +0100
@@ -203,6 +203,8 @@
 
     Vamp::Plugin *plugin;
     Vamp::Plugin::OutputList outputs;
+    StaticOutputInfo staticOutputInfo; // stuff not in Plugin::OutputDescriptor
+    
     Framing framing;
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-support/StaticOutputDescriptor.h	Thu Jun 08 16:17:03 2017 +0100
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Piper C++
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006-2017 Chris Cannam and QMUL.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef PIPER_STATIC_OUTPUT_DESCRIPTOR_H
+#define PIPER_STATIC_OUTPUT_DESCRIPTOR_H
+
+#include <string>
+#include <map>
+
+namespace piper_vamp {
+
+struct StaticOutputDescriptor
+{
+    std::string typeURI;
+};
+
+typedef std::map<std::string, StaticOutputDescriptor> StaticOutputInfo;
+
+}
+
+#endif