changeset 5:6e8607ebad03

Promote the more successful experiments (todo: get them to build again)
author Chris Cannam <c.cannam@qmul.ac.uk>
date Fri, 13 May 2016 13:48:59 +0100
parents 25499f505d0e
children d8358afe3f2c
files capnproto/VampnProto.h capnproto/schemamap.csv capnproto/vamp.capnp json/VampJson.h json/base-n/LICENSE json/base-n/README.md json/base-n/example/basen_example.cpp json/base-n/include/basen.hpp json/json-cli.cpp json/json-test.cpp json/json11/CMakeLists.txt json/json11/LICENSE.txt json/json11/Makefile json/json11/README.md json/json11/json11.cpp json/json11/json11.hpp json/json11/test.cpp json/test-cli.sh utilities/json-to-capnp.cpp
diffstat 19 files changed, 3495 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/capnproto/VampnProto.h	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,475 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "vamp.capnp.h"
+
+#include <capnp/message.h>
+#include <capnp/serialize-packed.h>
+
+#include <vamp-hostsdk/Plugin.h>
+#include <vamp-hostsdk/PluginLoader.h>
+#include <vamp-hostsdk/PluginStaticData.h>
+
+namespace vampipe
+{
+
+/**
+ * Convert the structures laid out in the Vamp SDK classes into Cap'n
+ * Proto structures.
+ * 
+ * At least some of this will be necessary for any implementation that
+ * is using the C++ Vamp SDK to provide its reference structures. An
+ * implementation could alternatively use the Cap'n Proto structures
+ * directly, and interact with Vamp plugins using the Vamp C API,
+ * without using the C++ Vamp SDK classes at all.
+ */
+
+class VampSDKConverter
+{
+public:
+    typedef ::capnp::MallocMessageBuilder MsgBuilder;
+
+    template <typename T, typename B>
+    static void buildBasicDescriptor(B &basic, const T &t) {
+        basic.setIdentifier(t.identifier);
+        basic.setName(t.name);
+        basic.setDescription(t.description);
+    }
+
+    template <typename T, typename B>
+    static void readBasicDescriptor(T &t, const B &basic) {
+        t.identifier = basic.getIdentifier();
+        t.name = basic.getName();
+        t.description = basic.getDescription();
+    }
+
+    template <typename T, typename M>
+    static void buildValueExtents(M &m, const T &t) {
+        m.setMinValue(t.minValue);
+        m.setMaxValue(t.maxValue);
+    }
+
+    template <typename T, typename M>
+    static void readValueExtents(T &t, const M &m) {
+        t.minValue = m.getMinValue();
+        t.maxValue = m.getMaxValue();
+    }
+
+    static void buildRealTime(RealTime::Builder &b, const Vamp::RealTime &t) {
+        b.setSec(t.sec);
+        b.setNsec(t.nsec);
+    }
+
+    static void readRealTime(Vamp::RealTime &t, const RealTime::Reader &r) {
+        t.sec = r.getSec();
+        t.nsec = r.getNsec();
+    }
+
+    static SampleType
+    fromSampleType(Vamp::Plugin::OutputDescriptor::SampleType t) {
+        switch (t) {
+        case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
+            return SampleType::ONE_SAMPLE_PER_STEP;
+        case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
+            return SampleType::FIXED_SAMPLE_RATE;
+        case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
+            return SampleType::VARIABLE_SAMPLE_RATE;
+        }
+        throw std::logic_error("unexpected Vamp SampleType enum value");
+    }
+
+    static Vamp::Plugin::OutputDescriptor::SampleType
+    toSampleType(SampleType t) {
+        switch (t) {
+        case SampleType::ONE_SAMPLE_PER_STEP:
+            return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
+        case SampleType::FIXED_SAMPLE_RATE:
+            return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
+        case SampleType::VARIABLE_SAMPLE_RATE:
+            return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
+        }
+        throw std::logic_error("unexpected Capnp SampleType enum value");
+    }
+
+    static void
+    buildOutputDescriptor(OutputDescriptor::Builder &b,
+                          const Vamp::Plugin::OutputDescriptor &od) {
+
+        auto basic = b.initBasic();
+        buildBasicDescriptor(basic, od);
+
+        b.setUnit(od.unit);
+
+        b.setSampleType(fromSampleType(od.sampleType));
+        b.setSampleRate(od.sampleRate);
+        b.setHasDuration(od.hasDuration);
+
+        b.setHasFixedBinCount(od.hasFixedBinCount);
+        if (od.hasFixedBinCount) {
+            b.setBinCount(od.binCount);
+            if (od.binNames.size() > 0) {
+                auto binNames = b.initBinNames(od.binNames.size());
+                for (size_t i = 0; i < od.binNames.size(); ++i) {
+                    binNames.set(i, od.binNames[i]);
+                }
+            }
+        }
+
+        b.setHasKnownExtents(od.hasKnownExtents);
+        if (od.hasKnownExtents) {
+            buildValueExtents(b, od);
+        }
+
+        b.setIsQuantized(od.isQuantized);
+        if (od.isQuantized) {
+            b.setQuantizeStep(od.quantizeStep);
+        }
+    }
+
+    static void
+    readOutputDescriptor(Vamp::Plugin::OutputDescriptor &od,
+                         const OutputDescriptor::Reader &r) {
+
+        readBasicDescriptor(od, r.getBasic());
+
+        od.unit = r.getUnit();
+
+        od.sampleType = toSampleType(r.getSampleType());
+        od.sampleRate = r.getSampleRate();
+        od.hasDuration = r.getHasDuration();
+
+        od.hasFixedBinCount = r.getHasFixedBinCount();
+        if (od.hasFixedBinCount) {
+            od.binCount = r.getBinCount();
+            for (const auto &n: r.getBinNames()) {
+                od.binNames.push_back(n);
+            }
+        }
+
+        od.hasKnownExtents = r.getHasKnownExtents();
+        if (od.hasKnownExtents) {
+            readValueExtents(od, r);
+        }
+
+        od.isQuantized = r.getIsQuantized();
+        if (od.isQuantized) {
+            od.quantizeStep = r.getQuantizeStep();
+        }
+    }
+
+    static void
+    buildParameterDescriptor(ParameterDescriptor::Builder &b,
+                             const Vamp::Plugin::ParameterDescriptor &pd) {
+
+        auto basic = b.initBasic();
+        buildBasicDescriptor(basic, pd);
+
+        b.setUnit(pd.unit);
+
+        buildValueExtents(b, pd);
+
+        b.setDefaultValue(pd.defaultValue);
+
+        b.setIsQuantized(pd.isQuantized);
+        if (pd.isQuantized) {
+            b.setQuantizeStep(pd.quantizeStep);
+        }
+        
+        if (pd.valueNames.size() > 0) {
+            auto valueNames = b.initValueNames(pd.valueNames.size());
+            for (size_t i = 0; i < pd.valueNames.size(); ++i) {
+                valueNames.set(i, pd.valueNames[i]);
+            }
+        }
+    }
+
+    static void
+    readParameterDescriptor(Vamp::Plugin::ParameterDescriptor &pd,
+                            const ParameterDescriptor::Reader &r) {
+
+        readBasicDescriptor(pd, r.getBasic());
+
+        pd.unit = r.getUnit();
+
+        readValueExtents(pd, r);
+
+        pd.defaultValue = r.getDefaultValue();
+
+        pd.isQuantized = r.getIsQuantized();
+        if (pd.isQuantized) {
+            pd.quantizeStep = r.getQuantizeStep();
+        }
+
+        for (const auto &n: r.getValueNames()) {
+            pd.valueNames.push_back(n);
+        }
+    }
+    
+    static void
+    buildFeature(Feature::Builder &b,
+                 const Vamp::Plugin::Feature &f) {
+
+        b.setHasTimestamp(f.hasTimestamp);
+        if (f.hasTimestamp) {
+            auto timestamp = b.initTimestamp();
+            buildRealTime(timestamp, f.timestamp);
+        }
+
+        b.setHasDuration(f.hasDuration);
+        if (f.hasDuration) {
+            auto duration = b.initDuration();
+            buildRealTime(duration, f.duration);
+        }
+
+        b.setLabel(f.label);
+
+        if (f.values.size() > 0) {
+            auto values = b.initValues(f.values.size());
+            for (size_t i = 0; i < f.values.size(); ++i) {
+                values.set(i, f.values[i]);
+            }
+        }
+    }
+
+    static void
+    readFeature(Vamp::Plugin::Feature &f,
+                const Feature::Reader &r) {
+
+        f.hasTimestamp = r.getHasTimestamp();
+        if (f.hasTimestamp) {
+            readRealTime(f.timestamp, r.getTimestamp());
+        }
+
+        f.hasDuration = r.getHasDuration();
+        if (f.hasDuration) {
+            readRealTime(f.duration, r.getDuration());
+        }
+
+        f.label = r.getLabel();
+
+        for (auto v: r.getValues()) {
+            f.values.push_back(v);
+        }
+    }
+    
+    static void
+    buildFeatureSet(FeatureSet::Builder &b,
+                    const Vamp::Plugin::FeatureSet &fs) {
+
+        auto featureset = b.initFeaturePairs(fs.size());
+        int ix = 0;
+        for (const auto &fsi : fs) {
+            auto fspair = featureset[ix];
+            fspair.setOutput(fsi.first);
+            auto featurelist = fspair.initFeatures(fsi.second.size());
+            for (size_t j = 0; j < fsi.second.size(); ++j) {
+                auto feature = featurelist[j];
+                buildFeature(feature, fsi.second[j]);
+            }
+            ++ix;
+        }
+    }
+
+    static void
+    readFeatureSet(Vamp::Plugin::FeatureSet &fs,
+                   const FeatureSet::Reader &r) {
+
+        for (const auto &p: r.getFeaturePairs()) {
+            Vamp::Plugin::FeatureList vfl;
+            for (const auto &f: p.getFeatures()) {
+                Vamp::Plugin::Feature vf;
+                readFeature(vf, f);
+                vfl.push_back(vf);
+            }
+            fs[p.getOutput()] = vfl;
+        }
+    }
+    
+    static InputDomain
+    fromInputDomain(Vamp::Plugin::InputDomain d) {
+        switch(d) {
+        case Vamp::Plugin::TimeDomain:
+            return InputDomain::TIME_DOMAIN;
+        case Vamp::Plugin::FrequencyDomain:
+            return InputDomain::FREQUENCY_DOMAIN;
+        default:
+            throw std::logic_error("unexpected Vamp InputDomain enum value");
+        }
+    }
+
+    static Vamp::Plugin::InputDomain
+    toInputDomain(InputDomain d) {
+        switch(d) {
+        case InputDomain::TIME_DOMAIN:
+            return Vamp::Plugin::TimeDomain;
+        case InputDomain::FREQUENCY_DOMAIN:
+            return Vamp::Plugin::FrequencyDomain;
+        default:
+            throw std::logic_error("unexpected Capnp InputDomain enum value");
+        }
+    }
+    
+    static void
+    buildPluginStaticData(PluginStaticData::Builder &b,
+                          const Vamp::HostExt::PluginStaticData &d) {
+
+        b.setPluginKey(d.pluginKey);
+
+        auto basic = b.initBasic();
+        buildBasicDescriptor(basic, d.basic);
+
+        b.setMaker(d.maker);
+        b.setCopyright(d.copyright);
+        b.setPluginVersion(d.pluginVersion);
+
+        auto clist = b.initCategory(d.category.size());
+        for (size_t i = 0; i < d.category.size(); ++i) {
+            clist.set(i, d.category[i]);
+        }
+
+        b.setMinChannelCount(d.minChannelCount);
+        b.setMaxChannelCount(d.maxChannelCount);
+
+        const auto &vparams = d.parameters;
+        auto plist = b.initParameters(vparams.size());
+        for (size_t i = 0; i < vparams.size(); ++i) {
+            auto pd = plist[i];
+            buildParameterDescriptor(pd, vparams[i]);
+        }
+        
+        const auto &vprogs = d.programs;
+        auto pglist = b.initPrograms(vprogs.size());
+        for (size_t i = 0; i < vprogs.size(); ++i) {
+            pglist.set(i, vprogs[i]);
+        }
+
+        b.setInputDomain(fromInputDomain(d.inputDomain));
+
+        const auto &vouts = d.basicOutputInfo;
+        auto olist = b.initBasicOutputInfo(vouts.size());
+        for (size_t i = 0; i < vouts.size(); ++i) {
+            auto od = olist[i];
+            buildBasicDescriptor(od, vouts[i]);
+        }
+    }
+
+    static void
+    readPluginStaticData(Vamp::HostExt::PluginStaticData &d,
+                         const PluginStaticData::Reader &r) {
+        
+        d.pluginKey = r.getPluginKey();
+
+        readBasicDescriptor(d.basic, r.getBasic());
+
+        d.maker = r.getMaker();
+        d.copyright = r.getCopyright();
+        d.pluginVersion = r.getPluginVersion();
+
+        for (auto c: r.getCategory()) {
+            d.category.push_back(c);
+        }
+
+        d.minChannelCount = r.getMinChannelCount();
+        d.maxChannelCount = r.getMaxChannelCount();
+
+        for (auto p: r.getParameters()) {
+            Vamp::Plugin::ParameterDescriptor pd;
+            readParameterDescriptor(pd, p);
+            d.parameters.push_back(pd);
+        }
+
+        for (auto p: r.getPrograms()) {
+            d.programs.push_back(p);
+        }
+
+        d.inputDomain = toInputDomain(r.getInputDomain());
+
+        for (auto o: r.getBasicOutputInfo()) {
+            Vamp::HostExt::PluginStaticData::Basic b;
+            readBasicDescriptor(b, o);
+            d.basicOutputInfo.push_back(b);
+        }
+    }
+
+    static void
+    buildPluginConfiguration(PluginConfiguration::Builder &b,
+                             const Vamp::HostExt::PluginConfiguration &c) {
+
+        const auto &vparams = c.parameterValues;
+        auto params = b.initParameterValues(vparams.size());
+        int i = 0;
+        for (const auto &pp : vparams) {
+            auto param = params[i++];
+            param.setParameter(pp.first);
+            param.setValue(pp.second);
+        }
+
+        b.setCurrentProgram(c.currentProgram);
+        b.setChannelCount(c.channelCount);
+        b.setStepSize(c.stepSize);
+        b.setBlockSize(c.blockSize);
+    }
+
+    static void
+    readPluginConfiguration(Vamp::HostExt::PluginConfiguration &c,
+                            const PluginConfiguration::Reader &r) {
+
+        for (const auto &pp: r.getParameterValues()) {
+            c.parameterValues[pp.getParameter()] = pp.getValue();
+        }
+
+        c.currentProgram = r.getCurrentProgram();
+        c.channelCount = r.getChannelCount();
+        c.stepSize = r.getStepSize();
+        c.blockSize = r.getBlockSize();
+    }
+
+    static void
+    buildLoadRequest(LoadRequest::Builder &r,
+                     const Vamp::HostExt::LoadRequest &req) {
+
+        r.setPluginKey(req.pluginKey);
+        r.setInputSampleRate(req.inputSampleRate);
+
+        std::vector<AdapterFlag> flags;
+        if (req.adapterFlags & Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN) {
+            flags.push_back(AdapterFlag::ADAPT_INPUT_DOMAIN);
+        }
+        if (req.adapterFlags & Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT) {
+            flags.push_back(AdapterFlag::ADAPT_CHANNEL_COUNT);
+        }
+        if (req.adapterFlags & Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE) {
+            flags.push_back(AdapterFlag::ADAPT_BUFFER_SIZE);
+        }
+
+        auto f = r.initAdapterFlags(flags.size());
+        for (size_t i = 0; i < flags.size(); ++i) {
+            f.set(i, flags[i]);
+        }
+    }
+
+    static void
+    readLoadRequest(Vamp::HostExt::LoadRequest &req,
+                    const LoadRequest::Reader &r) {
+
+        req.pluginKey = r.getPluginKey();
+        req.inputSampleRate = r.getInputSampleRate();
+
+        int flags = 0;
+        for (const auto &a: r.getAdapterFlags()) {
+            if (a == AdapterFlag::ADAPT_INPUT_DOMAIN) {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
+            }
+            if (a == AdapterFlag::ADAPT_CHANNEL_COUNT) {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
+            }
+            if (a == AdapterFlag::ADAPT_BUFFER_SIZE) {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
+            }
+        }
+        req.adapterFlags = flags;
+    }
+};
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/capnproto/schemamap.csv	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,16 @@
+basic,
+configurationrequest,
+configurationresponse,
+feature,Feature
+featureset,FeatureSet
+loadrequest,LoadRequest
+loadresponse,LoadResponse
+outputdescriptor,OutputDescriptor
+parameterdescriptor,ParameterDescriptor
+pluginconfiguration,PluginConfiguration
+pluginstaticdata,PluginStaticData
+processblock,
+realtime,RealTime
+request,
+response,
+valueextents,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/capnproto/vamp.capnp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,155 @@
+
+@0xc4b1c6c44c999206;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("vampipe");
+
+enum InputDomain {
+    timeDomain         @0;
+    frequencyDomain    @1;
+}
+
+enum SampleType {
+    oneSamplePerStep   @0;
+    fixedSampleRate    @1;
+    variableSampleRate @2;
+}
+
+enum AdapterFlag {
+    adaptInputDomain   @0;
+    adaptChannelCount  @1;
+    adaptBufferSize    @2;
+}
+
+const adaptAllSafe :List(AdapterFlag) =
+      [ adaptInputDomain, adaptChannelCount ];
+
+const adaptAll :List(AdapterFlag) =
+      [ adaptInputDomain, adaptChannelCount, adaptBufferSize ];
+
+struct RealTime {
+    sec                @0  :Int32       = 0;
+    nsec               @1  :Int32       = 0;
+}
+
+struct Basic {
+    identifier         @0  :Text;
+    name               @1  :Text;
+    description        @2  :Text;
+}
+
+struct ParameterDescriptor {
+    basic              @0  :Basic;
+    unit               @1  :Text;
+    minValue           @2  :Float32     = 0.0;
+    maxValue           @3  :Float32     = 0.0;
+    defaultValue       @4  :Float32     = 0.0;
+    isQuantized        @5  :Bool        = false;
+    quantizeStep       @6  :Float32     = 0.0;
+    valueNames         @7  :List(Text)  = [];
+}
+
+struct OutputDescriptor {
+    basic              @0  :Basic;
+    unit               @1  :Text;
+    hasFixedBinCount   @2  :Bool        = false;
+    binCount           @3  :Int32       = 0;
+    binNames           @4  :List(Text)  = [];
+    hasKnownExtents    @5  :Bool        = false;
+    minValue           @6  :Float32     = 0.0;
+    maxValue           @7  :Float32     = 0.0;
+    isQuantized        @8  :Bool        = false;
+    quantizeStep       @9  :Float32     = 0.0;
+    sampleType         @10 :SampleType;
+    sampleRate         @11 :Float32     = 0.0;
+    hasDuration        @12 :Bool        = false;
+}
+
+struct PluginStaticData {
+    pluginKey          @0  :Text;
+    basic              @1  :Basic;
+    maker              @2  :Text;
+    copyright          @3  :Text;
+    pluginVersion      @4  :Int32;
+    category           @5  :List(Text);
+    minChannelCount    @6  :Int32;
+    maxChannelCount    @7  :Int32;
+    parameters         @8  :List(ParameterDescriptor);
+    programs           @9  :List(Text);
+    inputDomain        @10 :InputDomain;
+    basicOutputInfo    @11 :List(Basic);
+}
+
+struct ProcessInput {
+    inputBuffers       @0  :List(List(Float32));
+    timestamp          @1  :RealTime;
+}
+
+struct Feature {
+    hasTimestamp       @0  :Bool        = false;
+    timestamp          @1  :RealTime;
+    hasDuration        @2  :Bool        = false;
+    duration           @3  :RealTime;
+    label              @4  :Text;
+    values             @5  :List(Float32) = [];
+}
+
+struct FeatureSet {
+    struct FSPair {
+        output         @0  :Int32;
+        features       @1  :List(Feature) = [];
+    }
+    featurePairs       @0  :List(FSPair);
+}
+
+struct PluginConfiguration {
+    struct PVPair {
+        parameter      @0  :Text;
+        value          @1  :Float32;
+    }
+    parameterValues    @0  :List(PVPair);
+    currentProgram     @1  :Text;
+    channelCount       @2  :Int32;
+    stepSize           @3  :Int32;
+    blockSize          @4  :Int32;
+}
+
+struct LoadRequest {
+    pluginKey          @0  :Text;
+    inputSampleRate    @1  :Float32;
+    adapterFlags       @2  :List(AdapterFlag);
+}
+
+struct LoadResponse {
+    pluginHandle       @0  :Int64;
+    staticData         @1  :PluginStaticData;
+    defaultConfiguration @2  :PluginConfiguration;
+}
+
+struct ConfigurationRequest {
+    pluginHandle       @0  :Int64;
+    configuration      @1  :PluginConfiguration;
+}
+
+struct ConfigurationResponse {
+    outputs            @0  :List(OutputDescriptor);
+}
+
+struct VampRequest {
+    request :union {
+	list           @0  :Void;
+	load           @1  :LoadRequest;
+	configure      @2  :ConfigurationRequest;
+    }
+}
+
+struct VampResponse {
+    success            @0  :Bool;
+    errorText          @1  :Text = "";
+    response :union {
+	list           @2  :List(PluginStaticData);
+	load           @3  :LoadResponse;
+	configure      @4  :ConfigurationResponse;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/VampJson.h	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,664 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#ifndef VAMP_JSON_H
+#define VAMP_JSON_H
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <json11/json11.hpp>
+#include <base-n/include/basen.hpp>
+
+#include <vamp-hostsdk/Plugin.h>
+#include <vamp-hostsdk/PluginLoader.h>
+
+class VampJson
+{
+public:
+    class Failure : virtual public std::runtime_error {
+    public:
+        Failure(std::string s) : runtime_error(s) { }
+    };
+    
+    template <typename T>
+    static json11::Json
+    fromBasicDescriptor(const T &t) {
+        return json11::Json::object { 
+            { "identifier", t.identifier },
+            { "name", t.name },
+            { "description", t.description }
+        };
+    }
+
+    template <typename T>
+    static void
+    toBasicDescriptor(json11::Json j, T &t) {
+        if (!j.is_object()) {
+            throw Failure("object expected for basic descriptor content");
+        }
+        if (!j["identifier"].is_string()) {
+            throw Failure("string expected for identifier");
+        }
+        t.identifier = j["identifier"].string_value();
+        t.name = j["name"].string_value();
+        t.description = j["description"].string_value();
+    }
+
+    template <typename T>
+    static json11::Json
+    fromValueExtents(const T &t) {
+        return json11::Json::object {
+            { "min", t.minValue },
+            { "max", t.maxValue }
+        };
+    }
+
+    template <typename T>
+    static bool
+    toValueExtents(json11::Json j, T &t) {
+        if (j["extents"].is_null()) {
+            return false;
+        } else if (j["extents"].is_object()) {
+            if (j["extents"]["min"].is_number() &&
+                j["extents"]["max"].is_number()) {
+                t.minValue = j["extents"]["min"].number_value();
+                t.maxValue = j["extents"]["max"].number_value();
+                return true;
+            } else {
+                throw Failure("numbers expected for min and max");
+            }
+        } else {
+            throw Failure("object expected for extents (if present)");
+        }
+    }
+
+    static json11::Json
+    fromRealTime(const Vamp::RealTime &r) {
+        return json11::Json::object {
+            { "s", r.sec },
+            { "n", r.nsec }
+        };
+    }
+
+    static Vamp::RealTime
+    toRealTime(json11::Json j) {
+        json11::Json sec = j["s"];
+        json11::Json nsec = j["n"];
+        if (!sec.is_number() || !nsec.is_number()) {
+            throw Failure("invalid Vamp::RealTime object " + j.dump());
+        }
+        return Vamp::RealTime(sec.int_value(), nsec.int_value());
+    }
+
+    static std::string
+    fromSampleType(Vamp::Plugin::OutputDescriptor::SampleType type) {
+        switch (type) {
+        case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
+            return "OneSamplePerStep";
+        case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
+            return "FixedSampleRate";
+        case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
+            return "VariableSampleRate";
+        }
+        return "";
+    }
+
+    static Vamp::Plugin::OutputDescriptor::SampleType
+    toSampleType(std::string text) {
+        if (text == "OneSamplePerStep") {
+            return Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
+        } else if (text == "FixedSampleRate") {
+            return Vamp::Plugin::OutputDescriptor::FixedSampleRate;
+        } else if (text == "VariableSampleRate") {
+            return Vamp::Plugin::OutputDescriptor::VariableSampleRate;
+        } else {
+            throw Failure("invalid sample type string: " + text);
+        }
+    }
+
+    static json11::Json
+    fromOutputDescriptor(const Vamp::Plugin::OutputDescriptor &desc) {
+        json11::Json::object jo {
+            { "basic", fromBasicDescriptor(desc) },
+            { "unit", desc.unit },
+            { "sampleType", fromSampleType(desc.sampleType) },
+            { "sampleRate", desc.sampleRate },
+            { "hasDuration", desc.hasDuration }
+        };
+        if (desc.hasFixedBinCount) {
+            jo["binCount"] = int(desc.binCount);
+            jo["binNames"] = json11::Json::array
+                (desc.binNames.begin(), desc.binNames.end());
+        }
+        if (desc.hasKnownExtents) {
+            jo["extents"] = fromValueExtents(desc);
+        }
+        if (desc.isQuantized) {
+            jo["quantizeStep"] = desc.quantizeStep;
+        }
+        return json11::Json(jo);
+    }
+
+    static Vamp::Plugin::OutputDescriptor
+    toOutputDescriptor(json11::Json j) {
+
+        Vamp::Plugin::OutputDescriptor od;
+        if (!j.is_object()) {
+            throw Failure("object expected for output descriptor");
+        }
+    
+        toBasicDescriptor(j["basic"], od);
+
+        od.unit = j["unit"].string_value();
+
+        od.sampleType = toSampleType(j["sampleType"].string_value());
+
+        if (!j["sampleRate"].is_number()) {
+            throw Failure("number expected for sample rate");
+        }
+        od.sampleRate = j["sampleRate"].number_value();
+        od.hasDuration = j["hasDuration"].bool_value();
+
+        if (j["binCount"].is_number() && j["binCount"].int_value() > 0) {
+            od.hasFixedBinCount = true;
+            od.binCount = j["binCount"].int_value();
+            for (auto &n: j["binNames"].array_items()) {
+                if (!n.is_string()) {
+                    throw Failure("string expected for bin name");
+                }
+                od.binNames.push_back(n.string_value());
+            }
+        } else {
+            od.hasFixedBinCount = false;
+        }
+
+        bool extentsPresent = toValueExtents(j, od);
+        od.hasKnownExtents = extentsPresent;
+
+        if (j["quantizeStep"].is_number()) {
+            od.isQuantized = true;
+            od.quantizeStep = j["quantizeStep"].number_value();
+        } else {
+            od.isQuantized = false;
+        }
+
+        return od;
+    }
+
+    static json11::Json
+    fromParameterDescriptor(const Vamp::PluginBase::ParameterDescriptor &desc) {
+
+        json11::Json::object jo {
+            { "basic", fromBasicDescriptor(desc) },
+            { "unit", desc.unit },
+            { "extents", fromValueExtents(desc) },
+            { "defaultValue", desc.defaultValue },
+            { "valueNames", json11::Json::array
+                    (desc.valueNames.begin(), desc.valueNames.end()) }
+        };
+        if (desc.isQuantized) {
+            jo["quantizeStep"] = desc.quantizeStep;
+        }
+        return json11::Json(jo);
+    }
+
+    static Vamp::PluginBase::ParameterDescriptor
+    toParameterDescriptor(json11::Json j) {
+
+        Vamp::PluginBase::ParameterDescriptor pd;
+        if (!j.is_object()) {
+            throw Failure("object expected for parameter descriptor");
+        }
+    
+        toBasicDescriptor(j["basic"], pd);
+
+        pd.unit = j["unit"].string_value();
+
+        bool extentsPresent = toValueExtents(j, pd);
+        if (!extentsPresent) {
+            throw Failure("extents must be present in parameter descriptor");
+        }
+    
+        if (!j["defaultValue"].is_number()) {
+            throw Failure("number expected for default value");
+        }
+    
+        pd.defaultValue = j["defaultValue"].number_value();
+
+        pd.valueNames.clear();
+        for (auto &n: j["valueNames"].array_items()) {
+            if (!n.is_string()) {
+                throw Failure("string expected for value name");
+            }
+            pd.valueNames.push_back(n.string_value());
+        }
+
+        if (j["quantizeStep"].is_number()) {
+            pd.isQuantized = true;
+            pd.quantizeStep = j["quantizeStep"].number_value();
+        } else {
+            pd.isQuantized = false;
+        }
+
+        return pd;
+    }
+
+    static std::string
+    fromFloatBuffer(const float *buffer, size_t nfloats) {
+        // must use char pointers, otherwise the converter will only
+        // encode every 4th byte (as it will count up in float* steps)
+        const char *start = reinterpret_cast<const char *>(buffer);
+        const char *end = reinterpret_cast<const char *>(buffer + nfloats);
+        std::string encoded;
+        bn::encode_b64(start, end, back_inserter(encoded));
+        return encoded;
+    }
+
+    static std::vector<float>
+    toFloatBuffer(std::string encoded) {
+        std::string decoded;
+        bn::decode_b64(encoded.begin(), encoded.end(), back_inserter(decoded));
+        const float *buffer = reinterpret_cast<const float *>(decoded.c_str());
+        size_t n = decoded.size() / sizeof(float);
+        return std::vector<float>(buffer, buffer + n);
+    }
+
+    static json11::Json
+    fromFeature(const Vamp::Plugin::Feature &f) {
+
+        json11::Json::object jo;
+        if (f.values.size() > 0) {
+            jo["b64values"] = fromFloatBuffer(f.values.data(), f.values.size());
+        }
+        if (f.label != "") {
+            jo["label"] = f.label;
+        }
+        if (f.hasTimestamp) {
+            jo["timestamp"] = fromRealTime(f.timestamp);
+        }
+        if (f.hasDuration) {
+            jo["duration"] = fromRealTime(f.duration);
+        }
+        return json11::Json(jo);
+    }
+
+    static Vamp::Plugin::Feature
+    toFeature(json11::Json j) {
+
+        Vamp::Plugin::Feature f;
+        if (!j.is_object()) {
+            throw Failure("object expected for feature");
+        }
+        if (j["timestamp"].is_object()) {
+            f.timestamp = toRealTime(j["timestamp"]);
+            f.hasTimestamp = true;
+        }
+        if (j["duration"].is_object()) {
+            f.duration = toRealTime(j["duration"]);
+            f.hasDuration = true;
+        }
+        if (j["b64values"].is_string()) {
+            f.values = toFloatBuffer(j["b64values"].string_value());
+        } else if (j["values"].is_array()) {
+            for (auto v : j["values"].array_items()) {
+                f.values.push_back(v.number_value());
+            }
+        }
+        f.label = j["label"].string_value();
+        return f;
+    }
+
+    static json11::Json
+    fromFeatureSet(const Vamp::Plugin::FeatureSet &fs) {
+
+        json11::Json::object jo;
+        for (const auto &fsi : fs) {
+            std::vector<json11::Json> fj;
+            for (const Vamp::Plugin::Feature &f: fsi.second) {
+                fj.push_back(fromFeature(f));
+            }
+            std::stringstream sstr;
+            sstr << fsi.first;
+            std::string n = sstr.str();
+            jo[n] = fj;
+        }
+        return json11::Json(jo);
+    }
+
+    static Vamp::Plugin::FeatureList
+    toFeatureList(json11::Json j) {
+
+        Vamp::Plugin::FeatureList fl;
+        if (!j.is_array()) {
+            throw Failure("array expected for feature list");
+        }
+        for (const json11::Json &fj : j.array_items()) {
+            fl.push_back(toFeature(fj));
+        }
+        return fl;
+    }
+
+    static Vamp::Plugin::FeatureSet
+    toFeatureSet(json11::Json j) {
+
+        Vamp::Plugin::FeatureSet fs;
+        if (!j.is_object()) {
+            throw Failure("object expected for feature set");
+        }
+        for (auto &entry : j.object_items()) {
+            std::string nstr = entry.first;
+            size_t count = 0;
+            int n = stoi(nstr, &count);
+            if (n < 0 || fs.find(n) != fs.end() || count < nstr.size()) {
+                throw Failure("invalid or duplicate numerical index for output");
+            }
+            fs[n] = toFeatureList(entry.second);
+        }
+        return fs;
+    }
+
+    static std::string
+    fromInputDomain(Vamp::Plugin::InputDomain domain) {
+
+        switch (domain) {
+        case Vamp::Plugin::TimeDomain:
+            return "TimeDomain";
+        case Vamp::Plugin::FrequencyDomain:
+            return "FrequencyDomain";
+        }
+        return "";
+    }
+
+    static Vamp::Plugin::InputDomain
+    toInputDomain(std::string text) {
+
+        if (text == "TimeDomain") {
+            return Vamp::Plugin::TimeDomain;
+        } else if (text == "FrequencyDomain") {
+            return Vamp::Plugin::FrequencyDomain;
+        } else {
+            throw Failure("invalid input domain string: " + text);
+        }
+    }
+
+    static json11::Json
+    fromPluginStaticData(const Vamp::HostExt::PluginStaticData &d) {
+
+        json11::Json::object jo;
+        jo["pluginKey"] = d.pluginKey;
+        jo["basic"] = fromBasicDescriptor(d.basic);
+        jo["maker"] = d.maker;
+        jo["copyright"] = d.copyright;
+        jo["pluginVersion"] = d.pluginVersion;
+
+        json11::Json::array cat;
+        for (const std::string &c: d.category) cat.push_back(c);
+        jo["category"] = cat;
+
+        jo["minChannelCount"] = d.minChannelCount;
+        jo["maxChannelCount"] = d.maxChannelCount;
+
+        json11::Json::array params;
+        Vamp::PluginBase::ParameterList vparams = d.parameters;
+        for (auto &p: vparams) params.push_back(fromParameterDescriptor(p));
+        jo["parameters"] = params;
+
+        json11::Json::array progs;
+        Vamp::PluginBase::ProgramList vprogs = d.programs;
+        for (auto &p: vprogs) progs.push_back(p);
+        jo["programs"] = progs;
+
+        jo["inputDomain"] = fromInputDomain(d.inputDomain);
+
+        json11::Json::array outinfo;
+        auto vouts = d.basicOutputInfo;
+        for (auto &o: vouts) outinfo.push_back(fromBasicDescriptor(o));
+        jo["basicOutputInfo"] = outinfo;
+    
+        return json11::Json(jo);
+    }
+
+    static Vamp::HostExt::PluginStaticData
+    toPluginStaticData(json11::Json j) {
+
+        std::string err;
+        if (!j.has_shape({
+                    { "pluginKey", json11::Json::STRING },
+                    { "pluginVersion", json11::Json::NUMBER },
+                    { "minChannelCount", json11::Json::NUMBER },
+                    { "maxChannelCount", json11::Json::NUMBER },
+                    { "inputDomain", json11::Json::STRING }}, err)) {
+            throw Failure("malformed plugin static data: " + err);
+        }
+
+        if (!j["basicOutputInfo"].is_array()) {
+            throw Failure("array expected for basic output info");
+        }
+
+        if (!j["maker"].is_null() &&
+            !j["maker"].is_string()) {
+            throw Failure("string expected for maker");
+        }
+        
+        if (!j["copyright"].is_null() &&
+            !j["copyright"].is_string()) {
+            throw Failure("string expected for copyright");
+        }
+
+        if (!j["category"].is_null() &&
+            !j["category"].is_array()) {
+            throw Failure("array expected for category");
+        }
+
+        if (!j["parameters"].is_null() &&
+            !j["parameters"].is_array()) {
+            throw Failure("array expected for parameters");
+        }
+
+        if (!j["programs"].is_null() &&
+            !j["programs"].is_array()) {
+            throw Failure("array expected for programs");
+        }
+
+        if (!j["inputDomain"].is_null() &&
+            !j["inputDomain"].is_string()) {
+            throw Failure("string expected for inputDomain");
+        }
+
+        if (!j["basicOutputInfo"].is_null() &&
+            !j["basicOutputInfo"].is_array()) {
+            throw Failure("array expected for basicOutputInfo");
+        }
+
+        Vamp::HostExt::PluginStaticData psd;
+
+        psd.pluginKey = j["pluginKey"].string_value();
+
+        toBasicDescriptor(j["basic"], psd.basic);
+
+        psd.maker = j["maker"].string_value();
+        psd.copyright = j["copyright"].string_value();
+        psd.pluginVersion = j["pluginVersion"].int_value();
+
+        for (const auto &c : j["category"].array_items()) {
+            if (!c.is_string()) {
+                throw Failure("strings expected in category array");
+            }
+            psd.category.push_back(c.string_value());
+        }
+
+        psd.minChannelCount = j["minChannelCount"].int_value();
+        psd.maxChannelCount = j["maxChannelCount"].int_value();
+
+        for (const auto &p : j["parameters"].array_items()) {
+            auto pd = toParameterDescriptor(p);
+            psd.parameters.push_back(pd);
+        }
+
+        for (const auto &p : j["programs"].array_items()) {
+            if (!p.is_string()) {
+                throw Failure("strings expected in programs array");
+            }
+            psd.programs.push_back(p.string_value());
+        }
+
+        psd.inputDomain = toInputDomain(j["inputDomain"].string_value());
+
+        for (const auto &bo : j["basicOutputInfo"].array_items()) {
+            Vamp::HostExt::PluginStaticData::Basic b;
+            toBasicDescriptor(bo, b);
+            psd.basicOutputInfo.push_back(b);
+        }
+
+        return psd;
+    }
+
+    static json11::Json
+    fromPluginConfiguration(const Vamp::HostExt::PluginConfiguration &c) {
+
+        json11::Json::object jo;
+
+        json11::Json::object paramValues;
+        for (auto &vp: c.parameterValues) {
+            paramValues[vp.first] = vp.second;
+        }
+        jo["parameterValues"] = paramValues;
+
+        if (c.currentProgram != "") {
+            jo["currentProgram"] = c.currentProgram;
+        }
+
+        jo["channelCount"] = c.channelCount;
+        jo["stepSize"] = c.stepSize;
+        jo["blockSize"] = c.blockSize;
+    
+        return json11::Json(jo);
+    }
+
+    static Vamp::HostExt::PluginConfiguration
+    toPluginConfiguration(json11::Json j) {
+        
+        std::string err;
+        if (!j.has_shape({
+                    { "channelCount", json11::Json::NUMBER },
+                    { "stepSize", json11::Json::NUMBER },
+                    { "blockSize", json11::Json::NUMBER } }, err)) {
+            throw Failure("malformed plugin configuration: " + err);
+        }
+
+        if (!j["parameterValues"].is_null() &&
+            !j["parameterValues"].is_object()) {
+            throw Failure("object expected for parameter values");
+        }
+
+        for (auto &pv : j["parameterValues"].object_items()) {
+            if (!pv.second.is_number()) {
+                throw Failure("number expected for parameter value");
+            }
+        }
+    
+        if (!j["currentProgram"].is_null() &&
+            !j["currentProgram"].is_string()) {
+            throw Failure("string expected for program name");
+        }
+
+        Vamp::HostExt::PluginConfiguration config;
+
+        config.channelCount = j["channelCount"].number_value();
+        config.stepSize = j["stepSize"].number_value();
+        config.blockSize = j["blockSize"].number_value();
+        
+        for (auto &pv : j["parameterValues"].object_items()) {
+            config.parameterValues[pv.first] = pv.second.number_value();
+        }
+
+        if (j["currentProgram"].is_string()) {
+            config.currentProgram = j["currentProgram"].string_value();
+        }
+
+        return config;
+    }
+
+    static json11::Json
+    fromAdapterFlags(int flags) {
+
+        json11::Json::array arr;
+
+        if (flags & Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN) {
+            arr.push_back("AdaptInputDomain");
+        }
+        if (flags & Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT) {
+            arr.push_back("AdaptChannelCount");
+        }
+        if (flags & Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE) {
+            arr.push_back("AdaptBufferSize");
+        }
+
+        return json11::Json(arr);
+    }
+
+    static Vamp::HostExt::PluginLoader::AdapterFlags
+    toAdapterFlags(json11::Json j) {
+
+        if (!j.is_array()) {
+            throw Failure("array expected for adapter flags");
+        }
+        int flags = 0x0;
+
+        for (auto &jj: j.array_items()) {
+            if (!jj.is_string()) {
+                throw Failure("string expected for adapter flag");
+            }
+            std::string text = jj.string_value();
+            if (text == "AdaptInputDomain") {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_INPUT_DOMAIN;
+            } else if (text == "AdaptChannelCount") {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_CHANNEL_COUNT;
+            } else if (text == "AdaptBufferSize") {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_BUFFER_SIZE;
+            } else if (text == "AdaptAllSafe") {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL_SAFE;
+            } else if (text == "AdaptAll") {
+                flags |= Vamp::HostExt::PluginLoader::ADAPT_ALL;
+            } else {
+                throw Failure("invalid adapter flag string: " + text);
+            }
+        }
+
+        return Vamp::HostExt::PluginLoader::AdapterFlags(flags);
+    }
+
+    static json11::Json
+    fromLoadRequest(Vamp::HostExt::LoadRequest req) {
+
+        json11::Json::object jo;
+        jo["pluginKey"] = req.pluginKey;
+        jo["inputSampleRate"] = req.inputSampleRate;
+        jo["adapterFlags"] = fromAdapterFlags(req.adapterFlags);
+        return json11::Json(jo);
+    }
+
+    static Vamp::HostExt::LoadRequest
+    toLoadRequest(json11::Json j) {
+        
+        std::string err;
+
+        if (!j.has_shape({
+                    { "pluginKey", json11::Json::STRING },
+                    { "inputSampleRate", json11::Json::NUMBER },
+                    { "adapterFlags", json11::Json::ARRAY } }, err)) {
+            throw VampJson::Failure("malformed load request: " + err);
+        }
+    
+        Vamp::HostExt::LoadRequest req;
+        req.pluginKey = j["pluginKey"].string_value();
+        req.inputSampleRate = j["inputSampleRate"].number_value();
+        req.adapterFlags = toAdapterFlags(j["adapterFlags"]);
+        return req;
+    }
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/base-n/LICENSE	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,19 @@
+Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com)
+
+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 OR COPYRIGHT HOLDERS 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/base-n/README.md	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,69 @@
+base-n provides encoding/decoding support for BaseX encoding schemes accessible through a standard STL-like iterator interface. Standard Base16, Base32, and Base64 are available out-of-the-box. Adding or modifying custom schemes is supported.
+
+# Usage overview #
+
+base-n is a small, single-header library which provides standard Base16, Base32, Base64, and custom Base-N encoding support.
+
+The main functionality is delivered by the following functions in `bn` namespace:
+```
+template<class Iter1, class Iter2>
+void encode_b16(Iter1 start, Iter1 end, Iter2 out);
+
+
+template<class Iter1, class Iter2>
+void encode_b32(Iter1 start, Iter1 end, Iter2 out);
+
+
+template<class Iter1, class Iter2>
+void encode_b64(Iter1 start, Iter1 end, Iter2 out);
+
+
+template<class Iter1, class Iter2>
+void decode_b16(Iter1 start, Iter1 end, Iter2 out);
+
+
+template<class Iter1, class Iter2>
+void decode_b32(Iter1 start, Iter1 end, Iter2 out);
+
+
+template<class Iter1, class Iter2>
+void decode_b64(Iter1 start, Iter1 end, Iter2 out);
+```
+
+In order to encode and decode data in `std::string` variable `in`, you can do the following:
+```
+bn::encode_b64(in.begin(), in.end(), back_inserter(encoded));
+bn::decode_b64(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+```
+
+Should you find yourself lacking some encoding scheme or the default character mapping rules are not good for your use case, you can easily provide your own encoder. For that, you need to define a struct type which will describe the new encoding. Sample below:
+```
+struct b8_custom
+{
+        static size_t group_length()
+        {
+                return 3;
+        }
+
+        static char encode(int index)
+        {
+                const char* const dictionary = "01234567";
+                assert(index < strlen(dictionary));
+                return dictionary[index];
+        }
+
+        static char decode(char c)
+        {
+                if (c >= '0' && c <= '7') {
+                        return c - '0';
+                }
+                return -1;
+        }
+};
+...
+string encoded;
+bn::impl::encode<b8_custom>(in.begin(), in.end(), back_inserter(encoded));
+bn::impl::decode<b8_custom>(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+```
+
+For a full working example, see `basen_example.cpp` file in `example` directory.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/base-n/example/basen_example.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,94 @@
+/**
+ * base-n, 1.0
+ * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com)
+ * 
+ * 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 OR COPYRIGHT HOLDERS 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.
+**/
+#include <cassert>
+#include <cstring>
+#include <iostream>
+#include <iterator>
+#include <string>
+
+#include "basen.hpp"
+
+int main()
+{
+    using namespace std;
+    string in = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
+    cout << in << endl;
+
+    {
+        string encoded;
+        bn::encode_b64(in.begin(), in.end(), back_inserter(encoded));
+        bn::decode_b64(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+        cout << endl;
+    }
+    {
+        string encoded;
+        bn::encode_b32(in.begin(), in.end(), back_inserter(encoded));
+        bn::decode_b32(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+        cout << endl;
+    }
+    {
+        string encoded;
+        bn::encode_b16(in.begin(), in.end(), back_inserter(encoded));
+        bn::decode_b16(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+        cout << endl;
+    }
+    {
+        string encoded = "#TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz\n"
+            "IHNpbmd1bGFyIHBhc3Npb24gZnJvbS@BvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg\n"
+            " dGhlIG1(pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu\n"
+            "\rdWVkIGFuZCBpbmRlZmF0aWdhY*mxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo\n"
+            "ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4";
+        bn::decode_b64(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+        cout << endl;
+    }
+    {
+        // move the struct definition outside of main() for non-C++11 compilers
+        struct b8_custom
+        {
+            static size_t group_length()
+            {
+               return 3;
+            }
+
+            static char encode(int index)
+            {
+                const char* const dictionary = "01234567";
+                assert(index < strlen(dictionary));
+                return dictionary[index];
+            }
+
+            static char decode(char c)
+            {
+                if (c >= '0' && c <= '7') {
+                    return c - '0';
+                }
+                return -1;
+            }
+        };
+        string encoded;
+        bn::impl::encode<b8_custom>(in.begin(), in.end(), back_inserter(encoded));
+        bn::impl::decode<b8_custom>(encoded.begin(), encoded.end(), ostream_iterator<char>(cout, ""));
+        cout << endl;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/base-n/include/basen.hpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,286 @@
+/**
+ * base-n, 1.0
+ * Copyright (C) 2012 Andrzej Zawadzki (azawadzki@gmail.com)
+ * 
+ * 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 OR COPYRIGHT HOLDERS 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.
+**/
+#ifndef BASEN_HPP
+#define BASEN_HPP
+
+#include <algorithm>
+#include <cctype>
+#include <cassert>
+#include <cstring>
+
+namespace bn
+{
+
+template<class Iter1, class Iter2>
+void encode_b16(Iter1 start, Iter1 end, Iter2 out);
+
+template<class Iter1, class Iter2>
+void encode_b32(Iter1 start, Iter1 end, Iter2 out);
+
+template<class Iter1, class Iter2>
+void encode_b64(Iter1 start, Iter1 end, Iter2 out);
+
+template<class Iter1, class Iter2>
+void decode_b16(Iter1 start, Iter1 end, Iter2 out);
+
+template<class Iter1, class Iter2>
+void decode_b32(Iter1 start, Iter1 end, Iter2 out);
+
+template<class Iter1, class Iter2>
+void decode_b64(Iter1 start, Iter1 end, Iter2 out);
+
+namespace impl
+{
+
+namespace {
+
+char extract_partial_bits(char value, unsigned int start_bit, unsigned int bits_count)
+{
+    assert(start_bit + bits_count < 8);
+    char t1 = value >> (8 - bits_count - start_bit);
+    char t2 = t1 & ~(-1 << bits_count);
+    return t2;
+}
+
+char extract_overlapping_bits(char previous, char next, unsigned int start_bit, unsigned int bits_count)
+{
+    assert(start_bit + bits_count < 16);
+    int bits_count_in_previous = 8 - start_bit;
+    int bits_count_in_next = bits_count - bits_count_in_previous;
+    char t1 = previous << bits_count_in_next;
+    char t2 = next >> (8 - bits_count_in_next) & ~(-1 << bits_count_in_next) ;
+    return (t1 | t2) & ~(-1 << bits_count);
+}
+
+}
+
+struct b16_conversion_traits
+{
+    static size_t group_length()
+    {
+       return 4;
+    }
+
+    static char encode(unsigned int index)
+    {
+        const char* const dictionary = "0123456789ABCDEF";
+        assert(index < strlen(dictionary));
+        return dictionary[index];
+    }
+
+    static char decode(char c)
+    {
+        if (c >= '0' && c <= '9') {
+            return c - '0';
+        } else if (c >= 'A' && c <= 'F') {
+            return c - 'A' + 10;
+        }
+        return -1;
+    }
+};
+
+struct b32_conversion_traits
+{
+    static size_t group_length()
+    {
+       return 5;
+    }
+
+    static char encode(unsigned int index)
+    {
+        const char * dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+        assert(index < strlen(dictionary));
+        return dictionary[index];
+    }
+
+    static char decode(char c)
+    {
+        if (c >= 'A' && c <= 'Z') {
+            return c - 'A';
+        } else if (c >= '2' && c <= '7') {
+            return c - '2' + 26;
+        }
+        return -1;
+    }
+};
+
+struct b64_conversion_traits
+{
+    static size_t group_length()
+    {
+       return 6;
+    }
+
+    static char encode(unsigned int index)
+    {
+        const char* const dictionary = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+        assert(index < strlen(dictionary));
+        return dictionary[index];
+    }
+
+    static char decode(char c)
+    {
+        const int alph_len = 26;
+        if (c >= 'A' && c <= 'Z') {
+            return c - 'A';
+        } else if (c >= 'a' && c <= 'z') {
+            return c - 'a' + alph_len * 1;
+        } else if (c >= '0' && c <= '9') {
+            return c - '0' + alph_len * 2;
+        } else if (c == '+') {
+            return c - '+' + alph_len * 2 + 10;
+        } else if (c == '/') {
+            return c - '/' + alph_len * 2 + 11;
+        }
+        return -1;
+    }
+};
+
+template<class ConversionTraits, class Iter1, class Iter2>
+void decode(Iter1 start, Iter1 end, Iter2 out)
+{
+    Iter1 iter = start;
+    int output_current_bit = 0;
+    char buffer = 0;
+
+    while (iter != end) {
+        if (std::isspace(*iter)) {
+            ++iter;
+            continue;
+        }
+        char value = ConversionTraits::decode(*iter);
+        if (value == -1) {
+            // malformed data, but let's go on...
+            ++iter;
+            continue;
+        }
+        unsigned int bits_in_current_byte = std::min<int>(output_current_bit + ConversionTraits::group_length(), 8) - output_current_bit;
+        if (bits_in_current_byte == ConversionTraits::group_length()) {
+            // the value fits within current byte, so we can extract it directly
+            buffer |= value << (8 - output_current_bit - ConversionTraits::group_length());
+            output_current_bit += ConversionTraits::group_length();
+            // check if we filled up current byte completely; in such case we flush output and continue
+            if (output_current_bit == 8) {
+                *out++ = buffer;
+                buffer = 0;
+                output_current_bit = 0;
+            }
+        } else {
+            // the value span across current and next byte
+            int bits_in_next_byte = ConversionTraits::group_length() - bits_in_current_byte;
+            // fill current byte and flush it to our output
+            buffer |= value >> bits_in_next_byte;
+            *out++ = buffer;
+            buffer = 0;
+            // save the remainder of our value in the buffer; it will be flushed
+            // during next iterations
+            buffer |= value << (8 - bits_in_next_byte);
+            output_current_bit = bits_in_next_byte;
+        }
+        ++iter;
+    }
+}
+
+template<class ConversionTraits, class Iter1, class Iter2>
+void encode(Iter1 start, Iter1 end, Iter2 out)
+{
+    Iter1 iter = start;
+    int start_bit = 0;
+    bool has_backlog = false;
+    char backlog = 0;
+
+    while (has_backlog || iter != end) {
+        if (!has_backlog) {
+            if (start_bit + ConversionTraits::group_length() < 8) {
+                // the value fits within single byte, so we can extract it
+                // directly
+                char v = extract_partial_bits(*iter, start_bit, ConversionTraits::group_length());
+                *out++ = ConversionTraits::encode(v);
+                // since we know that start_bit + ConversionTraits::group_length() < 8 we don't need to go
+                // to the next byte
+                start_bit += ConversionTraits::group_length();
+            } else {
+                // our bits are spanning across byte border; we need to keep the
+                // starting point and move over to next byte.
+                backlog = *iter++;
+                has_backlog = true;
+            }
+        } else {
+            // encode value which is made from bits spanning across byte
+            // boundary
+            char v;
+            if (iter == end)
+                 v = extract_overlapping_bits(backlog, 0, start_bit, ConversionTraits::group_length());
+            else
+                 v = extract_overlapping_bits(backlog, *iter, start_bit, ConversionTraits::group_length());
+            *out++ = ConversionTraits::encode(v);
+            has_backlog = false;
+            start_bit = (start_bit + ConversionTraits::group_length()) % 8;
+        }
+    }
+}
+
+} // impl
+
+using namespace bn::impl;
+
+template<class Iter1, class Iter2>
+void encode_b16(Iter1 start, Iter1 end, Iter2 out)
+{
+    encode<b16_conversion_traits>(start, end, out);
+}
+
+template<class Iter1, class Iter2>
+void encode_b32(Iter1 start, Iter1 end, Iter2 out)
+{
+    encode<b32_conversion_traits>(start, end, out);
+}
+
+template<class Iter1, class Iter2>
+void encode_b64(Iter1 start, Iter1 end, Iter2 out)
+{
+    encode<b64_conversion_traits>(start, end, out);
+}
+
+template<class Iter1, class Iter2>
+void decode_b16(Iter1 start, Iter1 end, Iter2 out)
+{
+    decode<b16_conversion_traits>(start, end, out);
+}
+
+template<class Iter1, class Iter2>
+void decode_b32(Iter1 start, Iter1 end, Iter2 out)
+{
+    decode<b32_conversion_traits>(start, end, out);
+}
+
+template<class Iter1, class Iter2>
+void decode_b64(Iter1 start, Iter1 end, Iter2 out)
+{
+    decode<b64_conversion_traits>(start, end, out);
+}
+
+} // bn
+
+#endif // BASEN_HPP
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json-cli.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,209 @@
+
+#include "VampJson.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+#include <map>
+#include <set>
+
+using namespace std;
+using namespace Vamp;
+using namespace Vamp::HostExt;
+using namespace json11;
+
+static map<uint32_t, Plugin *> loadedPlugins;
+static set<uint32_t> initialisedPlugins;
+
+static uint32_t nextHandle = 1; // plugin handle type must fit in JSON number
+
+Vamp::HostExt::LoadResponse
+loadPlugin(json11::Json j) {
+
+    auto req = VampJson::toLoadRequest(j);
+    auto loader = Vamp::HostExt::PluginLoader::getInstance();
+    auto response = loader->loadPlugin(req);
+    
+    if (!response.plugin) {
+	throw VampJson::Failure("plugin load failed");
+    }
+    
+    return response;
+}
+    
+Vamp::Plugin::OutputList
+configurePlugin(Vamp::Plugin *plugin, json11::Json j) {
+    
+    auto config = VampJson::toPluginConfiguration(j);
+    auto loader = Vamp::HostExt::PluginLoader::getInstance();
+    auto outputs = loader->configurePlugin(plugin, config);
+
+    if (outputs.empty()) {
+	throw VampJson::Failure("plugin initialisation failed (invalid channelCount, stepSize, blockSize?)");
+    }
+    
+    return outputs;
+}
+
+Json
+handle_list(Json content)
+{
+    if (content != Json()) {
+	throw VampJson::Failure("no content expected for list request");
+    }
+    
+    auto loader = PluginLoader::getInstance();
+    auto pluginData = loader->listPluginData();
+
+    Json::array j;
+    for (const auto &pd: pluginData) {
+	j.push_back(VampJson::fromPluginStaticData(pd));
+    }
+    return Json(j);
+}
+
+Json
+handle_load(Json j)
+{
+    auto loadResponse = loadPlugin(j);
+
+    if (!loadResponse.plugin) {
+	throw VampJson::Failure("plugin load failed");
+    }
+    
+    uint32_t h = nextHandle++;
+    loadedPlugins[h] = loadResponse.plugin;
+
+    Json::object response;
+    response["pluginHandle"] = double(h);
+    response["staticData"] =
+	VampJson::fromPluginStaticData(loadResponse.staticData);
+    response["defaultConfiguration"] =
+	VampJson::fromPluginConfiguration(loadResponse.defaultConfiguration);
+
+    cerr << "Loaded plugin: handle is " << h << endl;
+    
+    return Json(response);
+}
+
+Json
+handle_configure(Json j)
+{
+    string err;
+
+    if (!j.has_shape({
+		{ "pluginHandle", Json::NUMBER },
+		{ "configuration", Json::OBJECT }}, err)) {
+	throw VampJson::Failure("malformed configuration request: " + err);
+    }
+
+    uint32_t handle = j["pluginHandle"].int_value();
+
+    if (loadedPlugins.find(handle) == loadedPlugins.end()) {
+	throw VampJson::Failure("unknown plugin handle");
+    }
+
+    if (initialisedPlugins.find(handle) != initialisedPlugins.end()) {
+	throw VampJson::Failure("plugin has already been initialised");
+    }
+
+    Plugin *plugin = loadedPlugins[handle];
+    
+    Json config = j["configuration"];
+
+    configurePlugin(plugin, config);
+
+    initialisedPlugins.insert(handle);
+
+    cerr << "Configured and initialised plugin " << handle << endl;
+
+    Json::object jout;
+    Json::array outs;
+    Vamp::Plugin::OutputList vouts = plugin->getOutputDescriptors();
+    for (auto &o: vouts) {
+	outs.push_back(VampJson::fromOutputDescriptor(o));
+    }
+    jout["outputList"] = outs;
+    return Json(jout);
+}
+
+Json
+handle(string input)
+{
+    string err;
+    Json j = Json::parse(input, err);
+
+    if (err != "") {
+	throw VampJson::Failure("invalid request: " + err);
+    }
+
+    if (!j["verb"].is_string()) {
+	throw VampJson::Failure("verb expected in request");
+    }
+
+    if (!j["content"].is_null() &&
+	!j["content"].is_object()) {
+	throw VampJson::Failure("object expected for content");
+    }
+
+    string verb = j["verb"].string_value();
+    Json content = j["content"];
+    Json result;
+
+    if (verb == "list") {
+	result = handle_list(content);
+    } else if (verb == "load") {
+	result = handle_load(content);
+    } else if (verb == "configure") {
+	result = handle_configure(content);
+    } else {
+	throw VampJson::Failure("unknown verb: " + verb +
+				" (known verbs are: list load configure)");
+    }
+
+    return result;
+}
+
+Json
+success_response(Json payload)
+{
+    Json::object obj;
+    obj["success"] = true;
+    obj["response"] = payload;
+    return Json(obj);
+}
+
+Json
+error_response(string text)
+{
+    Json::object obj;
+    obj["success"] = false;
+    obj["errorText"] = text;
+    return Json(obj);
+}
+
+template<typename T>
+T &getline(T &in, string prompt, string &out)
+{
+    cerr << prompt;
+    return getline(in, out);
+}
+
+int main(int, char **)
+{
+    string line;
+
+    while (getline(cin, "> ", line)) {
+	try {
+	    Json result = handle(line);
+	    cout << success_response(result).dump() << endl;
+	} catch (const VampJson::Failure &e) {
+	    cout << error_response(e.what()).dump() << endl;
+	}
+    }
+
+    return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json-test.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,94 @@
+
+#include "VampJson.h"
+
+using std::cerr;
+using std::endl;
+
+int main(int, char **)
+{
+    Vamp::PluginBase::ParameterDescriptor d;
+    d.identifier = "threshold";
+    d.name = "Energy rise threshold";
+    d.description = "Energy rise within a frequency bin necessary to count toward broadband total";
+    d.unit = "dB";
+    d.minValue = 0;
+    d.maxValue = 20.5;
+    d.defaultValue = 3;
+    d.isQuantized = false;
+    cerr << VampJson::fromParameterDescriptor(d).dump() << endl;
+
+    Vamp::Plugin::OutputDescriptor od;
+    od.identifier = "powerspectrum";
+    od.name = "Power Spectrum";
+    od.description = "Power values of the frequency spectrum bins calculated from the input signal";
+    od.unit = "";
+    od.hasFixedBinCount = true;
+    od.binCount = 513;
+    od.hasKnownExtents = false;
+    od.isQuantized = false;
+    od.sampleType = Vamp::Plugin::OutputDescriptor::OneSamplePerStep;
+    cerr << VampJson::fromOutputDescriptor(od).dump() << endl;
+
+    cerr << VampJson::fromFeature(Vamp::Plugin::Feature()).dump() << endl;
+    
+    Vamp::Plugin::Feature f;
+    f.hasTimestamp = true;
+    f.timestamp = Vamp::RealTime::fromSeconds(3.14159);
+    f.hasDuration = false;
+    f.values = { 1, 2, 3.000001, 4, 5, 6, 6.5, 7 };
+    f.label = "A feature";
+    cerr << VampJson::fromFeature(f).dump() << endl;
+
+    Vamp::Plugin::FeatureSet fs;
+    fs[0].push_back(f);
+    std::string fs_str = VampJson::fromFeatureSet(fs).dump();
+    cerr << fs_str << endl;
+
+    std::string err;
+    
+    try {
+	Vamp::Plugin::ParameterDescriptor d1 =
+	    VampJson::toParameterDescriptor
+	    (json11::Json::parse
+	     (VampJson::fromParameterDescriptor(d).dump(), err));
+	if (err != "") {
+	    cerr << "error returned from parser: " << err << endl;
+	}
+	cerr << "\nsuccessfully converted parameter descriptor back from json: serialising it again, we get:" << endl;
+	cerr << VampJson::fromParameterDescriptor(d1).dump() << endl;
+    } catch (std::runtime_error &e) {
+	cerr << "caught exception: " << e.what() << endl;
+    }
+    
+    try {
+	Vamp::Plugin::OutputDescriptor od1 =
+	    VampJson::toOutputDescriptor
+	    (json11::Json::parse
+	     (VampJson::fromOutputDescriptor(od).dump(), err));
+	if (err != "") {
+	    cerr << "error returned from parser: " << err << endl;
+	}
+	cerr << "\nsuccessfully converted output descriptor back from json: serialising it again, we get:" << endl;
+	cerr << VampJson::fromOutputDescriptor(od1).dump() << endl;
+    } catch (std::runtime_error &e) {
+	cerr << "caught exception: " << e.what() << endl;
+    }
+    
+    try {
+	Vamp::Plugin::FeatureSet fs1 =
+	    VampJson::toFeatureSet
+	    (json11::Json::parse(fs_str, err));
+	if (err != "") {
+	    cerr << "error returned from parser: " << err << endl;
+	}
+	cerr << "\nsuccessfully converted feature set back from json: serialising it again, we get:" << endl;
+	cerr << VampJson::fromFeatureSet(fs1).dump() << endl;
+    } catch (std::runtime_error &e) {
+	cerr << "caught exception: " << e.what() << endl;
+    }
+
+    return 0;
+}
+
+    
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/CMakeLists.txt	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,21 @@
+project(json11)
+
+cmake_minimum_required(VERSION 2.8)
+
+enable_testing()
+
+add_definitions(
+    -std=c++11
+    -fno-rtti
+    -fno-exceptions
+    -Wall
+    -Wextra
+    -Werror)
+
+set(json11_SRCS json11.cpp)
+
+add_library(json11 STATIC ${json11_SRCS})
+
+add_test(json11_test json11_test)
+
+add_executable(json11_test ${json11_SRCS} test.cpp)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/LICENSE.txt	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Dropbox, Inc.
+
+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 OR COPYRIGHT HOLDERS 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/Makefile	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,7 @@
+test: json11.cpp json11.hpp test.cpp
+	$(CXX) -O -std=c++11 json11.cpp test.cpp -o test -fno-rtti -fno-exceptions
+
+clean:
+	if [ -e test ]; then rm test; fi
+
+.PHONY: clean
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/README.md	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,42 @@
+json11
+------
+
+json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
+
+The core object provided by the library is json11::Json. A Json object represents any JSON
+value: null, bool, number (int or double), string (std::string), array (std::vector), or
+object (std::map).
+
+Json objects act like values. They can be assigned, copied, moved, compared for equality or
+order, and so on. There are also helper methods Json::dump, to serialize a Json to a string, and
+Json::parse (static) to parse a std::string as a Json object.
+
+It's easy to make a JSON object with C++11's new initializer syntax:
+
+    Json my_json = Json::object {
+        { "key1", "value1" },
+        { "key2", false },
+        { "key3", Json::array { 1, 2, 3 } },
+    };
+    std::string json_str = my_json.dump();
+
+There are also implicit constructors that allow standard and user-defined types to be
+automatically converted to JSON. For example:
+
+    class Point {
+    public:
+        int x;
+        int y;
+        Point (int x, int y) : x(x), y(y) {}
+        Json to_json() const { return Json::array { x, y }; }
+    };
+
+    std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
+    std::string points_json = Json(points).dump();
+
+JSON values can have their values queried and inspected:
+
+    Json json = Json::array { Json::object { { "k", "v" } } };
+    std::string str = json[0]["k"].string_value();
+
+More documentation is still to come. For now, see json11.hpp.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/json11.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,769 @@
+/* Copyright (c) 2013 Dropbox, Inc.
+ *
+ * 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 OR COPYRIGHT HOLDERS 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.
+ */
+
+#include "json11.hpp"
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <cstdio>
+#include <limits>
+
+namespace json11 {
+
+static const int max_depth = 200;
+
+using std::string;
+using std::vector;
+using std::map;
+using std::make_shared;
+using std::initializer_list;
+using std::move;
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Serialization
+ */
+
+static void dump(std::nullptr_t, string &out) {
+    out += "null";
+}
+
+static void dump(double value, string &out) {
+    if (std::isfinite(value)) {
+        char buf[32];
+        snprintf(buf, sizeof buf, "%.17g", value);
+        out += buf;
+    } else {
+        out += "null";
+    }
+}
+
+static void dump(int value, string &out) {
+    char buf[32];
+    snprintf(buf, sizeof buf, "%d", value);
+    out += buf;
+}
+
+static void dump(bool value, string &out) {
+    out += value ? "true" : "false";
+}
+
+static void dump(const string &value, string &out) {
+    out += '"';
+    for (size_t i = 0; i < value.length(); i++) {
+        const char ch = value[i];
+        if (ch == '\\') {
+            out += "\\\\";
+        } else if (ch == '"') {
+            out += "\\\"";
+        } else if (ch == '\b') {
+            out += "\\b";
+        } else if (ch == '\f') {
+            out += "\\f";
+        } else if (ch == '\n') {
+            out += "\\n";
+        } else if (ch == '\r') {
+            out += "\\r";
+        } else if (ch == '\t') {
+            out += "\\t";
+        } else if (static_cast<uint8_t>(ch) <= 0x1f) {
+            char buf[8];
+            snprintf(buf, sizeof buf, "\\u%04x", ch);
+            out += buf;
+        } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
+                   && static_cast<uint8_t>(value[i+2]) == 0xa8) {
+            out += "\\u2028";
+            i += 2;
+        } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
+                   && static_cast<uint8_t>(value[i+2]) == 0xa9) {
+            out += "\\u2029";
+            i += 2;
+        } else {
+            out += ch;
+        }
+    }
+    out += '"';
+}
+
+static void dump(const Json::array &values, string &out) {
+    bool first = true;
+    out += "[";
+    for (const auto &value : values) {
+        if (!first)
+            out += ", ";
+        value.dump(out);
+        first = false;
+    }
+    out += "]";
+}
+
+static void dump(const Json::object &values, string &out) {
+    bool first = true;
+    out += "{";
+    for (const auto &kv : values) {
+        if (!first)
+            out += ", ";
+        dump(kv.first, out);
+        out += ": ";
+        kv.second.dump(out);
+        first = false;
+    }
+    out += "}";
+}
+
+void Json::dump(string &out) const {
+    m_ptr->dump(out);
+}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Value wrappers
+ */
+
+template <Json::Type tag, typename T>
+class Value : public JsonValue {
+protected:
+
+    // Constructors
+    explicit Value(const T &value) : m_value(value) {}
+    explicit Value(T &&value)      : m_value(move(value)) {}
+
+    // Get type tag
+    Json::Type type() const override {
+        return tag;
+    }
+
+    // Comparisons
+    bool equals(const JsonValue * other) const override {
+        return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
+    }
+    bool less(const JsonValue * other) const override {
+        return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
+    }
+
+    const T m_value;
+    void dump(string &out) const override { json11::dump(m_value, out); }
+};
+
+class JsonDouble final : public Value<Json::NUMBER, double> {
+    double number_value() const override { return m_value; }
+    int int_value() const override { return static_cast<int>(m_value); }
+    bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
+    bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }
+public:
+    explicit JsonDouble(double value) : Value(value) {}
+};
+
+class JsonInt final : public Value<Json::NUMBER, int> {
+    double number_value() const override { return m_value; }
+    int int_value() const override { return m_value; }
+    bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
+    bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }
+public:
+    explicit JsonInt(int value) : Value(value) {}
+};
+
+class JsonBoolean final : public Value<Json::BOOL, bool> {
+    bool bool_value() const override { return m_value; }
+public:
+    explicit JsonBoolean(bool value) : Value(value) {}
+};
+
+class JsonString final : public Value<Json::STRING, string> {
+    const string &string_value() const override { return m_value; }
+public:
+    explicit JsonString(const string &value) : Value(value) {}
+    explicit JsonString(string &&value)      : Value(move(value)) {}
+};
+
+class JsonArray final : public Value<Json::ARRAY, Json::array> {
+    const Json::array &array_items() const override { return m_value; }
+    const Json & operator[](size_t i) const override;
+public:
+    explicit JsonArray(const Json::array &value) : Value(value) {}
+    explicit JsonArray(Json::array &&value)      : Value(move(value)) {}
+};
+
+class JsonObject final : public Value<Json::OBJECT, Json::object> {
+    const Json::object &object_items() const override { return m_value; }
+    const Json & operator[](const string &key) const override;
+public:
+    explicit JsonObject(const Json::object &value) : Value(value) {}
+    explicit JsonObject(Json::object &&value)      : Value(move(value)) {}
+};
+
+class JsonNull final : public Value<Json::NUL, std::nullptr_t> {
+public:
+    JsonNull() : Value(nullptr) {}
+};
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Static globals - static-init-safe
+ */
+struct Statics {
+    const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
+    const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
+    const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
+    const string empty_string;
+    const vector<Json> empty_vector;
+    const map<string, Json> empty_map;
+    Statics() {}
+};
+
+static const Statics & statics() {
+    static const Statics s {};
+    return s;
+}
+
+static const Json & static_null() {
+    // This has to be separate, not in Statics, because Json() accesses statics().null.
+    static const Json json_null;
+    return json_null;
+}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Constructors
+ */
+
+Json::Json() noexcept                  : m_ptr(statics().null) {}
+Json::Json(std::nullptr_t) noexcept    : m_ptr(statics().null) {}
+Json::Json(double value)               : m_ptr(make_shared<JsonDouble>(value)) {}
+Json::Json(int value)                  : m_ptr(make_shared<JsonInt>(value)) {}
+Json::Json(bool value)                 : m_ptr(value ? statics().t : statics().f) {}
+Json::Json(const string &value)        : m_ptr(make_shared<JsonString>(value)) {}
+Json::Json(string &&value)             : m_ptr(make_shared<JsonString>(move(value))) {}
+Json::Json(const char * value)         : m_ptr(make_shared<JsonString>(value)) {}
+Json::Json(const Json::array &values)  : m_ptr(make_shared<JsonArray>(values)) {}
+Json::Json(Json::array &&values)       : m_ptr(make_shared<JsonArray>(move(values))) {}
+Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
+Json::Json(Json::object &&values)      : m_ptr(make_shared<JsonObject>(move(values))) {}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Accessors
+ */
+
+Json::Type Json::type()                           const { return m_ptr->type();         }
+double Json::number_value()                       const { return m_ptr->number_value(); }
+int Json::int_value()                             const { return m_ptr->int_value();    }
+bool Json::bool_value()                           const { return m_ptr->bool_value();   }
+const string & Json::string_value()               const { return m_ptr->string_value(); }
+const vector<Json> & Json::array_items()          const { return m_ptr->array_items();  }
+const map<string, Json> & Json::object_items()    const { return m_ptr->object_items(); }
+const Json & Json::operator[] (size_t i)          const { return (*m_ptr)[i];           }
+const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key];         }
+
+double                    JsonValue::number_value()              const { return 0; }
+int                       JsonValue::int_value()                 const { return 0; }
+bool                      JsonValue::bool_value()                const { return false; }
+const string &            JsonValue::string_value()              const { return statics().empty_string; }
+const vector<Json> &      JsonValue::array_items()               const { return statics().empty_vector; }
+const map<string, Json> & JsonValue::object_items()              const { return statics().empty_map; }
+const Json &              JsonValue::operator[] (size_t)         const { return static_null(); }
+const Json &              JsonValue::operator[] (const string &) const { return static_null(); }
+
+const Json & JsonObject::operator[] (const string &key) const {
+    auto iter = m_value.find(key);
+    return (iter == m_value.end()) ? static_null() : iter->second;
+}
+const Json & JsonArray::operator[] (size_t i) const {
+    if (i >= m_value.size()) return static_null();
+    else return m_value[i];
+}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Comparison
+ */
+
+bool Json::operator== (const Json &other) const {
+    if (m_ptr->type() != other.m_ptr->type())
+        return false;
+
+    return m_ptr->equals(other.m_ptr.get());
+}
+
+bool Json::operator< (const Json &other) const {
+    if (m_ptr->type() != other.m_ptr->type())
+        return m_ptr->type() < other.m_ptr->type();
+
+    return m_ptr->less(other.m_ptr.get());
+}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Parsing
+ */
+
+/* esc(c)
+ *
+ * Format char c suitable for printing in an error message.
+ */
+static inline string esc(char c) {
+    char buf[12];
+    if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
+        snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
+    } else {
+        snprintf(buf, sizeof buf, "(%d)", c);
+    }
+    return string(buf);
+}
+
+static inline bool in_range(long x, long lower, long upper) {
+    return (x >= lower && x <= upper);
+}
+
+/* JsonParser
+ *
+ * Object that tracks all state of an in-progress parse.
+ */
+struct JsonParser {
+
+    /* State
+     */
+    const string &str;
+    size_t i;
+    string &err;
+    bool failed;
+    const JsonParse strategy;
+
+    /* fail(msg, err_ret = Json())
+     *
+     * Mark this parse as failed.
+     */
+    Json fail(string &&msg) {
+        return fail(move(msg), Json());
+    }
+
+    template <typename T>
+    T fail(string &&msg, const T err_ret) {
+        if (!failed)
+            err = std::move(msg);
+        failed = true;
+        return err_ret;
+    }
+
+    /* consume_whitespace()
+     *
+     * Advance until the current character is non-whitespace.
+     */
+    void consume_whitespace() {
+        while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
+            i++;
+    }
+
+    /* consume_comment()
+     *
+     * Advance comments (c-style inline and multiline).
+     */
+    bool consume_comment() {
+      bool comment_found = false;
+      if (str[i] == '/') {
+        i++;
+        if (i == str.size())
+          return fail("unexpected end of input inside comment", 0);
+        if (str[i] == '/') { // inline comment
+          i++;
+          if (i == str.size())
+            return fail("unexpected end of input inside inline comment", 0);
+          // advance until next line
+          while (str[i] != '\n') {
+            i++;
+            if (i == str.size())
+              return fail("unexpected end of input inside inline comment", 0);
+          }
+          comment_found = true;
+        }
+        else if (str[i] == '*') { // multiline comment
+          i++;
+          if (i > str.size()-2)
+            return fail("unexpected end of input inside multi-line comment", 0);
+          // advance until closing tokens
+          while (!(str[i] == '*' && str[i+1] == '/')) {
+            i++;
+            if (i > str.size()-2)
+              return fail(
+                "unexpected end of input inside multi-line comment", 0);
+          }
+          i += 2;
+          if (i == str.size())
+            return fail(
+              "unexpected end of input inside multi-line comment", 0);
+          comment_found = true;
+        }
+        else
+          return fail("malformed comment", 0);
+      }
+      return comment_found;
+    }
+
+    /* consume_garbage()
+     *
+     * Advance until the current character is non-whitespace and non-comment.
+     */
+    void consume_garbage() {
+      consume_whitespace();
+      if(strategy == JsonParse::COMMENTS) {
+        bool comment_found = false;
+        do {
+          comment_found = consume_comment();
+          consume_whitespace();
+        }
+        while(comment_found);
+      }
+    }
+
+    /* get_next_token()
+     *
+     * Return the next non-whitespace character. If the end of the input is reached,
+     * flag an error and return 0.
+     */
+    char get_next_token() {
+        consume_garbage();
+        if (i == str.size())
+            return fail("unexpected end of input", 0);
+
+        return str[i++];
+    }
+
+    /* encode_utf8(pt, out)
+     *
+     * Encode pt as UTF-8 and add it to out.
+     */
+    void encode_utf8(long pt, string & out) {
+        if (pt < 0)
+            return;
+
+        if (pt < 0x80) {
+            out += static_cast<char>(pt);
+        } else if (pt < 0x800) {
+            out += static_cast<char>((pt >> 6) | 0xC0);
+            out += static_cast<char>((pt & 0x3F) | 0x80);
+        } else if (pt < 0x10000) {
+            out += static_cast<char>((pt >> 12) | 0xE0);
+            out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
+            out += static_cast<char>((pt & 0x3F) | 0x80);
+        } else {
+            out += static_cast<char>((pt >> 18) | 0xF0);
+            out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
+            out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
+            out += static_cast<char>((pt & 0x3F) | 0x80);
+        }
+    }
+
+    /* parse_string()
+     *
+     * Parse a string, starting at the current position.
+     */
+    string parse_string() {
+        string out;
+        long last_escaped_codepoint = -1;
+        while (true) {
+            if (i == str.size())
+                return fail("unexpected end of input in string", "");
+
+            char ch = str[i++];
+
+            if (ch == '"') {
+                encode_utf8(last_escaped_codepoint, out);
+                return out;
+            }
+
+            if (in_range(ch, 0, 0x1f))
+                return fail("unescaped " + esc(ch) + " in string", "");
+
+            // The usual case: non-escaped characters
+            if (ch != '\\') {
+                encode_utf8(last_escaped_codepoint, out);
+                last_escaped_codepoint = -1;
+                out += ch;
+                continue;
+            }
+
+            // Handle escapes
+            if (i == str.size())
+                return fail("unexpected end of input in string", "");
+
+            ch = str[i++];
+
+            if (ch == 'u') {
+                // Extract 4-byte escape sequence
+                string esc = str.substr(i, 4);
+                // Explicitly check length of the substring. The following loop
+                // relies on std::string returning the terminating NUL when
+                // accessing str[length]. Checking here reduces brittleness.
+                if (esc.length() < 4) {
+                    return fail("bad \\u escape: " + esc, "");
+                }
+                for (int j = 0; j < 4; j++) {
+                    if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
+                            && !in_range(esc[j], '0', '9'))
+                        return fail("bad \\u escape: " + esc, "");
+                }
+
+                long codepoint = strtol(esc.data(), nullptr, 16);
+
+                // JSON specifies that characters outside the BMP shall be encoded as a pair
+                // of 4-hex-digit \u escapes encoding their surrogate pair components. Check
+                // whether we're in the middle of such a beast: the previous codepoint was an
+                // escaped lead (high) surrogate, and this is a trail (low) surrogate.
+                if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
+                        && in_range(codepoint, 0xDC00, 0xDFFF)) {
+                    // Reassemble the two surrogate pairs into one astral-plane character, per
+                    // the UTF-16 algorithm.
+                    encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
+                                 | (codepoint - 0xDC00)) + 0x10000, out);
+                    last_escaped_codepoint = -1;
+                } else {
+                    encode_utf8(last_escaped_codepoint, out);
+                    last_escaped_codepoint = codepoint;
+                }
+
+                i += 4;
+                continue;
+            }
+
+            encode_utf8(last_escaped_codepoint, out);
+            last_escaped_codepoint = -1;
+
+            if (ch == 'b') {
+                out += '\b';
+            } else if (ch == 'f') {
+                out += '\f';
+            } else if (ch == 'n') {
+                out += '\n';
+            } else if (ch == 'r') {
+                out += '\r';
+            } else if (ch == 't') {
+                out += '\t';
+            } else if (ch == '"' || ch == '\\' || ch == '/') {
+                out += ch;
+            } else {
+                return fail("invalid escape character " + esc(ch), "");
+            }
+        }
+    }
+
+    /* parse_number()
+     *
+     * Parse a double.
+     */
+    Json parse_number() {
+        size_t start_pos = i;
+
+        if (str[i] == '-')
+            i++;
+
+        // Integer part
+        if (str[i] == '0') {
+            i++;
+            if (in_range(str[i], '0', '9'))
+                return fail("leading 0s not permitted in numbers");
+        } else if (in_range(str[i], '1', '9')) {
+            i++;
+            while (in_range(str[i], '0', '9'))
+                i++;
+        } else {
+            return fail("invalid " + esc(str[i]) + " in number");
+        }
+
+        if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
+                && (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
+            return std::atoi(str.c_str() + start_pos);
+        }
+
+        // Decimal part
+        if (str[i] == '.') {
+            i++;
+            if (!in_range(str[i], '0', '9'))
+                return fail("at least one digit required in fractional part");
+
+            while (in_range(str[i], '0', '9'))
+                i++;
+        }
+
+        // Exponent part
+        if (str[i] == 'e' || str[i] == 'E') {
+            i++;
+
+            if (str[i] == '+' || str[i] == '-')
+                i++;
+
+            if (!in_range(str[i], '0', '9'))
+                return fail("at least one digit required in exponent");
+
+            while (in_range(str[i], '0', '9'))
+                i++;
+        }
+
+        return std::strtod(str.c_str() + start_pos, nullptr);
+    }
+
+    /* expect(str, res)
+     *
+     * Expect that 'str' starts at the character that was just read. If it does, advance
+     * the input and return res. If not, flag an error.
+     */
+    Json expect(const string &expected, Json res) {
+        assert(i != 0);
+        i--;
+        if (str.compare(i, expected.length(), expected) == 0) {
+            i += expected.length();
+            return res;
+        } else {
+            return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
+        }
+    }
+
+    /* parse_json()
+     *
+     * Parse a JSON object.
+     */
+    Json parse_json(int depth) {
+        if (depth > max_depth) {
+            return fail("exceeded maximum nesting depth");
+        }
+
+        char ch = get_next_token();
+        if (failed)
+            return Json();
+
+        if (ch == '-' || (ch >= '0' && ch <= '9')) {
+            i--;
+            return parse_number();
+        }
+
+        if (ch == 't')
+            return expect("true", true);
+
+        if (ch == 'f')
+            return expect("false", false);
+
+        if (ch == 'n')
+            return expect("null", Json());
+
+        if (ch == '"')
+            return parse_string();
+
+        if (ch == '{') {
+            map<string, Json> data;
+            ch = get_next_token();
+            if (ch == '}')
+                return data;
+
+            while (1) {
+                if (ch != '"')
+                    return fail("expected '\"' in object, got " + esc(ch));
+
+                string key = parse_string();
+                if (failed)
+                    return Json();
+
+                ch = get_next_token();
+                if (ch != ':')
+                    return fail("expected ':' in object, got " + esc(ch));
+
+                data[std::move(key)] = parse_json(depth + 1);
+                if (failed)
+                    return Json();
+
+                ch = get_next_token();
+                if (ch == '}')
+                    break;
+                if (ch != ',')
+                    return fail("expected ',' in object, got " + esc(ch));
+
+                ch = get_next_token();
+            }
+            return data;
+        }
+
+        if (ch == '[') {
+            vector<Json> data;
+            ch = get_next_token();
+            if (ch == ']')
+                return data;
+
+            while (1) {
+                i--;
+                data.push_back(parse_json(depth + 1));
+                if (failed)
+                    return Json();
+
+                ch = get_next_token();
+                if (ch == ']')
+                    break;
+                if (ch != ',')
+                    return fail("expected ',' in list, got " + esc(ch));
+
+                ch = get_next_token();
+                (void)ch;
+            }
+            return data;
+        }
+
+        return fail("expected value, got " + esc(ch));
+    }
+};
+
+Json Json::parse(const string &in, string &err, JsonParse strategy) {
+    JsonParser parser { in, 0, err, false, strategy };
+    Json result = parser.parse_json(0);
+
+    // Check for any trailing garbage
+    parser.consume_garbage();
+    if (parser.i != in.size())
+        return parser.fail("unexpected trailing " + esc(in[parser.i]));
+
+    return result;
+}
+
+// Documented in json11.hpp
+vector<Json> Json::parse_multi(const string &in,
+                               string &err,
+                               JsonParse strategy) {
+    JsonParser parser { in, 0, err, false, strategy };
+
+    vector<Json> json_vec;
+    while (parser.i != in.size() && !parser.failed) {
+        json_vec.push_back(parser.parse_json(0));
+        // Check for another object
+        parser.consume_garbage();
+    }
+    return json_vec;
+}
+
+/* * * * * * * * * * * * * * * * * * * *
+ * Shape-checking
+ */
+
+bool Json::has_shape(const shape & types, string & err) const {
+    if (!is_object()) {
+        err = "expected JSON object, got " + dump();
+        return false;
+    }
+
+    for (auto & item : types) {
+        if ((*this)[item.first].type() != item.second) {
+            err = "bad type for " + item.first + " in " + dump();
+            return false;
+        }
+    }
+
+    return true;
+}
+
+} // namespace json11
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/json11.hpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,211 @@
+/* json11
+ *
+ * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
+ *
+ * The core object provided by the library is json11::Json. A Json object represents any JSON
+ * value: null, bool, number (int or double), string (std::string), array (std::vector), or
+ * object (std::map).
+ *
+ * Json objects act like values: they can be assigned, copied, moved, compared for equality or
+ * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
+ * Json::parse (static) to parse a std::string as a Json object.
+ *
+ * Internally, the various types of Json object are represented by the JsonValue class
+ * hierarchy.
+ *
+ * A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
+ * so some JSON implementations distinguish between integers and floating-point numbers, while
+ * some don't. In json11, we choose the latter. Because some JSON implementations (namely
+ * Javascript itself) treat all numbers as the same type, distinguishing the two leads
+ * to JSON that will be *silently* changed by a round-trip through those implementations.
+ * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
+ * provides integer helpers.
+ *
+ * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
+ * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
+ * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
+ * will be exact for +/- 275 years.)
+ */
+
+/* Copyright (c) 2013 Dropbox, Inc.
+ *
+ * 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 OR COPYRIGHT HOLDERS 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+#include <memory>
+#include <initializer_list>
+
+namespace json11 {
+
+enum JsonParse {
+    STANDARD, COMMENTS
+};
+
+class JsonValue;
+
+class Json final {
+public:
+    // Types
+    enum Type {
+        NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
+    };
+
+    // Array and object typedefs
+    typedef std::vector<Json> array;
+    typedef std::map<std::string, Json> object;
+
+    // Constructors for the various types of JSON value.
+    Json() noexcept;                // NUL
+    Json(std::nullptr_t) noexcept;  // NUL
+    Json(double value);             // NUMBER
+    Json(int value);                // NUMBER
+    Json(bool value);               // BOOL
+    Json(const std::string &value); // STRING
+    Json(std::string &&value);      // STRING
+    Json(const char * value);       // STRING
+    Json(const array &values);      // ARRAY
+    Json(array &&values);           // ARRAY
+    Json(const object &values);     // OBJECT
+    Json(object &&values);          // OBJECT
+
+    // Implicit constructor: anything with a to_json() function.
+    template <class T, class = decltype(&T::to_json)>
+    Json(const T & t) : Json(t.to_json()) {}
+
+    // Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
+    template <class M, typename std::enable_if<
+        std::is_constructible<std::string, typename M::key_type>::value
+        && std::is_constructible<Json, typename M::mapped_type>::value,
+            int>::type = 0>
+    Json(const M & m) : Json(object(m.begin(), m.end())) {}
+
+    // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
+    template <class V, typename std::enable_if<
+        std::is_constructible<Json, typename V::value_type>::value,
+            int>::type = 0>
+    Json(const V & v) : Json(array(v.begin(), v.end())) {}
+
+    // This prevents Json(some_pointer) from accidentally producing a bool. Use
+    // Json(bool(some_pointer)) if that behavior is desired.
+    Json(void *) = delete;
+
+    // Accessors
+    Type type() const;
+
+    bool is_null()   const { return type() == NUL; }
+    bool is_number() const { return type() == NUMBER; }
+    bool is_bool()   const { return type() == BOOL; }
+    bool is_string() const { return type() == STRING; }
+    bool is_array()  const { return type() == ARRAY; }
+    bool is_object() const { return type() == OBJECT; }
+
+    // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
+    // distinguish between integer and non-integer numbers - number_value() and int_value()
+    // can both be applied to a NUMBER-typed object.
+    double number_value() const;
+    int int_value() const;
+
+    // Return the enclosed value if this is a boolean, false otherwise.
+    bool bool_value() const;
+    // Return the enclosed string if this is a string, "" otherwise.
+    const std::string &string_value() const;
+    // Return the enclosed std::vector if this is an array, or an empty vector otherwise.
+    const array &array_items() const;
+    // Return the enclosed std::map if this is an object, or an empty map otherwise.
+    const object &object_items() const;
+
+    // Return a reference to arr[i] if this is an array, Json() otherwise.
+    const Json & operator[](size_t i) const;
+    // Return a reference to obj[key] if this is an object, Json() otherwise.
+    const Json & operator[](const std::string &key) const;
+
+    // Serialize.
+    void dump(std::string &out) const;
+    std::string dump() const {
+        std::string out;
+        dump(out);
+        return out;
+    }
+
+    // Parse. If parse fails, return Json() and assign an error message to err.
+    static Json parse(const std::string & in,
+                      std::string & err,
+                      JsonParse strategy = JsonParse::STANDARD);
+    static Json parse(const char * in,
+                      std::string & err,
+                      JsonParse strategy = JsonParse::STANDARD) {
+        if (in) {
+            return parse(std::string(in), err, strategy);
+        } else {
+            err = "null input";
+            return nullptr;
+        }
+    }
+    // Parse multiple objects, concatenated or separated by whitespace
+    static std::vector<Json> parse_multi(
+        const std::string & in,
+        std::string & err,
+        JsonParse strategy = JsonParse::STANDARD);
+
+    bool operator== (const Json &rhs) const;
+    bool operator<  (const Json &rhs) const;
+    bool operator!= (const Json &rhs) const { return !(*this == rhs); }
+    bool operator<= (const Json &rhs) const { return !(rhs < *this); }
+    bool operator>  (const Json &rhs) const { return  (rhs < *this); }
+    bool operator>= (const Json &rhs) const { return !(*this < rhs); }
+
+    /* has_shape(types, err)
+     *
+     * Return true if this is a JSON object and, for each item in types, has a field of
+     * the given type. If not, return false and set err to a descriptive message.
+     */
+    typedef std::initializer_list<std::pair<std::string, Type>> shape;
+    bool has_shape(const shape & types, std::string & err) const;
+
+private:
+    std::shared_ptr<JsonValue> m_ptr;
+};
+
+// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
+class JsonValue {
+protected:
+    friend class Json;
+    friend class JsonInt;
+    friend class JsonDouble;
+    virtual Json::Type type() const = 0;
+    virtual bool equals(const JsonValue * other) const = 0;
+    virtual bool less(const JsonValue * other) const = 0;
+    virtual void dump(std::string &out) const = 0;
+    virtual double number_value() const;
+    virtual int int_value() const;
+    virtual bool bool_value() const;
+    virtual const std::string &string_value() const;
+    virtual const Json::array &array_items() const;
+    virtual const Json &operator[](size_t i) const;
+    virtual const Json::object &object_items() const;
+    virtual const Json &operator[](const std::string &key) const;
+    virtual ~JsonValue() {}
+};
+
+} // namespace json11
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/json11/test.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,205 @@
+#include <string>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include "json11.hpp"
+#include <cassert>
+#include <list>
+#include <set>
+#include <unordered_map>
+
+using namespace json11;
+using std::string;
+
+// Check that Json has the properties we want.
+#include <type_traits>
+#define CHECK_TRAIT(x) static_assert(std::x::value, #x)
+CHECK_TRAIT(is_nothrow_constructible<Json>);
+CHECK_TRAIT(is_nothrow_default_constructible<Json>);
+CHECK_TRAIT(is_copy_constructible<Json>);
+CHECK_TRAIT(is_nothrow_move_constructible<Json>);
+CHECK_TRAIT(is_copy_assignable<Json>);
+CHECK_TRAIT(is_nothrow_move_assignable<Json>);
+CHECK_TRAIT(is_nothrow_destructible<Json>);
+
+void parse_from_stdin() {
+    string buf;
+    string line;
+    while (std::getline(std::cin, line)) {
+        buf += line + "\n";
+    }
+
+    string err;
+    auto json = Json::parse(buf, err);
+    if (!err.empty()) {
+        printf("Failed: %s\n", err.c_str());
+    } else {
+        printf("Result: %s\n", json.dump().c_str());
+    }
+}
+
+int main(int argc, char **argv) {
+    if (argc == 2 && argv[1] == string("--stdin")) {
+        parse_from_stdin();
+        return 0;
+    }
+
+    const string simple_test =
+        R"({"k1":"v1", "k2":42, "k3":["a",123,true,false,null]})";
+
+    string err;
+    auto json = Json::parse(simple_test, err);
+
+    std::cout << "k1: " << json["k1"].string_value() << "\n";
+    std::cout << "k3: " << json["k3"].dump() << "\n";
+
+    for (auto &k : json["k3"].array_items()) {
+        std::cout << "    - " << k.dump() << "\n";
+    }
+
+    const string comment_test = R"({
+      // comment /* with nested comment */
+      "a": 1,
+      // comment
+      // continued
+      "b": "text",
+      /* multi
+         line
+         comment */
+      // and single-line comment
+      "c": [1, 2, 3]
+    })";
+
+    string err_comment;
+    auto json_comment = Json::parse(
+      comment_test, err_comment, JsonParse::COMMENTS);
+    if (!err_comment.empty()) {
+        printf("Failed: %s\n", err_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_comment.dump().c_str());
+    }
+
+    string failing_comment_test = R"({
+      /* bad comment
+      "a": 1,
+    })";
+
+    string err_failing_comment;
+    auto json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    if (!err_failing_comment.empty()) {
+        printf("Failed: %s\n", err_failing_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_failing_comment.dump().c_str());
+    }
+
+    failing_comment_test = R"({
+      / / bad comment })";
+
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    if (!err_failing_comment.empty()) {
+        printf("Failed: %s\n", err_failing_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_failing_comment.dump().c_str());
+    }
+
+    failing_comment_test = R"({// bad comment })";
+
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    if (!err_failing_comment.empty()) {
+        printf("Failed: %s\n", err_failing_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_failing_comment.dump().c_str());
+    }
+
+    failing_comment_test = R"({
+          "a": 1
+        }/)";
+
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    if (!err_failing_comment.empty()) {
+        printf("Failed: %s\n", err_failing_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_failing_comment.dump().c_str());
+    }
+
+    failing_comment_test = R"({/* bad
+                                  comment *})";
+
+    json_failing_comment = Json::parse(
+      failing_comment_test, err_failing_comment, JsonParse::COMMENTS);
+    if (!err_failing_comment.empty()) {
+        printf("Failed: %s\n", err_failing_comment.c_str());
+    } else {
+        printf("Result: %s\n", json_failing_comment.dump().c_str());
+    }
+
+    std::list<int> l1 { 1, 2, 3 };
+    std::vector<int> l2 { 1, 2, 3 };
+    std::set<int> l3 { 1, 2, 3 };
+    assert(Json(l1) == Json(l2));
+    assert(Json(l2) == Json(l3));
+
+    std::map<string, string> m1 { { "k1", "v1" }, { "k2", "v2" } };
+    std::unordered_map<string, string> m2 { { "k1", "v1" }, { "k2", "v2" } };
+    assert(Json(m1) == Json(m2));
+
+    // Json literals
+    Json obj = Json::object({
+        { "k1", "v1" },
+        { "k2", 42.0 },
+        { "k3", Json::array({ "a", 123.0, true, false, nullptr }) },
+    });
+
+    std::cout << "obj: " << obj.dump() << "\n";
+
+    assert(Json("a").number_value() == 0);
+    assert(Json("a").string_value() == "a");
+    assert(Json().number_value() == 0);
+
+    assert(obj == json);
+    assert(Json(42) == Json(42.0));
+    assert(Json(42) != Json(42.1));
+
+    const string unicode_escape_test =
+        R"([ "blah\ud83d\udca9blah\ud83dblah\udca9blah\u0000blah\u1234" ])";
+
+    const char utf8[] = "blah" "\xf0\x9f\x92\xa9" "blah" "\xed\xa0\xbd" "blah"
+                        "\xed\xb2\xa9" "blah" "\0" "blah" "\xe1\x88\xb4";
+
+    Json uni = Json::parse(unicode_escape_test, err);
+    assert(uni[0].string_value().size() == (sizeof utf8) - 1);
+    assert(std::memcmp(uni[0].string_value().data(), utf8, sizeof utf8) == 0);
+
+    // Demonstrates the behavior change in Xcode 7 / Clang 3.7 described
+    // here: https://llvm.org/bugs/show_bug.cgi?id=23812
+    Json nested_array = Json::array { Json::array { 1, 2, 3 } };
+    assert(nested_array.is_array());
+    assert(nested_array.array_items().size() == 1);
+    assert(nested_array.array_items()[0].is_array());
+    assert(nested_array.array_items()[0].array_items().size() == 3);
+
+    Json my_json = Json::object {
+        { "key1", "value1" },
+        { "key2", false },
+        { "key3", Json::array { 1, 2, 3 } },
+    };
+    std::string json_str = my_json.dump();
+    printf("%s\n", json_str.c_str());
+
+    class Point {
+    public:
+        int x;
+        int y;
+        Point (int x, int y) : x(x), y(y) {}
+        Json to_json() const { return Json::array { x, y }; }
+    };
+
+    std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
+    std::string points_json = Json(points).dump();
+    printf("%s\n", points_json.c_str());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json/test-cli.sh	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+./json-cli <<EOF
+{"verb":"list"}
+{"verb":"load","content": {"pluginKey":"vamp-example-plugins:percussiononsets","inputSampleRate":44100,"adapterFlags":["AdaptInputDomain"]}}
+{"verb":"configure","content":{"pluginHandle":1,"configuration":{"blockSize": 512, "channelCount": 1, "parameterValues": {"sensitivity": 40, "threshold": 3}, "stepSize": 1024}}}
+EOF
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utilities/json-to-capnp.cpp	Fri May 13 13:48:59 2016 +0100
@@ -0,0 +1,132 @@
+
+#include "VampJson.h"
+#include "vampipe-convert.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+using namespace std;
+using namespace json11;
+using namespace vampipe;
+
+// Accepting JSON objects with two fields, "type" and "payload". The
+// "type" string corresponds to the JSON schema filename
+// (e.g. "outputdescriptor") and the "payload" is the JSON object
+// encoded with that schema.
+
+Json
+json_input(string input)
+{
+    string err;
+    Json j = Json::parse(input, err);
+    if (err != "") {
+	throw VampJson::Failure("invalid json: " + err);
+    }
+    if (!j.is_object()) {
+	throw VampJson::Failure("object expected at top level");
+    }
+    if (!j["type"].is_string()) {
+	throw VampJson::Failure("string expected for type field");
+    }
+    if (!j["payload"].is_object()) {
+	throw VampJson::Failure("object expected for payload field");
+    }
+    return j;
+}
+
+void 
+handle_input(::capnp::MallocMessageBuilder &message, string input)
+{
+    string err;
+
+    Json j = json_input(input);
+    string type = j["type"].string_value();
+    Json payload = j["payload"];
+
+    if (type == "basic") {
+	throw VampJson::Failure("can't convert Basic block on its own");
+
+    } else if (type == "configurationrequest") {
+	throw VampJson::Failure("not implemented yet"); ///!!!
+
+    } else if (type == "configurationresponse") {
+	throw VampJson::Failure("not implemented yet"); ///!!!
+
+    } else if (type == "feature") {
+	auto f = message.initRoot<Feature>();
+	VampSDKConverter::buildFeature
+	    (f, VampJson::toFeature(payload));
+
+    } else if (type == "featureset") {
+	auto fs = message.initRoot<FeatureSet>();
+	VampSDKConverter::buildFeatureSet
+	    (fs, VampJson::toFeatureSet(payload));
+
+    } else if (type == "loadrequest") {
+	auto req = message.initRoot<LoadRequest>();
+	VampSDKConverter::buildLoadRequest
+	    (req, VampJson::toLoadRequest(payload));
+	
+    } else if (type == "loadresponse") {
+	//!!! response types & configure call for plugin handles, but
+	//!!! we don't have any context in which a plugin handle can
+	//!!! be persistent here
+	throw VampJson::Failure("not implemented yet"); ///!!!
+
+    } else if (type == "outputdescriptor") {
+	auto od = message.initRoot<OutputDescriptor>();
+	VampSDKConverter::buildOutputDescriptor
+	    (od, VampJson::toOutputDescriptor(payload));
+
+    } else if (type == "parameterdescriptor") {
+	auto pd = message.initRoot<ParameterDescriptor>();
+	VampSDKConverter::buildParameterDescriptor
+	    (pd, VampJson::toParameterDescriptor(payload));
+
+    } else if (type == "pluginconfiguration") {
+	auto pc = message.initRoot<PluginConfiguration>();
+	auto config = VampJson::toPluginConfiguration(payload);
+	VampSDKConverter::buildPluginConfiguration(pc, config);
+
+    } else if (type == "pluginstaticdata") {
+	auto pc = message.initRoot<PluginStaticData>();
+	auto sd = VampJson::toPluginStaticData(payload);
+ 	VampSDKConverter::buildPluginStaticData(pc, sd);
+
+    } else if (type == "processblock") {
+	throw VampJson::Failure("not implemented yet"); ///!!!
+
+    } else if (type == "realtime") {
+	auto b = message.initRoot<RealTime>();
+	VampSDKConverter::buildRealTime
+	    (b, VampJson::toRealTime(payload));
+	
+    } else if (type == "valueextents") {
+	throw VampJson::Failure("no ValueExtents struct in Cap'n Proto mapping");
+	
+    } else {
+	throw VampJson::Failure("unknown or unsupported JSON schema type " +
+				type);
+    }
+}
+    
+int main(int, char **)
+{
+    string input;
+
+    while (getline(cin, input)) {
+	try {
+	    ::capnp::MallocMessageBuilder message;
+	    handle_input(message, input);
+	    writePackedMessageToFd(1, message); // stdout
+	    return 0;
+	} catch (const VampJson::Failure &e) {
+	    cerr << "Failed to convert JSON to Cap'n Proto message: "
+		 << e.what() << endl;
+	    return 1;
+	}
+    }
+}
+
+