From b91e1a81db7b45d0460da1c8a134d855e0ff265c Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Mon, 11 Feb 2013 00:18:55 +0000
Subject: Order examples in a sensible progression for the book.

---
 plugins/eg-amp.lv2/README.txt             |  21 --
 plugins/eg-amp.lv2/amp.c                  | 225 --------------
 plugins/eg-amp.lv2/amp.ttl                |  77 -----
 plugins/eg-amp.lv2/manifest.ttl.in        | 101 ------
 plugins/eg-amp.lv2/waf                    |   1 -
 plugins/eg-amp.lv2/wscript                |  67 ----
 plugins/eg-metro.lv2/README.txt           |   9 -
 plugins/eg-metro.lv2/manifest.ttl.in      |   7 -
 plugins/eg-metro.lv2/metro.c              | 347 ---------------------
 plugins/eg-metro.lv2/metro.ttl            |  39 ---
 plugins/eg-metro.lv2/waf                  |   1 -
 plugins/eg-metro.lv2/wscript              |  64 ----
 plugins/eg-midigate.lv2/README.txt        |  10 -
 plugins/eg-midigate.lv2/manifest.ttl.in   |  10 -
 plugins/eg-midigate.lv2/midigate.c        | 223 -------------
 plugins/eg-midigate.lv2/midigate.ttl      |  80 -----
 plugins/eg-midigate.lv2/waf               |   1 -
 plugins/eg-midigate.lv2/wscript           |  65 ----
 plugins/eg-sampler.lv2/README.txt         |   1 -
 plugins/eg-sampler.lv2/click.wav          | Bin 644 -> 0 bytes
 plugins/eg-sampler.lv2/manifest.ttl.in    |  13 -
 plugins/eg-sampler.lv2/sampler.c          | 498 ------------------------------
 plugins/eg-sampler.lv2/sampler.ttl        |  83 -----
 plugins/eg-sampler.lv2/sampler_ui.c       | 201 ------------
 plugins/eg-sampler.lv2/uris.h             | 142 ---------
 plugins/eg-sampler.lv2/waf                |   1 -
 plugins/eg-sampler.lv2/wscript            |  80 -----
 plugins/eg-synth.lv2/manifest.ttl.in      |   9 -
 plugins/eg-synth.lv2/synth.c              | 171 ----------
 plugins/eg-synth.lv2/synth.ttl            |  44 ---
 plugins/eg-synth.lv2/waf                  |   1 -
 plugins/eg-synth.lv2/wscript              |  64 ----
 plugins/eg01-amp.lv2/README.txt           |  21 ++
 plugins/eg01-amp.lv2/amp.c                | 225 ++++++++++++++
 plugins/eg01-amp.lv2/amp.ttl              |  86 ++++++
 plugins/eg01-amp.lv2/manifest.ttl.in      | 101 ++++++
 plugins/eg01-amp.lv2/waf                  |   1 +
 plugins/eg01-amp.lv2/wscript              |  67 ++++
 plugins/eg02-midigate.lv2/README.txt      |  10 +
 plugins/eg02-midigate.lv2/manifest.ttl.in |  10 +
 plugins/eg02-midigate.lv2/midigate.c      | 223 +++++++++++++
 plugins/eg02-midigate.lv2/midigate.ttl    |  80 +++++
 plugins/eg02-midigate.lv2/waf             |   1 +
 plugins/eg02-midigate.lv2/wscript         |  65 ++++
 plugins/eg03-metro.lv2/README.txt         |   9 +
 plugins/eg03-metro.lv2/manifest.ttl.in    |   7 +
 plugins/eg03-metro.lv2/metro.c            | 345 +++++++++++++++++++++
 plugins/eg03-metro.lv2/metro.ttl          |  39 +++
 plugins/eg03-metro.lv2/waf                |   1 +
 plugins/eg03-metro.lv2/wscript            |  64 ++++
 plugins/eg04-sampler.lv2/README.txt       |   1 +
 plugins/eg04-sampler.lv2/click.wav        | Bin 0 -> 644 bytes
 plugins/eg04-sampler.lv2/manifest.ttl.in  |  13 +
 plugins/eg04-sampler.lv2/sampler.c        | 498 ++++++++++++++++++++++++++++++
 plugins/eg04-sampler.lv2/sampler.ttl      |  83 +++++
 plugins/eg04-sampler.lv2/sampler_ui.c     | 201 ++++++++++++
 plugins/eg04-sampler.lv2/uris.h           | 142 +++++++++
 plugins/eg04-sampler.lv2/waf              |   1 +
 plugins/eg04-sampler.lv2/wscript          |  80 +++++
 59 files changed, 2374 insertions(+), 2656 deletions(-)
 delete mode 100644 plugins/eg-amp.lv2/README.txt
 delete mode 100644 plugins/eg-amp.lv2/amp.c
 delete mode 100644 plugins/eg-amp.lv2/amp.ttl
 delete mode 100644 plugins/eg-amp.lv2/manifest.ttl.in
 delete mode 120000 plugins/eg-amp.lv2/waf
 delete mode 100644 plugins/eg-amp.lv2/wscript
 delete mode 100644 plugins/eg-metro.lv2/README.txt
 delete mode 100644 plugins/eg-metro.lv2/manifest.ttl.in
 delete mode 100644 plugins/eg-metro.lv2/metro.c
 delete mode 100644 plugins/eg-metro.lv2/metro.ttl
 delete mode 120000 plugins/eg-metro.lv2/waf
 delete mode 100644 plugins/eg-metro.lv2/wscript
 delete mode 100644 plugins/eg-midigate.lv2/README.txt
 delete mode 100644 plugins/eg-midigate.lv2/manifest.ttl.in
 delete mode 100644 plugins/eg-midigate.lv2/midigate.c
 delete mode 100644 plugins/eg-midigate.lv2/midigate.ttl
 delete mode 120000 plugins/eg-midigate.lv2/waf
 delete mode 100644 plugins/eg-midigate.lv2/wscript
 delete mode 100644 plugins/eg-sampler.lv2/README.txt
 delete mode 100644 plugins/eg-sampler.lv2/click.wav
 delete mode 100644 plugins/eg-sampler.lv2/manifest.ttl.in
 delete mode 100644 plugins/eg-sampler.lv2/sampler.c
 delete mode 100644 plugins/eg-sampler.lv2/sampler.ttl
 delete mode 100644 plugins/eg-sampler.lv2/sampler_ui.c
 delete mode 100644 plugins/eg-sampler.lv2/uris.h
 delete mode 120000 plugins/eg-sampler.lv2/waf
 delete mode 100644 plugins/eg-sampler.lv2/wscript
 delete mode 100644 plugins/eg-synth.lv2/manifest.ttl.in
 delete mode 100644 plugins/eg-synth.lv2/synth.c
 delete mode 100644 plugins/eg-synth.lv2/synth.ttl
 delete mode 120000 plugins/eg-synth.lv2/waf
 delete mode 100644 plugins/eg-synth.lv2/wscript
 create mode 100644 plugins/eg01-amp.lv2/README.txt
 create mode 100644 plugins/eg01-amp.lv2/amp.c
 create mode 100644 plugins/eg01-amp.lv2/amp.ttl
 create mode 100644 plugins/eg01-amp.lv2/manifest.ttl.in
 create mode 120000 plugins/eg01-amp.lv2/waf
 create mode 100644 plugins/eg01-amp.lv2/wscript
 create mode 100644 plugins/eg02-midigate.lv2/README.txt
 create mode 100644 plugins/eg02-midigate.lv2/manifest.ttl.in
 create mode 100644 plugins/eg02-midigate.lv2/midigate.c
 create mode 100644 plugins/eg02-midigate.lv2/midigate.ttl
 create mode 120000 plugins/eg02-midigate.lv2/waf
 create mode 100644 plugins/eg02-midigate.lv2/wscript
 create mode 100644 plugins/eg03-metro.lv2/README.txt
 create mode 100644 plugins/eg03-metro.lv2/manifest.ttl.in
 create mode 100644 plugins/eg03-metro.lv2/metro.c
 create mode 100644 plugins/eg03-metro.lv2/metro.ttl
 create mode 120000 plugins/eg03-metro.lv2/waf
 create mode 100644 plugins/eg03-metro.lv2/wscript
 create mode 100644 plugins/eg04-sampler.lv2/README.txt
 create mode 100644 plugins/eg04-sampler.lv2/click.wav
 create mode 100644 plugins/eg04-sampler.lv2/manifest.ttl.in
 create mode 100644 plugins/eg04-sampler.lv2/sampler.c
 create mode 100644 plugins/eg04-sampler.lv2/sampler.ttl
 create mode 100644 plugins/eg04-sampler.lv2/sampler_ui.c
 create mode 100644 plugins/eg04-sampler.lv2/uris.h
 create mode 120000 plugins/eg04-sampler.lv2/waf
 create mode 100644 plugins/eg04-sampler.lv2/wscript

(limited to 'plugins')

diff --git a/plugins/eg-amp.lv2/README.txt b/plugins/eg-amp.lv2/README.txt
deleted file mode 100644
index c2ab37d..0000000
--- a/plugins/eg-amp.lv2/README.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-== Simple Amplifier ==
-
-This plugin is a simple example of a basic LV2 plugin with no additional features.
-It has audio ports which contain an array of `float`,
-and control ports which contain a single `float`.
-
-LV2 plugins are defined in two parts: code and data.
-The code is written in C (or any C compatible language, such as C++) and defines the executable portions of the plugin.
-Static data is described separately in human and machine readable files in the http://www.w3.org/TeamSubmission/turtle/[Turtle] syntax.
-Turtle is a syntax for the RDF data model,
-but familiarity with RDF is not required to understand this documentation.
-
-Generally, code is kept minimal,
-and all static information is described in the data.
-There are several advantages to this approach:
-
- * Hosts can discover and inspect plugins without loading or executing any plugin code
- * It is simple to work with plugin data using scripting languages, command line tools, etc.
- * A standard format allows the re-use of existing vocabularies to describe plugins
- * The data inherently integrates with the web, databases, etc.
- * Labels and documentation are translatable, and available to hosts for display in user interfaces
diff --git a/plugins/eg-amp.lv2/amp.c b/plugins/eg-amp.lv2/amp.c
deleted file mode 100644
index 8dd7b4f..0000000
--- a/plugins/eg-amp.lv2/amp.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
-  Copyright 2006-2011 David Robillard <d@drobilla.net>
-  Copyright 2006 Steve Harris <steve@plugin.org.uk>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/** Include standard C headers */
-#include <math.h>
-#include <stdlib.h>
-
-/**
-   LV2 headers are based on the URI of the specification they come from, so a
-   consistent convention can be used even for unofficial extensions.  The URI
-   of the core LV2 specification is <http://lv2plug.in/ns/lv2core>, by
-   replacing `http:/` with `lv2` any header in the specification bundle can be
-   included, in this case `lv2.h`.
-*/
-#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
-
-/**
-   The URI is the identifier for a plugin, and how the host associates this
-   implementation in code with its description in data.  In this plugin it is
-   only used once in the code, but defining the plugin URI at the top of the
-   file is a good convention to follow.  If this URI does not match that used
-   in the data files, the host will fail to load the plugin.
-*/
-#define AMP_URI "http://lv2plug.in/plugins/eg-amp"
-
-/**
-   In code, ports are referred to by index.  An enumeration of port indices
-   should be defined for readability.
-*/
-typedef enum {
-	AMP_GAIN   = 0,
-	AMP_INPUT  = 1,
-	AMP_OUTPUT = 2
-} PortIndex;
-
-/**
-   Every plugin defines a private structure for the plugin instance.  All data
-   associated with a plugin instance is stored here, and is available to
-   every instance method.  In this simple plugin, only port buffers need to be
-   stored, since there is no additional instance data.  */
-typedef struct {
-	// Port buffers
-	const float* gain;
-	const float* input;
-	float*       output;
-} Amp;
-
-/**
-   The instantiate() function is called by the host to create a new plugin
-   instance.  The host passes the plugin descriptor, sample rate, and bundle
-   path for plugins that need to load additional resources (e.g. waveforms).
-   The features parameter contains host-provided features defined in LV2
-   extensions, but this simple plugin does not use any.
-
-   This function is in the ``instantiation'' threading class, so no other
-   methods on this instance will be called concurrently with it.
-*/
-static LV2_Handle
-instantiate(const LV2_Descriptor*     descriptor,
-            double                    rate,
-            const char*               bundle_path,
-            const LV2_Feature* const* features)
-{
-	Amp* amp = (Amp*)malloc(sizeof(Amp));
-
-	return (LV2_Handle)amp;
-}
-
-/**
-   The connect_port() method is called by the host to connect a particular port
-   to a buffer.  The plugin must store the data location, but data may not be
-   accessed except in run().
-
-   This method is in the ``audio'' threading class, and is called in the same
-   context as run().
-*/
-static void
-connect_port(LV2_Handle instance,
-             uint32_t   port,
-             void*      data)
-{
-	Amp* amp = (Amp*)instance;
-
-	switch ((PortIndex)port) {
-	case AMP_GAIN:
-		amp->gain = (const float*)data;
-		break;
-	case AMP_INPUT:
-		amp->input = (const float*)data;
-		break;
-	case AMP_OUTPUT:
-		amp->output = (float*)data;
-		break;
-	}
-}
-
-/**
-   The activate() method is called by the host to initialise and prepare the
-   plugin instance for running.  The plugin must reset all internal state
-   except for buffer locations set by connect_port().  Since this plugin has
-   no other internal state, this method does nothing.
-
-   This method is in the ``instantiation'' threading class, so no other
-   methods on this instance will be called concurrently with it.
-*/
-static void
-activate(LV2_Handle instance)
-{
-}
-
-/** Define a macro for converting a gain in dB to a coefficient */
-#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f)
-
-/** Process a block of audio (audio thread, must be RT safe). */
-static void
-run(LV2_Handle instance, uint32_t n_samples)
-{
-	const Amp* amp = (const Amp*)instance;
-
-	const float        gain   = *(amp->gain);
-	const float* const input  = amp->input;
-	float* const       output = amp->output;
-
-	const float coef = DB_CO(gain);
-
-	for (uint32_t pos = 0; pos < n_samples; pos++) {
-		output[pos] = input[pos] * coef;
-	}
-}
-
-/**
-   The deactivate() method is the counterpart to activate() called by the host
-   after running the plugin.  It indicates that the host will not call run()
-   again until another call to activate() and is mainly useful for more
-   advanced plugins with ``live'' characteristics such as those with auxiliary
-   processing threads.  As with activate(), this plugin has no use for this
-   information so this method does nothing.
-
-   This method is in the ``instantiation'' threading class, so no other
-   methods on this instance will be called concurrently with it.
-*/
-static void
-deactivate(LV2_Handle instance)
-{
-}
-
-/**
-   Destroy a plugin instance (counterpart to instantiate()).
-
-   This method is in the ``instantiation'' threading class, so no other
-   methods on this instance will be called concurrently with it.
-*/
-static void
-cleanup(LV2_Handle instance)
-{
-	free(instance);
-}
-
-/**
-   The extension_data function returns any extension data supported by the
-   plugin.  Note that this is not an instance method, but a function on the
-   plugin descriptor.  It is usually used by plugins to implement additional
-   interfaces.  This plugin does not have any extension data, so this function
-   returns NULL.
-
-   This method is in the ``discovery'' threading class, so no other functions
-   or methods in this plugin library will be called concurrently with it.
-*/
-static const void*
-extension_data(const char* uri)
-{
-	return NULL;
-}
-
-/**
-   Define the LV2_Descriptor for this plugin.  It is best to define descriptors
-   statically to avoid leaking memory and non-portable shared library
-   constructors and destructors to clean up properly.
-*/
-static const LV2_Descriptor descriptor = {
-	AMP_URI,
-	instantiate,
-	connect_port,
-	activate,
-	run,
-	deactivate,
-	cleanup,
-	extension_data
-};
-
-/**
-   The lv2_descriptor() function is the entry point to the plugin library.  The
-   host will load the library and call this function repeatedly with increasing
-   indices to find all the plugins defined in the library.  The index is not an
-   indentifier, the URI of the returned descriptor is used to determine the
-   identify of the plugin.
-   
-   This method is in the ``discovery'' threading class, so no other functions
-   or methods in this plugin library will be called concurrently with it.
-*/
-LV2_SYMBOL_EXPORT
-const LV2_Descriptor*
-lv2_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-amp.lv2/amp.ttl b/plugins/eg-amp.lv2/amp.ttl
deleted file mode 100644
index 25e4320..0000000
--- a/plugins/eg-amp.lv2/amp.ttl
+++ /dev/null
@@ -1,77 +0,0 @@
-@prefix doap: <http://usefulinc.com/ns/doap#> .
-@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
-@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-<http://lv2plug.in/plugins/eg-amp>
-	a lv2:Plugin ,
-		lv2:AmplifierPlugin ;
-# Plugins are associated with a project, where common information like
-# developers, home page, and so on are described.  This plugin is part of the
-# LV2 project, which has URI <http://lv2plug.in/ns/lv2>, and is described
-# elsewhere.  Typical plugin collections will describe the project in
-# manifest.ttl
-	lv2:project <http://lv2plug.in/ns/lv2> ;
-# Every plugin must have a name, described with the doap:name property.
-# Translations to various languages can be added by putting a language tag
-# after strings as shown.
-	doap:name "Simple Amplifier" ,
-		"简单放大器"@ch ,
-		"Einfacher Verstärker"@de ,
-		"Simple Amp"@en-gb ,
-		"Amplificador Simple"@es ,
-		"Amplificateur de Base"@fr ,
-		"Amplificatore Semplice"@it ,
-		"簡単なアンプ"@jp ,
-		"Просто Усилитель"@ru ;
-	doap:license <http://opensource.org/licenses/isc> ;
-	lv2:optionalFeature lv2:hardRTCapable ;
-	lv2:port [
-# Every port must have at least two types, one that specifies direction
-# (lv2:InputPort or lv2:OutputPort), and another to describe the data type.
-# This port is a lv2:ControlPort, which means it contains a single float.
-		a lv2:InputPort ,
-			lv2:ControlPort ;
-		lv2:index 0 ;
-		lv2:symbol "gain" ;
-		lv2:name "Gain" ,
-			"收益"@ch ,
-			"Gewinn"@de ,
-			"Gain"@en-gb ,
-			"Aumento"@es ,
-			"Gain"@fr ,
-			"Guadagno"@it ,
-			"利益"@jp ,
-			"Увеличение"@ru ;
-# An lv2:ControlPort should always describe its default value, and usually a
-# minimum and maximum value.  Defining a range is not strictly required, but
-# should be done wherever possible to aid host support, particularly for UIs.
-		lv2:default 0.0 ;
-		lv2:minimum -90.0 ;
-		lv2:maximum 24.0 ;
-		lv2:scalePoint [
-			rdfs:label "+5" ;
-			rdf:value 5.0
-		] , [
-			rdfs:label "0" ;
-			rdf:value 0.0
-		] , [
-			rdfs:label "-5" ;
-			rdf:value -5.0
-		] , [
-			rdfs:label "-10" ;
-			rdf:value -10.0
-		]
-	] , [
-		a lv2:AudioPort ,
-			lv2:InputPort ;
-		lv2:index 1 ;
-		lv2:symbol "in" ;
-		lv2:name "In"
-	] , [
-		a lv2:AudioPort ,
-			lv2:OutputPort ;
-		lv2:index 2 ;
-		lv2:symbol "out" ;
-		lv2:name "Out"
-	] .
diff --git a/plugins/eg-amp.lv2/manifest.ttl.in b/plugins/eg-amp.lv2/manifest.ttl.in
deleted file mode 100644
index 51f4a79..0000000
--- a/plugins/eg-amp.lv2/manifest.ttl.in
+++ /dev/null
@@ -1,101 +0,0 @@
-# LV2 Bundle Manifest
-#
-# All LV2 plugins are installed as "bundles", a directory with a particular
-# format.  Inside the bundle, the entry point is a file called "manifest.ttl".
-# This file lists what plugins are in this bundle, and which files are (.so,
-# .ttl, etc.) are associated with those plugins.
-#
-# Hosts read bundles' manifest.ttl to discover what plugins (and other
-# resources) are available.  Manifest files should be as small as possible for
-# performance reasons.
-#
-#
-# ==== Namespace Prefixes ====
-#
-# Turtle files often contain many URIs.  To make this more readable, prefixes
-# can be defined.  For example, with the `lv2:` prefix below, instead of
-# <http://lv2plug.in/ns/lv2core#Plugin> the shorter form `lv2:Plugin` can be
-# used.  This is just a shorthand for URIs within a file, the prefixes are not
-# significant otherwise.
-
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-# ==== Data ====
-
-<http://lv2plug.in/plugins/eg-amp>
-	a lv2:Plugin ;
-	lv2:binary <amp@LIB_EXT@>  ;
-	rdfs:seeAlso <amp.ttl> .
-
-# The token `@LIB_EXT@` above is replaced by the build system with the
-# appropriate extension for the current platform (e.g. .so, .dylib, .dll).
-# This file is called called `manifest.ttl.in` rather than `manifest.ttl`
-# to indicate that it is not the final file to be installed.
-# This is not necessary, but is a good idea for portable plugins.
-# For reability, the text will assume `.so` is the extension used.
-#
-# In short, this declares that the resource with URI
-# "http://lv2plug.in/plugins/eg-amp" is an LV2 plugin, with executable code in
-# the file "amp.so" and a full description in "amp.ttl".  These paths are
-# relative to the bundle directory.
-#
-# There are 3 statements in this description:
-# |================================================================
-# | Subject                             | Predicate    | Object
-# | <http://lv2plug.in/plugins/eg-amp>  | a            | lv2:Plugin
-# | <http://lv2plug.in/plugins/eg-amp>  | lv2:binary   | <amp.so>
-# | <http://lv2plug.in/plugins/eg-amp>  | rdfs:seeAlso | <amp.ttl>
-# |================================================================
-#
-# The semicolon is used to continue the previous subject; an equivalent
-# but more verbose syntax for the same data is:
-
-<http://lv2plug.in/plugins/eg-amp> a lv2:Plugin .
-<http://lv2plug.in/plugins/eg-amp> lv2:binary <amp.so> .
-<http://lv2plug.in/plugins/eg-amp> rdfs:seeAlso <amp.ttl> .
-
-# (Since this data is equivalent, it is safe, if pointless, to list it twice)
-#
-# Note that the documentation for a URI can often be found by visiting that URI
-# in a web browser, e.g. the documentation for lv2:binary can be found at
-# <http://lv2plug.in/ns/lv2core#binary>.  If you encounter a URI in some data
-# which you do not understand, try this first.
-#
-# Note the URI of a plugin does NOT need to be an actual web address, it's just
-# a global identifier.  It is, however, a good idea to use an actual web
-# address if possible, since it can be used to easily access documentation,
-# downloads, etc.  Note there are compatibility rules for when the URI of a
-# plugin must be changed, see the http://lv2plug.in/ns/lv2core[LV2 specification]
-# for details.
-#
-# AUTHORS MUST NOT CREATE URIS AT DOMAINS THEY DO NOT CONTROL WITHOUT
-# PERMISSION, AND *ESPECIALLY* MUST NOT CREATE SYNTACTICALLY INVALID URIS,
-# E.G. WHERE THE PORTION FOLLOWING "http://" IS NOT AN ACTUAL DOMAIN NAME.  If
-# you need an example URI, the domain http://example.org/ is reserved for this
-# purpose.  It is best to use web URIs, e.g. at the domain where plugins are
-# hosted for download, even if there is currently no documents hosted there.
-# If this is truly impossible, use a URN, e.g. urn:myplugs:superamp.
-#
-# A detailed explanation of each statement follows.
-
-<http://lv2plug.in/plugins/eg-amp> a lv2:Plugin .
-
-# The `a` is a Turtle shortcut for rdf:type and more or less means ``is a''.
-# `lv2:Plugin` expands to <http://lv2plug.in/ns/lv2core#Plugin> (using the
-# `lv2:` prefix above) which is the type of all LV2 plugins.
-# This statement means ``<http://lv2plug.in/plugins/eg-amp> is an LV2 plugin''.
-
-<http://lv2plug.in/plugins/eg-amp> lv2:binary <amp@LIB_EXT@> .
-
-# This says "this plugin has executable code ("binary") in the file
-# named "amp.so", which is located in this bundle.  The LV2 specification
-# defines that all relative URIs in manifest files are relative to the bundle
-# directory, so this refers to the file amp.so in the same directory as this
-# manifest.ttl file.
-
-<http://lv2plug.in/plugins/eg-amp> rdfs:seeAlso <amp.ttl> .
-
-# This says ``there is more information about this plugin located in the file
-# `amp.ttl`''.  The host will look at all such files when it needs to actually
-# use or investigate the plugin.
diff --git a/plugins/eg-amp.lv2/waf b/plugins/eg-amp.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-amp.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf
\ No newline at end of file
diff --git a/plugins/eg-amp.lv2/wscript b/plugins/eg-amp.lv2/wscript
deleted file mode 100644
index d4295ff..0000000
--- a/plugins/eg-amp.lv2/wscript
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-amp.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
-    opt.load('compiler_c')
-    autowaf.set_options(opt)
-
-def configure(conf):
-    conf.load('compiler_c')
-    autowaf.configure(conf)
-    autowaf.set_c99_mode(conf)
-    autowaf.display_header('Amp Configuration')
-
-    if not autowaf.is_child():
-        autowaf.check_pkg(conf, 'lv2', uselib_store='LV2')
-        
-    conf.check(features='c cprogram', lib='m', uselib_store='M', mandatory=False) 
-
-    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
-    print('')
-
-def build(bld):
-    bundle = 'eg-amp.lv2'
-
-    # Make a pattern for shared objects without the 'lib' prefix
-    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
-    module_ext = module_pat[module_pat.rfind('.'):]
-
-    # Build manifest.ttl by substitution (for portable lib extension)
-    bld(features     = 'subst',
-        source       = 'manifest.ttl.in',
-        target       = '%s/%s' % (bundle, 'manifest.ttl'),
-        install_path = '${LV2DIR}/%s' % bundle,
-        LIB_EXT      = module_ext)
-
-    # Copy other data files to build bundle (build/eg-amp.lv2)
-    for i in ['amp.ttl']:
-        bld(features     = 'subst',
-            is_copy      = True,
-            source       = i,
-            target       = '%s/%s' % (bundle, i),
-            install_path = '${LV2DIR}/%s' % bundle)
-
-    # Use LV2 headers from parent directory if building as a sub-project
-    includes = None
-    if autowaf.is_child:
-        includes = '../..'
-
-    # Build plugin library
-    obj = bld(features     = 'c cshlib',
-              source       = 'amp.c',
-              name         = 'amp',
-              target       = '%s/amp' % bundle,
-              install_path = '${LV2DIR}/%s' % bundle,
-              uselib       = 'M LV2',
-              includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
-
diff --git a/plugins/eg-metro.lv2/README.txt b/plugins/eg-metro.lv2/README.txt
deleted file mode 100644
index 5e9a84a..0000000
--- a/plugins/eg-metro.lv2/README.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-== Metronome ==
-
-This plugin demonstrates tempo synchronisation by clicking on every beat.  The
-host sends this information to the plugin as events, so an event with new time
-and tempo information will be received whenever there is a change.
-
-Time is assumed to continue rolling at the tempo and speed defined by the last
-received tempo event, even across cycles, until a new tempo event is received
-or the plugin is deactivated.
diff --git a/plugins/eg-metro.lv2/manifest.ttl.in b/plugins/eg-metro.lv2/manifest.ttl.in
deleted file mode 100644
index bd93f66..0000000
--- a/plugins/eg-metro.lv2/manifest.ttl.in
+++ /dev/null
@@ -1,7 +0,0 @@
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-<http://lv2plug.in/plugins/eg-metro>
-	a lv2:Plugin ;
-	lv2:binary <metro@LIB_EXT@> ;
-	rdfs:seeAlso <metro.ttl> .
diff --git a/plugins/eg-metro.lv2/metro.c b/plugins/eg-metro.lv2/metro.c
deleted file mode 100644
index 97821a1..0000000
--- a/plugins/eg-metro.lv2/metro.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
-  LV2 Metronome Example Plugin
-  Copyright 2012 David Robillard <d@drobilla.net>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef __cplusplus
-#    include <stdbool.h>
-#endif
-
-#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
-#include "lv2/lv2plug.in/ns/ext/atom/util.h"
-#include "lv2/lv2plug.in/ns/ext/time/time.h"
-#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
-#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
-
-#ifndef M_PI
-#    define M_PI 3.14159265
-#endif
-
-#define EG_METRO_URI "http://lv2plug.in/plugins/eg-metro"
-
-typedef struct {
-	LV2_URID atom_Blank;
-	LV2_URID atom_Float;
-	LV2_URID atom_Path;
-	LV2_URID atom_Resource;
-	LV2_URID atom_Sequence;
-	LV2_URID time_Position;
-	LV2_URID time_barBeat;
-	LV2_URID time_beatsPerMinute;
-	LV2_URID time_speed;
-} MetroURIs;
-
-static const double attack_s = 0.005;
-static const double decay_s  = 0.075;
-
-enum {
-	METRO_CONTROL = 0,
-	METRO_NOTIFY  = 1,
-	METRO_OUT     = 2
-};
-
-typedef enum {
-	STATE_ATTACK,
-	STATE_DECAY,
-	STATE_OFF
-} State;
-
-typedef struct {
-	/* Features */
-	LV2_URID_Map* map;
-
-	/* URIs */
-	MetroURIs uris;
-
-	/* Ports */
-	struct {
-		LV2_Atom_Sequence* control;
-		LV2_Atom_Sequence* notify;
-		float*             output;
-	} ports;
-
-	/** The rate, bpm, and speed are the basic information sent by the host. */
-	double   rate;
-	float    bpm;
-	float    speed;
-
-	/** To keep track of when to play the next click, we need to keep track of
-	    a few pieces of information: */
-
-	/** - The frames since the start of the last click is stored */
-	uint32_t elapsed_len;
-
-	/** - The current play offset in the wave */
-	uint32_t wave_offset;
-
-	/** - The current play state (attack, decay, or off) */
-	State state;
-
-	/** The wave to play is a simple sine wave generated at instantiation time
-	    based on the sample rate.  The length in frames is stored in order to
-	    continuously play the wave in a cycle to avoid discontinuity clicks. */
-	float*   wave;
-	uint32_t wave_len;
-
-	/** The continuously playing sine wave is enveloped to provide an actual
-	    metronome tick.  This plugin uses a simple AD envelope with fixed
-	    parameters.  A more sophisticated implementation might use a more
-	    advanced envelope and allow the user to modify these parameters. */
-	uint32_t attack_len;
-	uint32_t decay_len;
-} Metro;
-
-static void
-connect_port(LV2_Handle instance,
-             uint32_t   port,
-             void*      data)
-{
-	Metro* self = (Metro*)instance;
-
-	switch (port) {
-	case METRO_CONTROL:
-		self->ports.control = (LV2_Atom_Sequence*)data;
-		break;
-	case METRO_NOTIFY:
-		self->ports.notify = (LV2_Atom_Sequence*)data;
-		break;
-	case METRO_OUT:
-		self->ports.output = (float*)data;
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-activate(LV2_Handle instance)
-{
-	Metro* self = (Metro*)instance;
-
-	self->elapsed_len = 0;
-	self->wave_offset = 0;
-	self->state       = STATE_OFF;
-}
-
-static LV2_Handle
-instantiate(const LV2_Descriptor*     descriptor,
-            double                    rate,
-            const char*               path,
-            const LV2_Feature* const* features)
-{
-	Metro* self = (Metro*)calloc(1, sizeof(Metro));
-	if (!self) {
-		return NULL;
-	}
-
-	/** Scan host features for URID map */
-	LV2_URID_Map* map = NULL;
-	for (int i = 0; features[i]; ++i) {
-		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
-			map = (LV2_URID_Map*)features[i]->data;
-		}
-	}
-	if (!map) {
-		fprintf(stderr, "Host does not support urid:map.\n");
-		free(self);
-		return NULL;
-	}
-
-	/** Map URIS */
-	MetroURIs* const uris = &self->uris;
-	self->map = map;
-	uris->atom_Blank          = map->map(map->handle, LV2_ATOM__Blank);
-	uris->atom_Float          = map->map(map->handle, LV2_ATOM__Float);
-	uris->atom_Path           = map->map(map->handle, LV2_ATOM__Path);
-	uris->atom_Resource       = map->map(map->handle, LV2_ATOM__Resource);
-	uris->atom_Sequence       = map->map(map->handle, LV2_ATOM__Sequence);
-	uris->time_Position       = map->map(map->handle, LV2_TIME__Position);
-	uris->time_barBeat        = map->map(map->handle, LV2_TIME__barBeat);
-	uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
-	uris->time_speed          = map->map(map->handle, LV2_TIME__speed);
-
-	/** Initialise fields */
-	self->rate       = rate;
-	self->bpm        = 120.0f;
-	self->attack_len = attack_s * rate;
-	self->decay_len  = decay_s * rate;
-	self->state      = STATE_OFF;
-
-	/** Generate one cycle of a sine wave at the desired frequency. */
-	const double freq = 440.0 * 2.0;
-	const double amp  = 0.5;
-	self->wave_len = rate / freq;
-	self->wave     = (float*)malloc(self->wave_len * sizeof(float));
-	for (uint32_t i = 0; i < self->wave_len; ++i) {
-		self->wave[i] = sin(i * 2 * M_PI * freq / rate) * amp;
-	}
-
-	return (LV2_Handle)self;
-}
-
-static void
-cleanup(LV2_Handle instance)
-{
-	free(instance);
-}
-
-static void
-play(Metro* self, uint32_t begin, uint32_t end)
-{
-	float* const   output          = self->ports.output;
-	const uint32_t frames_per_beat = 60.0f / self->bpm * self->rate;
-
-	if (self->speed == 0.0f) {
-		memset(output, 0, (end - begin) * sizeof(float));
-		return;
-	}
-
-	for (uint32_t i = begin; i < end; ++i) {
-		switch (self->state) {
-		case STATE_ATTACK:
-			/** Amplitude increases from 0..1 until attack_len */
-			output[i] = self->wave[self->wave_offset] *
-				self->elapsed_len / (float)self->attack_len;
-			if (self->elapsed_len >= self->attack_len) {
-				self->state = STATE_DECAY;
-			}
-			break;
-		case STATE_DECAY:
-			/** Amplitude decreases from 1..0 until attack_len + decay_len */
-			output[i] = 0.0f;
-			output[i] = self->wave[self->wave_offset] *
-				(1 - ((self->elapsed_len - self->attack_len) /
-				      (float)self->decay_len));
-			if (self->elapsed_len >= self->attack_len + self->decay_len) {
-				self->state = STATE_OFF;
-			}
-			break;
-		case STATE_OFF:
-			output[i] = 0.0f;
-		}
-
-		/** We continuously play the sine wave regardless of envelope */
-		self->wave_offset = (self->wave_offset + 1) % self->wave_len;
-
-		/** Update elapsed time and start attack if necessary */
-		if (++self->elapsed_len == frames_per_beat) {
-			self->state       = STATE_ATTACK;
-			self->elapsed_len = 0;
-		}
-	}
-}
-
-static void
-update_position(Metro* self, const LV2_Atom_Object* obj)
-{
-	const MetroURIs* uris = &self->uris;
-
-	/** Received new transport position/speed */
-	LV2_Atom *beat = NULL, *bpm = NULL, *speed = NULL;
-	lv2_atom_object_get(obj,
-	                    uris->time_barBeat, &beat,
-	                    uris->time_beatsPerMinute, &bpm,
-	                    uris->time_speed, &speed,
-	                    NULL);
-	if (bpm && bpm->type == uris->atom_Float) {
-		/** Tempo changed, update BPM */
-		self->bpm = ((LV2_Atom_Float*)bpm)->body;
-	}
-	if (speed && speed->type == uris->atom_Float) {
-		/** Speed changed, e.g. 0 (stop) to 1 (play) */
-		self->speed = ((LV2_Atom_Float*)speed)->body;
-	}
-	if (beat && beat->type == uris->atom_Float) {
-		/** Received a beat position, synchronise.
-		    This is a simple hard sync that may cause clicks.
-		    A real plugin would do something more graceful.
-		*/
-		const float frames_per_beat = 60.0f / self->bpm * self->rate;
-		const float bar_beats       = ((LV2_Atom_Float*)beat)->body;
-		const float beat_beats      = bar_beats - floorf(bar_beats);
-		self->elapsed_len           = beat_beats * frames_per_beat;
-		if (self->elapsed_len < self->attack_len) {
-			self->state = STATE_ATTACK;
-		} else if (self->elapsed_len < self->attack_len + self->decay_len) {
-			self->state = STATE_DECAY;
-		} else {
-			self->state = STATE_OFF;
-		}
-	}
-}
-
-static void
-run(LV2_Handle instance, uint32_t sample_count)
-{
-	Metro*           self = (Metro*)instance;
-	const MetroURIs* uris = &self->uris;
-
-	/** Empty notify output for now */
-	LV2_Atom_Sequence* notify = self->ports.notify;
-	notify->atom.type = self->uris.atom_Sequence;
-	notify->atom.size = sizeof(LV2_Atom_Sequence_Body);
-	notify->body.unit = notify->body.pad = 0;
-
-	/** Work forwards in time frame by frame, handling events as we go */
-	const LV2_Atom_Sequence* in     = self->ports.control;
-	uint32_t                 last_t = 0;
-	for (LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body);
-	     !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev);
-	     ev = lv2_atom_sequence_next(ev)) {
-
-		/** Play the click for the time slice from last_t until now */
-		play(self, last_t, ev->time.frames);
-
-		if (ev->body.type == uris->atom_Blank) {
-			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
-			if (obj->body.otype == uris->time_Position) {
-				/** Received position information, update */
-				update_position(self, obj);
-			}
-		}
-
-		/** Update time for next iteration and move to next event*/
-		last_t = ev->time.frames;
-	}
-
-	/** Play for remainder of cycle */
-	play(self, last_t, sample_count);
-}
-
-static const LV2_Descriptor descriptor = {
-	EG_METRO_URI,
-	instantiate,
-	connect_port,
-	activate,
-	run,
-	NULL,  // deactivate,
-	cleanup,
-	NULL,  // extension_data
-};
-
-LV2_SYMBOL_EXPORT const LV2_Descriptor*
-lv2_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-metro.lv2/metro.ttl b/plugins/eg-metro.lv2/metro.ttl
deleted file mode 100644
index a6f297f..0000000
--- a/plugins/eg-metro.lv2/metro.ttl
+++ /dev/null
@@ -1,39 +0,0 @@
-@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
-@prefix doap: <http://usefulinc.com/ns/doap#> .
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix time: <http://lv2plug.in/ns/ext/time#> .
-@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
-
-<http://lv2plug.in/plugins/eg-metro>
-	a lv2:Plugin ;
-	doap:name "Example Metronome" ;
-	doap:license <http://opensource.org/licenses/isc> ;
-	lv2:project <http://lv2plug.in/ns/lv2> ;
-	lv2:requiredFeature urid:map ;
-	lv2:optionalFeature lv2:hardRTCapable ;
-	lv2:port [
-		a lv2:InputPort ,
-			atom:AtomPort ;
-		atom:bufferType atom:Sequence ;
-# Since this port supports time:Position, the host knows to deliver time and
-# tempo information
-		atom:supports time:Position ;
-		lv2:index 0 ;
-		lv2:symbol "control" ;
-		lv2:name "Control" ;
-	] , [
-		a lv2:OutputPort ,
-			atom:AtomPort ;
-		atom:bufferType atom:Sequence ;
-		atom:supports <http://lv2plug.in/ns/ext/patch#Patch> ;
-		lv2:portProperty lv2:connectionOptional ;
-		lv2:index 1 ;
-		lv2:symbol "notify" ;
-		lv2:name "Notify" ;
-	] , [
-		a lv2:AudioPort ,
-			lv2:OutputPort ;
-		lv2:index 2 ;
-		lv2:symbol "out" ;
-		lv2:name "Out" ;
-	] .
diff --git a/plugins/eg-metro.lv2/waf b/plugins/eg-metro.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-metro.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf
\ No newline at end of file
diff --git a/plugins/eg-metro.lv2/wscript b/plugins/eg-metro.lv2/wscript
deleted file mode 100644
index 40642b6..0000000
--- a/plugins/eg-metro.lv2/wscript
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-metro.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
-    opt.load('compiler_c')
-    autowaf.set_options(opt)
-
-def configure(conf):
-    conf.load('compiler_c')
-    autowaf.configure(conf)
-    autowaf.set_c99_mode(conf)
-    autowaf.display_header('Metro Configuration')
-
-    if not autowaf.is_child():
-        autowaf.check_pkg(conf, 'lv2', atleast_version='0.2.0', uselib_store='LV2')
-
-    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
-    print('')
-
-def build(bld):
-    bundle = 'eg-metro.lv2'
-
-    # Make a pattern for shared objects without the 'lib' prefix
-    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
-    module_ext = module_pat[module_pat.rfind('.'):]
-
-    # Build manifest.ttl by substitution (for portable lib extension)
-    bld(features     = 'subst',
-        source       = 'manifest.ttl.in',
-        target       = '%s/%s' % (bundle, 'manifest.ttl'),
-        install_path = '${LV2DIR}/%s' % bundle,
-        LIB_EXT      = module_ext)
-    
-    # Copy other data files to build bundle (build/eg-metro.lv2)
-    bld(features     = 'subst',
-        is_copy      = True,
-        source       = 'metro.ttl',
-        target       = '%s/metro.ttl' % bundle,
-        install_path = '${LV2DIR}/%s' % bundle)
-
-    # Use LV2 headers from parent directory if building as a sub-project
-    includes = ['.']
-    if autowaf.is_child:
-        includes += ['../..']
-
-    # Build plugin library
-    obj = bld(features     = 'c cshlib',
-              source       = 'metro.c',
-              name         = 'metro',
-              target       = '%s/metro' % bundle,
-              install_path = '${LV2DIR}/%s' % bundle,
-              use          = 'LV2',
-              includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
-
diff --git a/plugins/eg-midigate.lv2/README.txt b/plugins/eg-midigate.lv2/README.txt
deleted file mode 100644
index 8f4a0f0..0000000
--- a/plugins/eg-midigate.lv2/README.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-== MIDI Gate ==
-
-This plugin demonstrates:
-
- * Receiving MIDI input
-
- * Processing audio based on MIDI events with sample accuracy
-
- * Supporting MIDI programs which the host can control/automate, or present a
-   user interface for with human readable labels
diff --git a/plugins/eg-midigate.lv2/manifest.ttl.in b/plugins/eg-midigate.lv2/manifest.ttl.in
deleted file mode 100644
index d32f1dc..0000000
--- a/plugins/eg-midigate.lv2/manifest.ttl.in
+++ /dev/null
@@ -1,10 +0,0 @@
-# The manifest.ttl file follows the same template as the previous example.
-
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
-
-<http://lv2plug.in/plugins/eg-midigate>
-	a lv2:Plugin ;
-	lv2:binary <midigate@LIB_EXT@> ;
-	rdfs:seeAlso <midigate.ttl> .
diff --git a/plugins/eg-midigate.lv2/midigate.c b/plugins/eg-midigate.lv2/midigate.c
deleted file mode 100644
index 3b74bfc..0000000
--- a/plugins/eg-midigate.lv2/midigate.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
-  Copyright 2013 David Robillard <d@drobilla.net>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include <stdio.h>
-
-#include <math.h>
-#include <stdlib.h>
-
-#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
-#include "lv2/lv2plug.in/ns/ext/atom/util.h"
-#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
-#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
-#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
-
-#define MIDIGATE_URI "http://lv2plug.in/plugins/eg-midigate"
-
-typedef enum {
-	MIDIGATE_CONTROL = 0,
-	MIDIGATE_IN      = 1,
-	MIDIGATE_OUT     = 2
-} PortIndex;
-
-typedef struct {
-	// Port buffers
-	const LV2_Atom_Sequence* control;
-	const float*             in;
-	float*                   out;
-
-	// Features
-	LV2_URID_Map* map;
-
-	struct {
-		LV2_URID midi_MidiEvent;
-	} uris;
-
-	unsigned n_active_notes;
-	unsigned program;
-} Midigate;
-
-static LV2_Handle
-instantiate(const LV2_Descriptor*     descriptor,
-            double                    rate,
-            const char*               bundle_path,
-            const LV2_Feature* const* features)
-{
-	/** Scan features array for the URID feature we need. */
-	LV2_URID_Map* map = NULL;
-	for (int i = 0; features[i]; ++i) {
-		if (!strcmp(features[i]->URI, LV2_URID__map)) {
-			map = (LV2_URID_Map*)features[i]->data;
-			break;
-		}
-	}
-	if (!map) {
-		/**
-		   No URID feature given.  This is a host bug since we require this
-		   feature, but should be handled gracefully anyway.
-		*/
-		return NULL;
-	}
-
-	Midigate* self = (Midigate*)calloc(1, sizeof(Midigate));
-	self->map = map;
-	self->uris.midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
-
-	return (LV2_Handle)self;
-}
-
-static void
-connect_port(LV2_Handle instance,
-             uint32_t   port,
-             void*      data)
-{
-	Midigate* self = (Midigate*)instance;
-
-	switch ((PortIndex)port) {
-	case MIDIGATE_CONTROL:
-		self->control = (const LV2_Atom_Sequence*)data;
-		break;
-	case MIDIGATE_IN:
-		self->in = (const float*)data;
-		break;
-	case MIDIGATE_OUT:
-		self->out = (float*)data;
-		break;
-	}
-}
-
-static void
-activate(LV2_Handle instance)
-{
-	Midigate* self = (Midigate*)instance;
-	self->n_active_notes = 0;
-	self->program        = 0;
-}
-
-/**
-   A function to write a chunk of output, to be called from run().  If the gate
-   is high, then the input will be passed through for this chunk, otherwise
-   silence is written.
-*/
-static void
-write_output(Midigate* self, uint32_t offset, uint32_t len)
-{
-	const bool active = (self->program == 0)
-		? (self->n_active_notes > 0)
-		: (self->n_active_notes == 0);
-	if (active) {
-		memcpy(self->out + offset, self->in + offset, len * sizeof(float));
-	} else {
-		memset(self->out + offset, 0, len * sizeof(float));
-	}
-}
-
-/**
-   This plugin works through the cycle in chunks starting at offset zero.  The
-   +offset+ represents the current time within this this cycle, so
-   the output from 0 to +offset+ has already been written.
-
-   MIDI events are read in a loop.  In each iteration, the number of active
-   notes (on note on and note off) or the program (on program change) is
-   updated, then the output is written up until the current event time.  Then
-   +offset+ is updated and the next event is processed.  After the loop the
-   final chunk from the last event to the end of the cycle is emitted.
-
-   This pattern of iterating over input events and writing output along the way
-   is a common idiom for writing sample accurate output based on event input.
-
-   Note that this simple example simply writes input or zero for each sample
-   based on the gate.  A serious implementation would need to envelope the
-   transition to avoid aliasing.
-*/
-static void
-run(LV2_Handle instance, uint32_t sample_count)
-{
-	Midigate* self   = (Midigate*)instance;
-	uint32_t  offset = 0;
-
-	LV2_ATOM_SEQUENCE_FOREACH(self->control, ev) {
-		if (ev->body.type == self->uris.midi_MidiEvent) {
-			const uint8_t* const msg = (const uint8_t*)(ev + 1);
-			switch (lv2_midi_message_type(msg)) {
-			case LV2_MIDI_MSG_NOTE_ON:
-				++self->n_active_notes;
-				break;
-			case LV2_MIDI_MSG_NOTE_OFF:
-				--self->n_active_notes;
-				break;
-			case LV2_MIDI_MSG_PGM_CHANGE:
-				if (msg[1] == 0 || msg[1] == 1) {
-					self->program = msg[1];
-				}
-				break;
-			default: break;
-			}
-		}
-
-		write_output(self, offset, ev->time.frames - offset);
-		offset = ev->time.frames;
-	}
-
-	write_output(self, offset, sample_count - offset);
-}
-
-/**
-   We have no resources to free on deactivation.
-   Note that the next call to activate will re-initialise the state, namely
-   self->n_active_notes, so there is no need to do so here.
-*/
-static void
-deactivate(LV2_Handle instance)
-{}
-
-static void
-cleanup(LV2_Handle instance)
-{
-	free(instance);
-}
-
-/**
-   This plugin also has no extension data to return.
-*/
-static const void*
-extension_data(const char* uri)
-{
-	return NULL;
-}
-
-static const LV2_Descriptor descriptor = {
-	MIDIGATE_URI,
-	instantiate,
-	connect_port,
-	activate,
-	run,
-	deactivate,
-	cleanup,
-	extension_data
-};
-
-LV2_SYMBOL_EXPORT
-const LV2_Descriptor*
-lv2_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-midigate.lv2/midigate.ttl b/plugins/eg-midigate.lv2/midigate.ttl
deleted file mode 100644
index 59ac815..0000000
--- a/plugins/eg-midigate.lv2/midigate.ttl
+++ /dev/null
@@ -1,80 +0,0 @@
-# The same set of namespace prefixes with two additions for LV2 extensions this
-# plugin uses: atom and urid.
-
-@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
-@prefix doap: <http://usefulinc.com/ns/doap#> .
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
-
-<http://lv2plug.in/plugins/eg-midigate>
-	a lv2:Plugin ;
-	doap:name "Example MIDI Gate" ;
-	doap:license <http://opensource.org/licenses/isc> ;
-	lv2:project <http://lv2plug.in/ns/lv2> ;
-	lv2:requiredFeature urid:map ;
-	lv2:optionalFeature lv2:hardRTCapable ;
-# Describe program banks so the host can automate and/or present a user
-# interface.  Describing supported programs (or any other MIDI event) is not
-# required, but is a good idea since it allows hosts to make better use of
-# plugins.  This plugin has a single bank of two programs, which have a
-# (mandatory) label, and an (optional) comment to describe their meaning in
-# more detail.
-#
-# Both programs and the bank have an index, which corresponds to the MIDI bank
-# and program numbers that will activate them.  Since there are other ways to
-# change programs (not used here), an index is not strictly required, but must
-# be present to support program changes from MIDI.
-	lv2:bank [
-		rdfs:label "Default" ;
-		lv2:index 0 ;
-		lv2:program [
-			lv2:index 0 ;
-			rdfs:label "Normal" ;
-			rdfs:comment "Input is passed through if notes are active."
-		] , [
-			lv2:index 1 ;
-			rdfs:label "Inverted" ;
-			rdfs:comment "Input is passed through if no notes are active."
-		]
-	] ;
-# This plugin has three ports.  There is an audio input and output as before,
-# as well as a new AtomPort.  An AtomPort buffer contains an Atom, which is a
-# generic container for any type of data.  In this case, we want to receive
-# MIDI events, so the (mandatory) +atom:bufferType+ is atom:Sequence, which is
-# a series of events with time stamps.
-#
-# Events themselves are also generic and can contain any type of data, but in
-# this case we are only interested in MIDI events.  The (optional)
-# +atom:supports+ property describes which event types are supported.  Though
-# not required, this information should always be given so the host knows what
-# types of event it can expect the plugin to understand.
-#
-# The (optional) +lv2:designation+ of this port is +lv2:control+, which
-# indicates that this is the "main" control port where the host should send
-# events it expects to configure the plugin, in this case changing the MIDI
-# program.  This is necessary since it is possible to have several MIDI input
-# ports, though typically it is best to have one.
-	lv2:port [
-		a lv2:InputPort ,
-			atom:AtomPort ;
-		atom:bufferType atom:Sequence ;
-		atom:supports midi:MidiEvent ;
-		lv2:designation lv2:control ;
-		lv2:index 0 ;
-		lv2:symbol "control" ;
-		lv2:name "Control"
-	] , [
-		a lv2:AudioPort ,
-			lv2:InputPort ;
-		lv2:index 1 ;
-		lv2:symbol "in" ;
-		lv2:name "In"
-	] , [
-		a lv2:AudioPort ,
-			lv2:OutputPort ;
-		lv2:index 2 ;
-		lv2:symbol "out" ;
-		lv2:name "Out"
-	] .
diff --git a/plugins/eg-midigate.lv2/waf b/plugins/eg-midigate.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-midigate.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf
\ No newline at end of file
diff --git a/plugins/eg-midigate.lv2/wscript b/plugins/eg-midigate.lv2/wscript
deleted file mode 100644
index 44336af..0000000
--- a/plugins/eg-midigate.lv2/wscript
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-midigate.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
-    opt.load('compiler_c')
-    autowaf.set_options(opt)
-
-def configure(conf):
-    conf.load('compiler_c')
-    autowaf.configure(conf)
-    autowaf.set_c99_mode(conf)
-    autowaf.display_header('Midigate Configuration')
-
-    if not autowaf.is_child():
-        autowaf.check_pkg(conf, 'lv2', uselib_store='LV2')
-        
-    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
-    print('')
-
-def build(bld):
-    bundle = 'eg-midigate.lv2'
-
-    # Make a pattern for shared objects without the 'lib' prefix
-    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
-    module_ext = module_pat[module_pat.rfind('.'):]
-
-    # Build manifest.ttl by substitution (for portable lib extension)
-    bld(features     = 'subst',
-        source       = 'manifest.ttl.in',
-        target       = '%s/%s' % (bundle, 'manifest.ttl'),
-        install_path = '${LV2DIR}/%s' % bundle,
-        LIB_EXT      = module_ext)
-
-    # Copy other data files to build bundle (build/eg-midigate.lv2)
-    for i in ['midigate.ttl']:
-        bld(features     = 'subst',
-            is_copy      = True,
-            source       = i,
-            target       = '%s/%s' % (bundle, i),
-            install_path = '${LV2DIR}/%s' % bundle)
-
-    # Use LV2 headers from parent directory if building as a sub-project
-    includes = None
-    if autowaf.is_child:
-        includes = '../..'
-
-    # Build plugin library
-    obj = bld(features     = 'c cshlib',
-              source       = 'midigate.c',
-              name         = 'midigate',
-              target       = '%s/midigate' % bundle,
-              install_path = '${LV2DIR}/%s' % bundle,
-              uselib       = 'LV2',
-              includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
-
diff --git a/plugins/eg-sampler.lv2/README.txt b/plugins/eg-sampler.lv2/README.txt
deleted file mode 100644
index c1cac46..0000000
--- a/plugins/eg-sampler.lv2/README.txt
+++ /dev/null
@@ -1 +0,0 @@
-== Sampler ==
diff --git a/plugins/eg-sampler.lv2/click.wav b/plugins/eg-sampler.lv2/click.wav
deleted file mode 100644
index 520a18c..0000000
Binary files a/plugins/eg-sampler.lv2/click.wav and /dev/null differ
diff --git a/plugins/eg-sampler.lv2/manifest.ttl.in b/plugins/eg-sampler.lv2/manifest.ttl.in
deleted file mode 100644
index b4fa23e..0000000
--- a/plugins/eg-sampler.lv2/manifest.ttl.in
+++ /dev/null
@@ -1,13 +0,0 @@
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
-
-<http://lv2plug.in/plugins/eg-sampler>
-	a lv2:Plugin ;
-	lv2:binary <sampler@LIB_EXT@> ;
-	rdfs:seeAlso <sampler.ttl> .
-
-<http://lv2plug.in/plugins/eg-sampler#ui>
-	a ui:GtkUI ;
-	ui:binary <sampler_ui@LIB_EXT@> ;
-	rdfs:seeAlso <sampler.ttl> .
diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c
deleted file mode 100644
index 5bb4e54..0000000
--- a/plugins/eg-sampler.lv2/sampler.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
-  LV2 Sampler Example Plugin
-  Copyright 2011-2012 David Robillard <d@drobilla.net>
-  Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
-  Copyright 2011 James Morris <jwm.art.net@gmail.com>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
-   @file sampler.c Sampler Plugin
-
-   A simple example of an LV2 sampler that dynamically loads a single sample
-   (based on incoming events) and triggers their playback (based on incoming
-   MIDI note events).
-
-   This plugin illustrates:
-   - UI <=> Plugin communication via events
-   - Use of the worker extension for non-realtime tasks (sample loading)
-   - Use of the log extension to print log messages via the host
-   - Saving plugin state via the state extension
-   - Dynamic plugin control via the same properties saved to state
-*/
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#ifndef __cplusplus
-#    include <stdbool.h>
-#endif
-
-#include <sndfile.h>
-
-#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
-#include "lv2/lv2plug.in/ns/ext/atom/util.h"
-#include "lv2/lv2plug.in/ns/ext/log/log.h"
-#include "lv2/lv2plug.in/ns/ext/log/logger.h"
-#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
-#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
-#include "lv2/lv2plug.in/ns/ext/state/state.h"
-#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
-#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
-#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
-
-#include "./uris.h"
-
-enum {
-	SAMPLER_CONTROL = 0,
-	SAMPLER_NOTIFY  = 1,
-	SAMPLER_OUT     = 2
-};
-
-static const char* default_sample_file = "click.wav";
-
-typedef struct {
-	SF_INFO info;      /**< Info about sample from sndfile */
-	float*  data;      /**< Sample data in float */
-	char*   path;      /**< Path of file */
-	size_t  path_len;  /**< Length of path */
-} Sample;
-
-typedef struct {
-	/* Features */
-	LV2_URID_Map*        map;
-	LV2_Worker_Schedule* schedule;
-	LV2_Log_Log*         log;
-
-	/* Forge for creating atoms */
-	LV2_Atom_Forge forge;
-
-	/* Logger convenience API */
-	LV2_Log_Logger logger;
-
-	/* Sample */
-	Sample* sample;
-
-	/* Ports */
-	const LV2_Atom_Sequence* control_port;
-	LV2_Atom_Sequence*       notify_port;
-	float*                   output_port;
-
-	/* Forge frame for notify port (for writing worker replies). */
-	LV2_Atom_Forge_Frame notify_frame;
-
-	/* URIs */
-	SamplerURIs uris;
-
-	/* Current position in run() */
-	uint32_t frame_offset;
-
-	/* Playback state */
-	sf_count_t frame;
-	bool       play;
-} Sampler;
-
-/**
-   An atom-like message used internally to apply/free samples.
-
-   This is only used internally to communicate with the worker, it is never
-   sent to the outside world via a port since it is not POD.  It is convenient
-   to use an Atom header so actual atoms can be easily sent through the same
-   ringbuffer.
-*/
-typedef struct {
-	LV2_Atom atom;
-	Sample*  sample;
-} SampleMessage;
-
-/**
-   Load a new sample and return it.
-
-   Since this is of course not a real-time safe action, this is called in the
-   worker thread only.  The sample is loaded and returned only, plugin state is
-   not modified.
-*/
-static Sample*
-load_sample(Sampler* self, const char* path)
-{
-	const size_t path_len  = strlen(path);
-
-	lv2_log_trace(&self->logger, "Loading sample %s\n", path);
-
-	Sample* const  sample  = (Sample*)malloc(sizeof(Sample));
-	SF_INFO* const info    = &sample->info;
-	SNDFILE* const sndfile = sf_open(path, SFM_READ, info);
-
-	if (!sndfile || !info->frames || (info->channels != 1)) {
-		lv2_log_error(&self->logger, "Failed to open sample '%s'\n", path);
-		free(sample);
-		return NULL;
-	}
-
-	/* Read data */
-	float* const data = malloc(sizeof(float) * info->frames);
-	if (!data) {
-		lv2_log_error(&self->logger, "Failed to allocate memory for sample\n");
-		return NULL;
-	}
-	sf_seek(sndfile, 0ul, SEEK_SET);
-	sf_read_float(sndfile, data, info->frames);
-	sf_close(sndfile);
-
-	/* Fill sample struct and return it. */
-	sample->data     = data;
-	sample->path     = (char*)malloc(path_len + 1);
-	sample->path_len = path_len;
-	memcpy(sample->path, path, path_len + 1);
-
-	return sample;
-}
-
-static void
-free_sample(Sampler* self, Sample* sample)
-{
-	if (sample) {
-		lv2_log_trace(&self->logger, "Freeing %s\n", sample->path);
-		free(sample->path);
-		free(sample->data);
-		free(sample);
-	}
-}
-
-/**
-   Do work in a non-realtime thread.
-
-   This is called for every piece of work scheduled in the audio thread using
-   self->schedule->schedule_work().  A reply can be sent back to the audio
-   thread using the provided respond function.
-*/
-static LV2_Worker_Status
-work(LV2_Handle                  instance,
-     LV2_Worker_Respond_Function respond,
-     LV2_Worker_Respond_Handle   handle,
-     uint32_t                    size,
-     const void*                 data)
-{
-	Sampler*        self = (Sampler*)instance;
-	const LV2_Atom* atom = (const LV2_Atom*)data;
-	if (atom->type == self->uris.eg_freeSample) {
-		/* Free old sample */
-		const SampleMessage* msg = (const SampleMessage*)data;
-		free_sample(self, msg->sample);
-	} else {
-		/* Handle set message (load sample). */
-		const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data;
-
-		/* Get file path from message */
-		const LV2_Atom* file_path = read_set_file(&self->uris, obj);
-		if (!file_path) {
-			return LV2_WORKER_ERR_UNKNOWN;
-		}
-
-		/* Load sample. */
-		Sample* sample = load_sample(self, LV2_ATOM_BODY_CONST(file_path));
-		if (sample) {
-			/* Loaded sample, send it to run() to be applied. */
-			respond(handle, sizeof(sample), &sample);
-		}
-	}
-
-	return LV2_WORKER_SUCCESS;
-}
-
-/**
-   Handle a response from work() in the audio thread.
-
-   When running normally, this will be called by the host after run().  When
-   freewheeling, this will be called immediately at the point the work was
-   scheduled.
-*/
-static LV2_Worker_Status
-work_response(LV2_Handle  instance,
-              uint32_t    size,
-              const void* data)
-{
-	Sampler* self = (Sampler*)instance;
-
-	SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample },
-	                      self->sample };
-
-	/* Send a message to the worker to free the current sample */
-	self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg);
-
-	/* Install the new sample */
-	self->sample = *(Sample*const*)data;
-
-	/* Send a notification that we're using a new sample. */
-	lv2_atom_forge_frame_time(&self->forge, self->frame_offset);
-	write_set_file(&self->forge, &self->uris,
-	               self->sample->path,
-	               self->sample->path_len);
-
-	return LV2_WORKER_SUCCESS;
-}
-
-static void
-connect_port(LV2_Handle instance,
-             uint32_t   port,
-             void*      data)
-{
-	Sampler* self = (Sampler*)instance;
-	switch (port) {
-	case SAMPLER_CONTROL:
-		self->control_port = (const LV2_Atom_Sequence*)data;
-		break;
-	case SAMPLER_NOTIFY:
-		self->notify_port = (LV2_Atom_Sequence*)data;
-		break;
-	case SAMPLER_OUT:
-		self->output_port = (float*)data;
-		break;
-	default:
-		break;
-	}
-}
-
-static LV2_Handle
-instantiate(const LV2_Descriptor*     descriptor,
-            double                    rate,
-            const char*               path,
-            const LV2_Feature* const* features)
-{
-	/* Allocate and initialise instance structure. */
-	Sampler* self = (Sampler*)malloc(sizeof(Sampler));
-	if (!self) {
-		return NULL;
-	}
-	memset(self, 0, sizeof(Sampler));
-
-	/* Get host features */
-	for (int i = 0; features[i]; ++i) {
-		if (!strcmp(features[i]->URI, LV2_URID__map)) {
-			self->map = (LV2_URID_Map*)features[i]->data;
-		} else if (!strcmp(features[i]->URI, LV2_WORKER__schedule)) {
-			self->schedule = (LV2_Worker_Schedule*)features[i]->data;
-		} else if (!strcmp(features[i]->URI, LV2_LOG__log)) {
-			self->log = (LV2_Log_Log*)features[i]->data;
-		}
-	}
-	if (!self->map) {
-		lv2_log_error(&self->logger, "Missing feature urid:map\n");
-		goto fail;
-	} else if (!self->schedule) {
-		lv2_log_error(&self->logger, "Missing feature work:schedule\n");
-		goto fail;
-	}
-
-	/* Map URIs and initialise forge/logger */
-	map_sampler_uris(self->map, &self->uris);
-	lv2_atom_forge_init(&self->forge, self->map);
-	lv2_log_logger_init(&self->logger, self->map, self->log);
-
-	/* Load the default sample file */
-	const size_t path_len    = strlen(path);
-	const size_t file_len    = strlen(default_sample_file);
-	const size_t len         = path_len + file_len;
-	char*        sample_path = (char*)malloc(len + 1);
-	snprintf(sample_path, len + 1, "%s%s", path, default_sample_file);
-	self->sample = load_sample(self, sample_path);
-	free(sample_path);
-
-	return (LV2_Handle)self;
-
-fail:
-	free(self);
-	return 0;
-}
-
-static void
-cleanup(LV2_Handle instance)
-{
-	Sampler* self = (Sampler*)instance;
-	free_sample(self, self->sample);
-	free(self);
-}
-
-static void
-run(LV2_Handle instance,
-    uint32_t   sample_count)
-{
-	Sampler*     self        = (Sampler*)instance;
-	SamplerURIs* uris        = &self->uris;
-	sf_count_t   start_frame = 0;
-	sf_count_t   pos         = 0;
-	float*       output      = self->output_port;
-
-	/* Set up forge to write directly to notify output port. */
-	const uint32_t notify_capacity = self->notify_port->atom.size;
-	lv2_atom_forge_set_buffer(&self->forge,
-	                          (uint8_t*)self->notify_port,
-	                          notify_capacity);
-
-	/* Start a sequence in the notify output port. */
-	lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0);
-
-	/* Read incoming events */
-	LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) {
-		self->frame_offset = ev->time.frames;
-		if (ev->body.type == uris->midi_Event) {
-			const uint8_t* const msg = (const uint8_t*)(ev + 1);
-			switch (lv2_midi_message_type(msg)) {
-			case LV2_MIDI_MSG_NOTE_ON:
-				start_frame = ev->time.frames;
-				self->frame = 0;
-				self->play  = true;
-				break;
-			default:
-				break;
-			}
-		} else if (is_object_type(uris, ev->body.type)) {
-			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
-			if (obj->body.otype == uris->patch_Set) {
-				/* Received a set message, send it to the worker. */
-				lv2_log_trace(&self->logger, "Queueing set message\n");
-				self->schedule->schedule_work(self->schedule->handle,
-				                              lv2_atom_total_size(&ev->body),
-				                              &ev->body);
-			} else {
-				lv2_log_trace(&self->logger,
-				              "Unknown object type %d\n", obj->body.otype);
-			}
-		} else {
-			lv2_log_trace(&self->logger,
-			              "Unknown event type %d\n", ev->body.type);
-		}
-	}
-
-	/* Render the sample (possibly already in progress) */
-	if (self->play) {
-		uint32_t       f  = self->frame;
-		const uint32_t lf = self->sample->info.frames;
-
-		for (pos = 0; pos < start_frame; ++pos) {
-			output[pos] = 0;
-		}
-
-		for (; pos < sample_count && f < lf; ++pos, ++f) {
-			output[pos] = self->sample->data[f];
-		}
-
-		self->frame = f;
-
-		if (f == lf) {
-			self->play = false;
-		}
-	}
-
-	/* Add zeros to end if sample not long enough (or not playing) */
-	for (; pos < sample_count; ++pos) {
-		output[pos] = 0.0f;
-	}
-}
-
-static LV2_State_Status
-save(LV2_Handle                instance,
-     LV2_State_Store_Function  store,
-     LV2_State_Handle          handle,
-     uint32_t                  flags,
-     const LV2_Feature* const* features)
-{
-	Sampler* self = (Sampler*)instance;
-	if (!self->sample) {
-		return LV2_STATE_SUCCESS;
-	}
-
-	LV2_State_Map_Path* map_path = NULL;
-	for (int i = 0; features[i]; ++i) {
-		if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) {
-			map_path = (LV2_State_Map_Path*)features[i]->data;
-		}
-	}
-
-	char* apath = map_path->abstract_path(map_path->handle, self->sample->path);
-
-	store(handle,
-	      self->uris.eg_sample,
-	      apath,
-	      strlen(self->sample->path) + 1,
-	      self->uris.atom_Path,
-	      LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
-
-	free(apath);
-
-	return LV2_STATE_SUCCESS;
-}
-
-static LV2_State_Status
-restore(LV2_Handle                  instance,
-        LV2_State_Retrieve_Function retrieve,
-        LV2_State_Handle            handle,
-        uint32_t                    flags,
-        const LV2_Feature* const*   features)
-{
-	Sampler* self = (Sampler*)instance;
-
-	size_t   size;
-	uint32_t type;
-	uint32_t valflags;
-
-	const void* value = retrieve(
-		handle,
-		self->uris.eg_sample,
-		&size, &type, &valflags);
-
-	if (value) {
-		const char* path = (const char*)value;
-		lv2_log_trace(&self->logger, "Restoring file %s\n", path);
-		free_sample(self, self->sample);
-		self->sample = load_sample(self, path);
-	}
-
-	return LV2_STATE_SUCCESS;
-}
-
-static const void*
-extension_data(const char* uri)
-{
-	static const LV2_State_Interface  state  = { save, restore };
-	static const LV2_Worker_Interface worker = { work, work_response, NULL };
-	if (!strcmp(uri, LV2_STATE__interface)) {
-		return &state;
-	} else if (!strcmp(uri, LV2_WORKER__interface)) {
-		return &worker;
-	}
-	return NULL;
-}
-
-static const LV2_Descriptor descriptor = {
-	EG_SAMPLER_URI,
-	instantiate,
-	connect_port,
-	NULL,  // activate,
-	run,
-	NULL,  // deactivate,
-	cleanup,
-	extension_data
-};
-
-LV2_SYMBOL_EXPORT
-const LV2_Descriptor* lv2_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl
deleted file mode 100644
index e008de0..0000000
--- a/plugins/eg-sampler.lv2/sampler.ttl
+++ /dev/null
@@ -1,83 +0,0 @@
-# LV2 Sampler Example Plugin
-# Copyright 2011-2012 David Robillard <d@drobilla.net>
-# Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
-# Copyright 2011 James Morris <jwm.art.net@gmail.com>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-@prefix atom:  <http://lv2plug.in/ns/ext/atom#> .
-@prefix doap:  <http://usefulinc.com/ns/doap#> .
-@prefix lv2:   <http://lv2plug.in/ns/lv2core#> .
-@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
-@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
-@prefix state: <http://lv2plug.in/ns/ext/state#> .
-@prefix ui:    <http://lv2plug.in/ns/extensions/ui#> .
-@prefix urid:  <http://lv2plug.in/ns/ext/urid#> .
-@prefix work:  <http://lv2plug.in/ns/ext/worker#> .
-
-<http://lv2plug.in/plugins/eg-sampler#sample>
-	a lv2:Parameter ;
-	rdfs:label "sample" ;
-	rdfs:range atom:Path .
-
-<http://lv2plug.in/plugins/eg-sampler>
-	a lv2:Plugin ;
-	doap:name "Example Sampler" ;
-	doap:license <http://opensource.org/licenses/isc> ;
-	lv2:project <http://lv2plug.in/ns/lv2> ;
-	lv2:requiredFeature urid:map ,
-		work:schedule ;
-	lv2:optionalFeature lv2:hardRTCapable ,
-		state:loadDefaultState ;
-	lv2:extensionData state:interface ,
-		work:interface ;
-	ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ;
-	patch:writable <http://lv2plug.in/plugins/eg-sampler#sample> ;
-	lv2:port [
-		a lv2:InputPort ,
-			atom:AtomPort ;
-		atom:bufferType atom:Sequence ;
-		atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ,
-			patch:Message ;
-		lv2:designation lv2:control ;
-		lv2:index 0 ;
-		lv2:symbol "control" ;
-		lv2:name "Control"
-	] , [
-		a lv2:OutputPort ,
-			atom:AtomPort ;
-		atom:bufferType atom:Sequence ;
-		atom:supports patch:Message ;
-		lv2:designation lv2:control ;
-		lv2:index 1 ;
-		lv2:symbol "notify" ;
-		lv2:name "Notify"
-	] , [
-		a lv2:AudioPort ,
-			lv2:OutputPort ;
-		lv2:index 2 ;
-		lv2:symbol "out" ;
-		lv2:name "Out"
-	] ;
-	state:state [
-		<http://lv2plug.in/plugins/eg-sampler#sample> <click.wav>
-	] .
-
-<http://lv2plug.in/plugins/eg-sampler#ui>
-	a ui:GtkUI ;
-	lv2:requiredFeature urid:map ;
-	ui:portNotification [
-		ui:plugin <http://lv2plug.in/plugins/eg-sampler> ;
-		lv2:symbol "notify" ;
-		ui:notifyType atom:Blank
-	] .
diff --git a/plugins/eg-sampler.lv2/sampler_ui.c b/plugins/eg-sampler.lv2/sampler_ui.c
deleted file mode 100644
index 40922ae..0000000
--- a/plugins/eg-sampler.lv2/sampler_ui.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
-  LV2 Sampler Example Plugin UI
-  Copyright 2011-2012 David Robillard <d@drobilla.net>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
-   @file ui.c Sampler Plugin UI
-*/
-
-#include <stdlib.h>
-
-#include <gtk/gtk.h>
-
-#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
-#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
-#include "lv2/lv2plug.in/ns/ext/atom/util.h"
-#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
-#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
-#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
-
-#include "./uris.h"
-
-#define SAMPLER_UI_URI "http://lv2plug.in/plugins/eg-sampler#ui"
-
-typedef struct {
-	LV2_Atom_Forge forge;
-
-	LV2_URID_Map* map;
-	SamplerURIs   uris;
-
-	LV2UI_Write_Function write;
-	LV2UI_Controller     controller;
-
-	GtkWidget* box;
-	GtkWidget* button;
-	GtkWidget* label;
-} SamplerUI;
-
-static void
-on_load_clicked(GtkWidget* widget,
-                void*      handle)
-{
-	SamplerUI* ui = (SamplerUI*)handle;
-
-	/* Create a dialog to select a sample file. */
-	GtkWidget* dialog = gtk_file_chooser_dialog_new(
-		"Load Sample",
-		NULL,
-		GTK_FILE_CHOOSER_ACTION_OPEN,
-		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-		NULL);
-
-	/* Run the dialog, and return if it is cancelled. */
-	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
-		gtk_widget_destroy(dialog);
-		return;
-	}
-
-	/* Get the file path from the dialog. */
-	char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-
-	/* Got what we need, destroy the dialog. */
-	gtk_widget_destroy(dialog);
-
-#define OBJ_BUF_SIZE 1024
-	uint8_t obj_buf[OBJ_BUF_SIZE];
-	lv2_atom_forge_set_buffer(&ui->forge, obj_buf, OBJ_BUF_SIZE);
-
-	LV2_Atom* msg = write_set_file(&ui->forge, &ui->uris,
-	                               filename, strlen(filename));
-
-	ui->write(ui->controller, 0, lv2_atom_total_size(msg),
-	          ui->uris.atom_eventTransfer,
-	          msg);
-
-	g_free(filename);
-}
-
-static LV2UI_Handle
-instantiate(const LV2UI_Descriptor*   descriptor,
-            const char*               plugin_uri,
-            const char*               bundle_path,
-            LV2UI_Write_Function      write_function,
-            LV2UI_Controller          controller,
-            LV2UI_Widget*             widget,
-            const LV2_Feature* const* features)
-{
-	SamplerUI* ui = (SamplerUI*)malloc(sizeof(SamplerUI));
-	ui->map        = NULL;
-	ui->write      = write_function;
-	ui->controller = controller;
-	ui->box        = NULL;
-	ui->button     = NULL;
-	ui->label      = NULL;
-
-	*widget = NULL;
-
-	for (int i = 0; features[i]; ++i) {
-		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
-			ui->map = (LV2_URID_Map*)features[i]->data;
-		}
-	}
-
-	if (!ui->map) {
-		fprintf(stderr, "sampler_ui: Host does not support urid:Map\n");
-		free(ui);
-		return NULL;
-	}
-
-	map_sampler_uris(ui->map, &ui->uris);
-
-	lv2_atom_forge_init(&ui->forge, ui->map);
-
-	ui->box = gtk_vbox_new(FALSE, 4);
-	ui->label = gtk_label_new("?");
-	ui->button = gtk_button_new_with_label("Load Sample");
-	gtk_box_pack_start(GTK_BOX(ui->box), ui->label, TRUE, TRUE, 4);
-	gtk_box_pack_start(GTK_BOX(ui->box), ui->button, FALSE, FALSE, 4);
-	g_signal_connect(ui->button, "clicked",
-	                 G_CALLBACK(on_load_clicked),
-	                 ui);
-
-	*widget = ui->box;
-
-	return ui;
-}
-
-static void
-cleanup(LV2UI_Handle handle)
-{
-	SamplerUI* ui = (SamplerUI*)handle;
-	gtk_widget_destroy(ui->button);
-	free(ui);
-}
-
-static void
-port_event(LV2UI_Handle handle,
-           uint32_t     port_index,
-           uint32_t     buffer_size,
-           uint32_t     format,
-           const void*  buffer)
-{
-	SamplerUI* ui = (SamplerUI*)handle;
-	if (format == ui->uris.atom_eventTransfer) {
-		LV2_Atom* atom = (LV2_Atom*)buffer;
-		if (atom->type == ui->uris.atom_Blank) {
-			LV2_Atom_Object* obj      = (LV2_Atom_Object*)atom;
-			const LV2_Atom*  file_uri = read_set_file(&ui->uris, obj);
-			if (!file_uri) {
-				fprintf(stderr, "Unknown message sent to UI.\n");
-				return;
-			}
-
-			const char* uri = (const char*)LV2_ATOM_BODY(file_uri);
-			gtk_label_set_text(GTK_LABEL(ui->label), uri);
-		} else {
-			fprintf(stderr, "Unknown message type.\n");
-		}
-	} else {
-		fprintf(stderr, "Unknown format.\n");
-	}
-}
-
-static const void*
-extension_data(const char* uri)
-{
-	return NULL;
-}
-
-static const LV2UI_Descriptor descriptor = {
-	SAMPLER_UI_URI,
-	instantiate,
-	cleanup,
-	port_event,
-	extension_data
-};
-
-LV2_SYMBOL_EXPORT
-const LV2UI_Descriptor*
-lv2ui_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-sampler.lv2/uris.h b/plugins/eg-sampler.lv2/uris.h
deleted file mode 100644
index e2ec6d0..0000000
--- a/plugins/eg-sampler.lv2/uris.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-  LV2 Sampler Example Plugin
-  Copyright 2011-2012 David Robillard <d@drobilla.net>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#ifndef SAMPLER_URIS_H
-#define SAMPLER_URIS_H
-
-#include "lv2/lv2plug.in/ns/ext/log/log.h"
-#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
-#include "lv2/lv2plug.in/ns/ext/state/state.h"
-
-#define EG_SAMPLER_URI          "http://lv2plug.in/plugins/eg-sampler"
-#define EG_SAMPLER__sample      EG_SAMPLER_URI "#sample"
-#define EG_SAMPLER__applySample EG_SAMPLER_URI "#applySample"
-#define EG_SAMPLER__freeSample  EG_SAMPLER_URI "#freeSample"
-
-typedef struct {
-	LV2_URID atom_Blank;
-	LV2_URID atom_Path;
-	LV2_URID atom_Resource;
-	LV2_URID atom_Sequence;
-	LV2_URID atom_URID;
-	LV2_URID atom_eventTransfer;
-	LV2_URID eg_applySample;
-	LV2_URID eg_sample;
-	LV2_URID eg_freeSample;
-	LV2_URID midi_Event;
-	LV2_URID patch_Set;
-	LV2_URID patch_property;
-	LV2_URID patch_value;
-} SamplerURIs;
-
-static inline void
-map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris)
-{
-	uris->atom_Blank         = map->map(map->handle, LV2_ATOM__Blank);
-	uris->atom_Path          = map->map(map->handle, LV2_ATOM__Path);
-	uris->atom_Resource      = map->map(map->handle, LV2_ATOM__Resource);
-	uris->atom_Sequence      = map->map(map->handle, LV2_ATOM__Sequence);
-	uris->atom_URID          = map->map(map->handle, LV2_ATOM__URID);
-	uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer);
-	uris->eg_applySample     = map->map(map->handle, EG_SAMPLER__applySample);
-	uris->eg_freeSample      = map->map(map->handle, EG_SAMPLER__freeSample);
-	uris->eg_sample          = map->map(map->handle, EG_SAMPLER__sample);
-	uris->midi_Event         = map->map(map->handle, LV2_MIDI__MidiEvent);
-	uris->patch_Set          = map->map(map->handle, LV2_PATCH__Set);
-	uris->patch_property     = map->map(map->handle, LV2_PATCH__property);
-	uris->patch_value        = map->map(map->handle, LV2_PATCH__value);
-}
-
-static inline bool
-is_object_type(const SamplerURIs* uris, LV2_URID type)
-{
-	return type == uris->atom_Resource
-		|| type == uris->atom_Blank;
-}
-
-/**
- * Write a message like the following to @p forge:
- * []
- *     a patch:Set ;
- *     patch:property eg:sample ;
- *     patch:value </home/me/foo.wav> .
- */
-static inline LV2_Atom*
-write_set_file(LV2_Atom_Forge*    forge,
-               const SamplerURIs* uris,
-               const char*        filename,
-               const size_t       filename_len)
-{
-	LV2_Atom_Forge_Frame frame;
-	LV2_Atom* set = (LV2_Atom*)lv2_atom_forge_blank(
-		forge, &frame, 1, uris->patch_Set);
-
-	lv2_atom_forge_property_head(forge, uris->patch_property, 0);
-	lv2_atom_forge_urid(forge, uris->eg_sample);
-	lv2_atom_forge_property_head(forge, uris->patch_value, 0);
-	lv2_atom_forge_path(forge, filename, filename_len);
-
-	lv2_atom_forge_pop(forge, &frame);
-
-	return set;
-}
-
-/**
- * Get the file path from a message like:
- * []
- *     a patch:Set ;
- *     patch:property eg:sample ;
- *     patch:value </home/me/foo.wav> .
- */
-static inline const LV2_Atom*
-read_set_file(const SamplerURIs*     uris,
-              const LV2_Atom_Object* obj)
-{
-	if (obj->body.otype != uris->patch_Set) {
-		fprintf(stderr, "Ignoring unknown message type %d\n", obj->body.otype);
-		return NULL;
-	}
-
-	/* Get property URI. */
-	const LV2_Atom* property = NULL;
-	lv2_atom_object_get(obj, uris->patch_property, &property, 0);
-	if (!property) {
-		fprintf(stderr, "Malformed set message has no body.\n");
-		return NULL;
-	} else if (property->type != uris->atom_URID) {
-		fprintf(stderr, "Malformed set message has non-URID property.\n");
-		return NULL;
-	} else if (((LV2_Atom_URID*)property)->body != uris->eg_sample) {
-		fprintf(stderr, "Set message for unknown property.\n");
-		return NULL;
-	}
-
-	/* Get value. */
-	const LV2_Atom* file_path = NULL;
-	lv2_atom_object_get(obj, uris->patch_value, &file_path, 0);
-	if (!file_path) {
-		fprintf(stderr, "Malformed set message has no value.\n");
-		return NULL;
-	} else if (file_path->type != uris->atom_Path) {
-		fprintf(stderr, "Set message value is not a Path.\n");
-		return NULL;
-	}
-
-	return file_path;
-}
-
-#endif  /* SAMPLER_URIS_H */
diff --git a/plugins/eg-sampler.lv2/waf b/plugins/eg-sampler.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-sampler.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf
\ No newline at end of file
diff --git a/plugins/eg-sampler.lv2/wscript b/plugins/eg-sampler.lv2/wscript
deleted file mode 100644
index 732c904..0000000
--- a/plugins/eg-sampler.lv2/wscript
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-sampler.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
-    opt.load('compiler_c')
-    autowaf.set_options(opt)
-
-def configure(conf):
-    conf.load('compiler_c')
-    autowaf.configure(conf)
-    autowaf.set_c99_mode(conf)
-    autowaf.display_header('Sampler Configuration')
-
-    if not autowaf.is_child():
-        autowaf.check_pkg(conf, 'lv2', atleast_version='1.2.1', uselib_store='LV2')
-
-    autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE',
-                      atleast_version='1.0.0', mandatory=True)
-    autowaf.check_pkg(conf, 'gtk+-2.0', uselib_store='GTK2',
-                      atleast_version='2.18.0', mandatory=False)
-
-    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
-    print('')
-
-def build(bld):
-    bundle = 'eg-sampler.lv2'
-
-    # Make a pattern for shared objects without the 'lib' prefix
-    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
-    module_ext = module_pat[module_pat.rfind('.'):]
-
-    # Build manifest.ttl by substitution (for portable lib extension)
-    bld(features     = 'subst',
-        source       = 'manifest.ttl.in',
-        target       = '%s/%s' % (bundle, 'manifest.ttl'),
-        install_path = '${LV2DIR}/%s' % bundle,
-        LIB_EXT      = module_ext)
-    
-    # Copy other data files to build bundle (build/eg-sampler.lv2)
-    for i in ['sampler.ttl', 'click.wav']:
-        bld(features     = 'subst',
-            is_copy      = True,
-            source       = i,
-            target       = '%s/%s' % (bundle, i),
-            install_path = '${LV2DIR}/%s' % bundle)
-
-    # Use LV2 headers from parent directory if building as a sub-project
-    includes = ['.']
-    if autowaf.is_child:
-        includes += ['../..']
-
-    # Build plugin library
-    obj = bld(features     = 'c cshlib',
-              source       = 'sampler.c',
-              name         = 'sampler',
-              target       = '%s/sampler' % bundle,
-              install_path = '${LV2DIR}/%s' % bundle,
-              use          = 'SNDFILE LV2',
-              includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
-
-    # Build UI library
-    if bld.is_defined('HAVE_GTK2'):
-        obj = bld(features     = 'c cshlib',
-                  source       = 'sampler_ui.c',
-                  name         = 'sampler_ui',
-                  target       = '%s/sampler_ui' % bundle,
-                  install_path = '${LV2DIR}/%s' % bundle,
-                  use          = 'GTK2 LV2',
-                  includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
diff --git a/plugins/eg-synth.lv2/manifest.ttl.in b/plugins/eg-synth.lv2/manifest.ttl.in
deleted file mode 100644
index 24acd71..0000000
--- a/plugins/eg-synth.lv2/manifest.ttl.in
+++ /dev/null
@@ -1,9 +0,0 @@
-# See manifest.ttl.in in the eg-amp.lv2 example for an explanation of this file
-
-@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-<http://lv2plug.in/plugins/eg-synth>
-	a lv2:Plugin ;
-	lv2:binary <synth@LIB_EXT@>  ;
-	rdfs:seeAlso <synth.ttl> .
diff --git a/plugins/eg-synth.lv2/synth.c b/plugins/eg-synth.lv2/synth.c
deleted file mode 100644
index 758989b..0000000
--- a/plugins/eg-synth.lv2/synth.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
-  Copyright 2012 Harry van Haaren <harryhaaren@gmail.com>
-  Copyright 2012 David Robillard <d@drobilla.net>
-
-  Permission to use, copy, modify, and/or distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-
-  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-/**
-   @file synth.c Implementation of the LV2 Sin Synth example plugin.
-
-   This is a simple LV2 synthesis plugin that demonstrates how to receive MIDI
-   events and render audio in response to them.
-*/
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
-
-#define SYNTH_URI "http://lv2plug.in/plugins/eg-synth"
-
-/** Port indices. */
-typedef enum {
-	SYNTH_FREQ = 0,
-	SYNTH_OUTPUT,
-} PortIndex;
-
-/** Plugin instance. */
-typedef struct {
-	// Sample rate, necessary to generate sin wave in run()
-	double sample_rate;
-
-	// Current wave phase
-	float phase;
-
-	// Port buffers
-	const float* freq;
-	float*       output;
-} Synth;
-
-/** Create a new plugin instance. */
-static LV2_Handle
-instantiate(const LV2_Descriptor*     descriptor,
-            double                    rate,
-            const char*               bundle_path,
-            const LV2_Feature* const* features)
-{
-	Synth* self = (Synth*)malloc(sizeof(Synth));
-	if (self) {
-		// Store the sample rate so it is available in run()
-		self->sample_rate = rate;
-	}
-	return (LV2_Handle)self;
-}
-
-/** Connect a port to a buffer (audio thread, must be RT safe). */
-static void
-connect_port(LV2_Handle instance,
-             uint32_t   port,
-             void*      data)
-{
-	Synth* self = (Synth*)instance;
-
-	switch ((PortIndex)port) {
-	case SYNTH_FREQ:
-		self->freq = (const float*)data;
-		break;
-	case SYNTH_OUTPUT:
-		self->output = (float*)data;
-		break;
-	}
-}
-
-/** Initialise and prepare the plugin instance for running. */
-static void
-activate(LV2_Handle instance)
-{
-	Synth* self = (Synth*)instance;
-
-	// Initialize phase so we start at the beginning of the wave
-	self->phase = 0.0f;
-}
-
-/** Process a block of audio (audio thread, must be RT safe). */
-static void
-run(LV2_Handle instance, uint32_t n_samples)
-{
-	Synth* self = (Synth*)instance;
-
-	const float  PI     = 3.1415;
-	const float  volume = 0.3;
-	const float  freq   = *(self->freq);
-	float* const output = self->output;
-
-	float samples_per_cycle = self->sample_rate / freq;
-
-	/* Calculate the phase offset per sample.  Phase ranges from 0..1, so
-	   phase_increment is a floating point number such that we get "freq"
-	   number of cycles in "sample_rate" amount of samples. */
-	float phase_increment = (1.f / samples_per_cycle);
-
-	for (uint32_t pos = 0; pos < n_samples; pos++) {
-		/* Calculate the next sample.  Phase ranges from 0..1, but sin()
-		   expects its input in radians, so we multiply by 2 PI to convert it.
-		   We also multiply by volume so it's not extremely loud. */
-		output[pos] = sin(self->phase * 2 * PI) * volume;
-
-		/* Increment the phase so we generate the next sample */
-		self->phase += phase_increment;
-		if (self->phase > 1.0f) {
-			self->phase = 0.0f;
-		}
-	}
-}
-
-/** Finish running (counterpart to activate()). */
-static void
-deactivate(LV2_Handle instance)
-{
-	/* Nothing to do here in this trivial mostly stateless plugin. */
-}
-
-/** Destroy a plugin instance (counterpart to instantiate()). */
-static void
-cleanup(LV2_Handle instance)
-{
-	free(instance);
-}
-
-/** Return extension data provided by the plugin. */
-static const void*
-extension_data(const char* uri)
-{
-	return NULL;  /* This plugin has no extension data. */
-}
-
-/** The LV2_Descriptor for this plugin. */
-static const LV2_Descriptor descriptor = {
-	SYNTH_URI,
-	instantiate,
-	connect_port,
-	activate,
-	run,
-	deactivate,
-	cleanup,
-	extension_data
-};
-
-/** Entry point, the host will call this function to access descriptors. */
-LV2_SYMBOL_EXPORT
-const LV2_Descriptor*
-lv2_descriptor(uint32_t index)
-{
-	switch (index) {
-	case 0:
-		return &descriptor;
-	default:
-		return NULL;
-	}
-}
diff --git a/plugins/eg-synth.lv2/synth.ttl b/plugins/eg-synth.lv2/synth.ttl
deleted file mode 100644
index fb97d56..0000000
--- a/plugins/eg-synth.lv2/synth.ttl
+++ /dev/null
@@ -1,44 +0,0 @@
-# LV2 Sinewave synth plugin
-# Copyright 2012 Harry van Haaren <harryhaaren@gmail.com>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-@prefix doap: <http://usefulinc.com/ns/doap#> .
-@prefix foaf: <http://xmlns.com/foaf/0.1/> .
-@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
-@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
-@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
-
-<http://lv2plug.in/plugins/eg-synth>
-	a lv2:Plugin ,
-		lv2:InstrumentPlugin ;
-	doap:name "Example Synthesizer" ;
-	doap:license <http://opensource.org/licenses/isc> ;
-	lv2:project <http://lv2plug.in/ns/lv2> ;
-	lv2:optionalFeature lv2:hardRTCapable ;
-	lv2:port [
-		a lv2:InputPort ,
-			lv2:ControlPort ;
-		lv2:index 0 ;
-		lv2:symbol "frequency" ;
-		lv2:name "Frequency" ;
-		lv2:default 440.0 ;
-		lv2:minimum 40.0 ;
-		lv2:maximum 880.0
-	] , [
-		a lv2:AudioPort ,
-			lv2:OutputPort ;
-		lv2:index 1 ;
-		lv2:symbol "out" ;
-		lv2:name "Out"
-	] .
diff --git a/plugins/eg-synth.lv2/waf b/plugins/eg-synth.lv2/waf
deleted file mode 120000
index 59a1ac9..0000000
--- a/plugins/eg-synth.lv2/waf
+++ /dev/null
@@ -1 +0,0 @@
-../../waf
\ No newline at end of file
diff --git a/plugins/eg-synth.lv2/wscript b/plugins/eg-synth.lv2/wscript
deleted file mode 100644
index 2069d03..0000000
--- a/plugins/eg-synth.lv2/wscript
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-from waflib.extras import autowaf as autowaf
-import re
-
-# Variables for 'waf dist'
-APPNAME = 'eg-synth.lv2'
-VERSION = '1.0.0'
-
-# Mandatory variables
-top = '.'
-out = 'build'
-
-def options(opt):
-    opt.load('compiler_c')
-    autowaf.set_options(opt)
-
-def configure(conf):
-    conf.load('compiler_c')
-    autowaf.configure(conf)
-    autowaf.set_c99_mode(conf)
-    autowaf.display_header('Synth Configuration')
-
-    if not autowaf.is_child():
-        autowaf.check_pkg(conf, 'lv2', uselib_store='LV2')
-
-    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
-    print('')
-
-def build(bld):
-    bundle = APPNAME
-
-    # Make a pattern for shared objects without the 'lib' prefix
-    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
-    module_ext = module_pat[module_pat.rfind('.'):]
-
-    # Build manifest.ttl by substitution (for portable lib extension)
-    bld(features     = 'subst',
-        source       = 'manifest.ttl.in',
-        target       = '%s/%s' % (bundle, 'manifest.ttl'),
-        install_path = '${LV2DIR}/%s' % bundle,
-        LIB_EXT      = module_ext)
-
-    # Copy other data files to build bundle (build/eg-amp.lv2)
-    for i in ['synth.ttl']:
-        bld(features     = 'subst',
-            is_copy      = True,
-            source       = i,
-            target       = '%s/%s' % (bundle, i),
-            install_path = '${LV2DIR}/%s' % bundle)
-
-    # Use LV2 headers from parent directory if building as a sub-project
-    includes = None
-    if autowaf.is_child:
-        includes = '../..'
-
-    # Build plugin library
-    obj = bld(features     = 'c cshlib',
-              source       = 'synth.c',
-              name         = 'synth',
-              target       = '%s/synth' % bundle,
-              install_path = '${LV2DIR}/%s' % bundle,
-              uselib       = 'LV2',
-              includes     = includes)
-    obj.env.cshlib_PATTERN = module_pat
diff --git a/plugins/eg01-amp.lv2/README.txt b/plugins/eg01-amp.lv2/README.txt
new file mode 100644
index 0000000..f024a4d
--- /dev/null
+++ b/plugins/eg01-amp.lv2/README.txt
@@ -0,0 +1,21 @@
+== Simple Amplifier ==
+
+This plugin is a simple example of a basic LV2 plugin with no additional features.
+It has audio ports which contain an array of `float`,
+and a control port which contain a single `float`.
+
+LV2 plugins are defined in two parts: code and data.
+The code is written in C, or any C compatible language such as C++.
+Static data is described separately in the human and machine friendly http://www.w3.org/TeamSubmission/turtle/[Turtle] syntax.
+Turtle is a syntax for the RDF data model,
+but familiarity with RDF is not required to understand this documentation.
+
+Generally, code is kept minimal,
+and all static information is described in the data.
+There are several advantages to this approach:
+
+ * Hosts can discover and inspect plugins without loading or executing any plugin code
+ * It is simple to work with plugin data using scripting languages, command line tools, etc.
+ * The standard format allow the use of existing vocabularies to describe plugins and related information
+ * The data inherently integrates with the web, databases, etc.
+ * Labels and documentation are translatable, and available to hosts for display in user interfaces
diff --git a/plugins/eg01-amp.lv2/amp.c b/plugins/eg01-amp.lv2/amp.c
new file mode 100644
index 0000000..8dd7b4f
--- /dev/null
+++ b/plugins/eg01-amp.lv2/amp.c
@@ -0,0 +1,225 @@
+/*
+  Copyright 2006-2011 David Robillard <d@drobilla.net>
+  Copyright 2006 Steve Harris <steve@plugin.org.uk>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/** Include standard C headers */
+#include <math.h>
+#include <stdlib.h>
+
+/**
+   LV2 headers are based on the URI of the specification they come from, so a
+   consistent convention can be used even for unofficial extensions.  The URI
+   of the core LV2 specification is <http://lv2plug.in/ns/lv2core>, by
+   replacing `http:/` with `lv2` any header in the specification bundle can be
+   included, in this case `lv2.h`.
+*/
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+   The URI is the identifier for a plugin, and how the host associates this
+   implementation in code with its description in data.  In this plugin it is
+   only used once in the code, but defining the plugin URI at the top of the
+   file is a good convention to follow.  If this URI does not match that used
+   in the data files, the host will fail to load the plugin.
+*/
+#define AMP_URI "http://lv2plug.in/plugins/eg-amp"
+
+/**
+   In code, ports are referred to by index.  An enumeration of port indices
+   should be defined for readability.
+*/
+typedef enum {
+	AMP_GAIN   = 0,
+	AMP_INPUT  = 1,
+	AMP_OUTPUT = 2
+} PortIndex;
+
+/**
+   Every plugin defines a private structure for the plugin instance.  All data
+   associated with a plugin instance is stored here, and is available to
+   every instance method.  In this simple plugin, only port buffers need to be
+   stored, since there is no additional instance data.  */
+typedef struct {
+	// Port buffers
+	const float* gain;
+	const float* input;
+	float*       output;
+} Amp;
+
+/**
+   The instantiate() function is called by the host to create a new plugin
+   instance.  The host passes the plugin descriptor, sample rate, and bundle
+   path for plugins that need to load additional resources (e.g. waveforms).
+   The features parameter contains host-provided features defined in LV2
+   extensions, but this simple plugin does not use any.
+
+   This function is in the ``instantiation'' threading class, so no other
+   methods on this instance will be called concurrently with it.
+*/
+static LV2_Handle
+instantiate(const LV2_Descriptor*     descriptor,
+            double                    rate,
+            const char*               bundle_path,
+            const LV2_Feature* const* features)
+{
+	Amp* amp = (Amp*)malloc(sizeof(Amp));
+
+	return (LV2_Handle)amp;
+}
+
+/**
+   The connect_port() method is called by the host to connect a particular port
+   to a buffer.  The plugin must store the data location, but data may not be
+   accessed except in run().
+
+   This method is in the ``audio'' threading class, and is called in the same
+   context as run().
+*/
+static void
+connect_port(LV2_Handle instance,
+             uint32_t   port,
+             void*      data)
+{
+	Amp* amp = (Amp*)instance;
+
+	switch ((PortIndex)port) {
+	case AMP_GAIN:
+		amp->gain = (const float*)data;
+		break;
+	case AMP_INPUT:
+		amp->input = (const float*)data;
+		break;
+	case AMP_OUTPUT:
+		amp->output = (float*)data;
+		break;
+	}
+}
+
+/**
+   The activate() method is called by the host to initialise and prepare the
+   plugin instance for running.  The plugin must reset all internal state
+   except for buffer locations set by connect_port().  Since this plugin has
+   no other internal state, this method does nothing.
+
+   This method is in the ``instantiation'' threading class, so no other
+   methods on this instance will be called concurrently with it.
+*/
+static void
+activate(LV2_Handle instance)
+{
+}
+
+/** Define a macro for converting a gain in dB to a coefficient */
+#define DB_CO(g) ((g) > -90.0f ? powf(10.0f, (g) * 0.05f) : 0.0f)
+
+/** Process a block of audio (audio thread, must be RT safe). */
+static void
+run(LV2_Handle instance, uint32_t n_samples)
+{
+	const Amp* amp = (const Amp*)instance;
+
+	const float        gain   = *(amp->gain);
+	const float* const input  = amp->input;
+	float* const       output = amp->output;
+
+	const float coef = DB_CO(gain);
+
+	for (uint32_t pos = 0; pos < n_samples; pos++) {
+		output[pos] = input[pos] * coef;
+	}
+}
+
+/**
+   The deactivate() method is the counterpart to activate() called by the host
+   after running the plugin.  It indicates that the host will not call run()
+   again until another call to activate() and is mainly useful for more
+   advanced plugins with ``live'' characteristics such as those with auxiliary
+   processing threads.  As with activate(), this plugin has no use for this
+   information so this method does nothing.
+
+   This method is in the ``instantiation'' threading class, so no other
+   methods on this instance will be called concurrently with it.
+*/
+static void
+deactivate(LV2_Handle instance)
+{
+}
+
+/**
+   Destroy a plugin instance (counterpart to instantiate()).
+
+   This method is in the ``instantiation'' threading class, so no other
+   methods on this instance will be called concurrently with it.
+*/
+static void
+cleanup(LV2_Handle instance)
+{
+	free(instance);
+}
+
+/**
+   The extension_data function returns any extension data supported by the
+   plugin.  Note that this is not an instance method, but a function on the
+   plugin descriptor.  It is usually used by plugins to implement additional
+   interfaces.  This plugin does not have any extension data, so this function
+   returns NULL.
+
+   This method is in the ``discovery'' threading class, so no other functions
+   or methods in this plugin library will be called concurrently with it.
+*/
+static const void*
+extension_data(const char* uri)
+{
+	return NULL;
+}
+
+/**
+   Define the LV2_Descriptor for this plugin.  It is best to define descriptors
+   statically to avoid leaking memory and non-portable shared library
+   constructors and destructors to clean up properly.
+*/
+static const LV2_Descriptor descriptor = {
+	AMP_URI,
+	instantiate,
+	connect_port,
+	activate,
+	run,
+	deactivate,
+	cleanup,
+	extension_data
+};
+
+/**
+   The lv2_descriptor() function is the entry point to the plugin library.  The
+   host will load the library and call this function repeatedly with increasing
+   indices to find all the plugins defined in the library.  The index is not an
+   indentifier, the URI of the returned descriptor is used to determine the
+   identify of the plugin.
+   
+   This method is in the ``discovery'' threading class, so no other functions
+   or methods in this plugin library will be called concurrently with it.
+*/
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+	switch (index) {
+	case 0:
+		return &descriptor;
+	default:
+		return NULL;
+	}
+}
diff --git a/plugins/eg01-amp.lv2/amp.ttl b/plugins/eg01-amp.lv2/amp.ttl
new file mode 100644
index 0000000..f4a87f2
--- /dev/null
+++ b/plugins/eg01-amp.lv2/amp.ttl
@@ -0,0 +1,86 @@
+# The full description of the plugin is in this file, which is linked to from
+# `manifest.ttl`.  This is done so the host only needs to scan the relatively
+# small `manifest.ttl` files to quickly discover all plugins.
+
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+# First the type of the plugin is described.  All plugins must explicitly list
+# `lv2:Plugin` as a type.  A more specific type should also be given, where
+# applicable, so hosts can present a nicer UI for loading plugins.  Note that
+# this URI is the identifier of the plugin, so if it does not match the one in
+# `manifest.ttl`, the host will not discover the plugin data at all.
+<http://lv2plug.in/plugins/eg-amp>
+	a lv2:Plugin ,
+		lv2:AmplifierPlugin ;
+# Plugins are associated with a project, where common information like
+# developers, home page, and so on are described.  This plugin is part of the
+# LV2 project, which has URI <http://lv2plug.in/ns/lv2>, and is described
+# elsewhere.  Typical plugin collections will describe the project in
+# manifest.ttl
+	lv2:project <http://lv2plug.in/ns/lv2> ;
+# Every plugin must have a name, described with the doap:name property.
+# Translations to various languages can be added by putting a language tag
+# after strings as shown.
+	doap:name "Simple Amplifier" ,
+		"简单放大器"@ch ,
+		"Einfacher Verstärker"@de ,
+		"Simple Amp"@en-gb ,
+		"Amplificador Simple"@es ,
+		"Amplificateur de Base"@fr ,
+		"Amplificatore Semplice"@it ,
+		"簡単なアンプ"@jp ,
+		"Просто Усилитель"@ru ;
+	doap:license <http://opensource.org/licenses/isc> ;
+	lv2:optionalFeature lv2:hardRTCapable ;
+	lv2:port [
+# Every port must have at least two types, one that specifies direction
+# (lv2:InputPort or lv2:OutputPort), and another to describe the data type.
+# This port is a lv2:ControlPort, which means it contains a single float.
+		a lv2:InputPort ,
+			lv2:ControlPort ;
+		lv2:index 0 ;
+		lv2:symbol "gain" ;
+		lv2:name "Gain" ,
+			"收益"@ch ,
+			"Gewinn"@de ,
+			"Gain"@en-gb ,
+			"Aumento"@es ,
+			"Gain"@fr ,
+			"Guadagno"@it ,
+			"利益"@jp ,
+			"Увеличение"@ru ;
+# An lv2:ControlPort should always describe its default value, and usually a
+# minimum and maximum value.  Defining a range is not strictly required, but
+# should be done wherever possible to aid host support, particularly for UIs.
+		lv2:default 0.0 ;
+		lv2:minimum -90.0 ;
+		lv2:maximum 24.0 ;
+		lv2:scalePoint [
+			rdfs:label "+5" ;
+			rdf:value 5.0
+		] , [
+			rdfs:label "0" ;
+			rdf:value 0.0
+		] , [
+			rdfs:label "-5" ;
+			rdf:value -5.0
+		] , [
+			rdfs:label "-10" ;
+			rdf:value -10.0
+		]
+	] , [
+		a lv2:AudioPort ,
+			lv2:InputPort ;
+		lv2:index 1 ;
+		lv2:symbol "in" ;
+		lv2:name "In"
+	] , [
+		a lv2:AudioPort ,
+			lv2:OutputPort ;
+		lv2:index 2 ;
+		lv2:symbol "out" ;
+		lv2:name "Out"
+	] .
diff --git a/plugins/eg01-amp.lv2/manifest.ttl.in b/plugins/eg01-amp.lv2/manifest.ttl.in
new file mode 100644
index 0000000..0da78b0
--- /dev/null
+++ b/plugins/eg01-amp.lv2/manifest.ttl.in
@@ -0,0 +1,101 @@
+# LV2 Bundle Manifest
+#
+# All LV2 plugins are installed as "bundles", a directory with a particular
+# format.  Inside the bundle, the entry point is a file called "manifest.ttl".
+# This file lists what plugins are in this bundle, and which files are (.so,
+# .ttl, etc.) are associated with those plugins.
+#
+# Hosts read bundles' manifest.ttl to discover what plugins (and other
+# resources) are available.  Manifest files should be as small as possible for
+# performance reasons.
+#
+#
+# ==== Namespace Prefixes ====
+#
+# Turtle files contain many URIs.  To make this more readable, prefixes
+# can be defined.  For example, with the `lv2:` prefix below, instead of
+# <http://lv2plug.in/ns/lv2core#Plugin> the shorter form `lv2:Plugin` can be
+# used.  This is just a shorthand for URIs within a file, the prefixes are not
+# significant otherwise.
+
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+# ==== Data ====
+
+<http://lv2plug.in/plugins/eg-amp>
+	a lv2:Plugin ;
+	lv2:binary <amp@LIB_EXT@>  ;
+	rdfs:seeAlso <amp.ttl> .
+
+# The token `@LIB_EXT@` above is replaced by the build system with the
+# appropriate extension for the current platform (e.g. .so, .dylib, .dll).
+# This file is called called `manifest.ttl.in` rather than `manifest.ttl`
+# to indicate that it is not the final file to be installed.
+# This is not necessary, but is a good idea for portable plugins.
+# For reability, the following text will assume `.so` is the extension used.
+#
+# In short, this declares that the resource with URI
+# "http://lv2plug.in/plugins/eg-amp" is an LV2 plugin, with executable code in
+# the file "amp.so" and a full description in "amp.ttl".  These paths are
+# relative to the bundle directory.
+#
+# There are 3 statements in this description:
+# |================================================================
+# | Subject                             | Predicate    | Object
+# | <http://lv2plug.in/plugins/eg-amp>  | a            | lv2:Plugin
+# | <http://lv2plug.in/plugins/eg-amp>  | lv2:binary   | <amp.so>
+# | <http://lv2plug.in/plugins/eg-amp>  | rdfs:seeAlso | <amp.ttl>
+# |================================================================
+#
+# The semicolon is used to continue the previous subject; an equivalent
+# but more verbose syntax for the same data is:
+
+<http://lv2plug.in/plugins/eg-amp> a lv2:Plugin .
+<http://lv2plug.in/plugins/eg-amp> lv2:binary <amp.so> .
+<http://lv2plug.in/plugins/eg-amp> rdfs:seeAlso <amp.ttl> .
+
+# (Since this data is equivalent, it is safe, if pointless, to list it twice)
+#
+# Note that the documentation for a URI can often be found by visiting that URI
+# in a web browser, e.g. the documentation for lv2:binary can be found at
+# <http://lv2plug.in/ns/lv2core#binary>.  If you encounter a URI in some data
+# which you do not understand, try this first.
+#
+# Note the URI of a plugin does NOT need to be an actual web address, it's just
+# a global identifier.  It is, however, a good idea to use an actual web
+# address if possible, since it can be used to easily access documentation,
+# downloads, etc.  Note there are compatibility rules for when the URI of a
+# plugin must be changed, see the http://lv2plug.in/ns/lv2core[LV2 specification]
+# for details.
+#
+# AUTHORS MUST NOT CREATE URIS AT DOMAINS THEY DO NOT CONTROL WITHOUT
+# PERMISSION, AND *ESPECIALLY* MUST NOT CREATE SYNTACTICALLY INVALID URIS,
+# E.G. WHERE THE PORTION FOLLOWING "http://" IS NOT AN ACTUAL DOMAIN NAME.  If
+# you need an example URI, the domain http://example.org/ is reserved for this
+# purpose.  It is best to use web URIs, e.g. at the domain where plugins are
+# hosted for download, even if no actual documents are currently hosted there.
+# If this is truly impossible, use a URN, e.g. urn:myplugs:superamp.
+#
+# A detailed explanation of each statement follows.
+
+<http://lv2plug.in/plugins/eg-amp> a lv2:Plugin .
+
+# The `a`, as in ``is a'', is a Turtle shortcut for `rdf:type`.
+# `lv2:Plugin` expands to <http://lv2plug.in/ns/lv2core#Plugin> (using the
+# `lv2:` prefix above) which is the type of all LV2 plugins.
+# This statement means ``<http://lv2plug.in/plugins/eg-amp> is an LV2 plugin''.
+
+<http://lv2plug.in/plugins/eg-amp> lv2:binary <amp@LIB_EXT@> .
+
+# This says "this plugin has executable code ("binary") in the file
+# named "amp.so", which is located in this bundle.  The LV2 specification
+# defines that all relative URIs in manifest files are relative to the bundle
+# directory, so this refers to the file amp.so in the same directory as this
+# manifest.ttl file.
+
+<http://lv2plug.in/plugins/eg-amp> rdfs:seeAlso <amp.ttl> .
+
+# This says ``there is more information about this plugin located in the file
+# `amp.ttl`''.  The host will look at all such files when it needs to actually
+# use or investigate the plugin.
diff --git a/plugins/eg01-amp.lv2/waf b/plugins/eg01-amp.lv2/waf
new file mode 120000
index 0000000..59a1ac9
--- /dev/null
+++ b/plugins/eg01-amp.lv2/waf
@@ -0,0 +1 @@
+../../waf
\ No newline at end of file
diff --git a/plugins/eg01-amp.lv2/wscript b/plugins/eg01-amp.lv2/wscript
new file mode 100644
index 0000000..d4295ff
--- /dev/null
+++ b/plugins/eg01-amp.lv2/wscript
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import re
+
+# Variables for 'waf dist'
+APPNAME = 'eg-amp.lv2'
+VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('compiler_c')
+    autowaf.set_options(opt)
+
+def configure(conf):
+    conf.load('compiler_c')
+    autowaf.configure(conf)
+    autowaf.set_c99_mode(conf)
+    autowaf.display_header('Amp Configuration')
+
+    if not autowaf.is_child():
+        autowaf.check_pkg(conf, 'lv2', uselib_store='LV2')
+        
+    conf.check(features='c cprogram', lib='m', uselib_store='M', mandatory=False) 
+
+    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
+    print('')
+
+def build(bld):
+    bundle = 'eg-amp.lv2'
+
+    # Make a pattern for shared objects without the 'lib' prefix
+    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
+    module_ext = module_pat[module_pat.rfind('.'):]
+
+    # Build manifest.ttl by substitution (for portable lib extension)
+    bld(features     = 'subst',
+        source       = 'manifest.ttl.in',
+        target       = '%s/%s' % (bundle, 'manifest.ttl'),
+        install_path = '${LV2DIR}/%s' % bundle,
+        LIB_EXT      = module_ext)
+
+    # Copy other data files to build bundle (build/eg-amp.lv2)
+    for i in ['amp.ttl']:
+        bld(features     = 'subst',
+            is_copy      = True,
+            source       = i,
+            target       = '%s/%s' % (bundle, i),
+            install_path = '${LV2DIR}/%s' % bundle)
+
+    # Use LV2 headers from parent directory if building as a sub-project
+    includes = None
+    if autowaf.is_child:
+        includes = '../..'
+
+    # Build plugin library
+    obj = bld(features     = 'c cshlib',
+              source       = 'amp.c',
+              name         = 'amp',
+              target       = '%s/amp' % bundle,
+              install_path = '${LV2DIR}/%s' % bundle,
+              uselib       = 'M LV2',
+              includes     = includes)
+    obj.env.cshlib_PATTERN = module_pat
+
diff --git a/plugins/eg02-midigate.lv2/README.txt b/plugins/eg02-midigate.lv2/README.txt
new file mode 100644
index 0000000..8f4a0f0
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/README.txt
@@ -0,0 +1,10 @@
+== MIDI Gate ==
+
+This plugin demonstrates:
+
+ * Receiving MIDI input
+
+ * Processing audio based on MIDI events with sample accuracy
+
+ * Supporting MIDI programs which the host can control/automate, or present a
+   user interface for with human readable labels
diff --git a/plugins/eg02-midigate.lv2/manifest.ttl.in b/plugins/eg02-midigate.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d32f1dc
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/manifest.ttl.in
@@ -0,0 +1,10 @@
+# The manifest.ttl file follows the same template as the previous example.
+
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://lv2plug.in/plugins/eg-midigate>
+	a lv2:Plugin ;
+	lv2:binary <midigate@LIB_EXT@> ;
+	rdfs:seeAlso <midigate.ttl> .
diff --git a/plugins/eg02-midigate.lv2/midigate.c b/plugins/eg02-midigate.lv2/midigate.c
new file mode 100644
index 0000000..3b74bfc
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/midigate.c
@@ -0,0 +1,223 @@
+/*
+  Copyright 2013 David Robillard <d@drobilla.net>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdio.h>
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/util.h"
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define MIDIGATE_URI "http://lv2plug.in/plugins/eg-midigate"
+
+typedef enum {
+	MIDIGATE_CONTROL = 0,
+	MIDIGATE_IN      = 1,
+	MIDIGATE_OUT     = 2
+} PortIndex;
+
+typedef struct {
+	// Port buffers
+	const LV2_Atom_Sequence* control;
+	const float*             in;
+	float*                   out;
+
+	// Features
+	LV2_URID_Map* map;
+
+	struct {
+		LV2_URID midi_MidiEvent;
+	} uris;
+
+	unsigned n_active_notes;
+	unsigned program;
+} Midigate;
+
+static LV2_Handle
+instantiate(const LV2_Descriptor*     descriptor,
+            double                    rate,
+            const char*               bundle_path,
+            const LV2_Feature* const* features)
+{
+	/** Scan features array for the URID feature we need. */
+	LV2_URID_Map* map = NULL;
+	for (int i = 0; features[i]; ++i) {
+		if (!strcmp(features[i]->URI, LV2_URID__map)) {
+			map = (LV2_URID_Map*)features[i]->data;
+			break;
+		}
+	}
+	if (!map) {
+		/**
+		   No URID feature given.  This is a host bug since we require this
+		   feature, but should be handled gracefully anyway.
+		*/
+		return NULL;
+	}
+
+	Midigate* self = (Midigate*)calloc(1, sizeof(Midigate));
+	self->map = map;
+	self->uris.midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
+
+	return (LV2_Handle)self;
+}
+
+static void
+connect_port(LV2_Handle instance,
+             uint32_t   port,
+             void*      data)
+{
+	Midigate* self = (Midigate*)instance;
+
+	switch ((PortIndex)port) {
+	case MIDIGATE_CONTROL:
+		self->control = (const LV2_Atom_Sequence*)data;
+		break;
+	case MIDIGATE_IN:
+		self->in = (const float*)data;
+		break;
+	case MIDIGATE_OUT:
+		self->out = (float*)data;
+		break;
+	}
+}
+
+static void
+activate(LV2_Handle instance)
+{
+	Midigate* self = (Midigate*)instance;
+	self->n_active_notes = 0;
+	self->program        = 0;
+}
+
+/**
+   A function to write a chunk of output, to be called from run().  If the gate
+   is high, then the input will be passed through for this chunk, otherwise
+   silence is written.
+*/
+static void
+write_output(Midigate* self, uint32_t offset, uint32_t len)
+{
+	const bool active = (self->program == 0)
+		? (self->n_active_notes > 0)
+		: (self->n_active_notes == 0);
+	if (active) {
+		memcpy(self->out + offset, self->in + offset, len * sizeof(float));
+	} else {
+		memset(self->out + offset, 0, len * sizeof(float));
+	}
+}
+
+/**
+   This plugin works through the cycle in chunks starting at offset zero.  The
+   +offset+ represents the current time within this this cycle, so
+   the output from 0 to +offset+ has already been written.
+
+   MIDI events are read in a loop.  In each iteration, the number of active
+   notes (on note on and note off) or the program (on program change) is
+   updated, then the output is written up until the current event time.  Then
+   +offset+ is updated and the next event is processed.  After the loop the
+   final chunk from the last event to the end of the cycle is emitted.
+
+   This pattern of iterating over input events and writing output along the way
+   is a common idiom for writing sample accurate output based on event input.
+
+   Note that this simple example simply writes input or zero for each sample
+   based on the gate.  A serious implementation would need to envelope the
+   transition to avoid aliasing.
+*/
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+	Midigate* self   = (Midigate*)instance;
+	uint32_t  offset = 0;
+
+	LV2_ATOM_SEQUENCE_FOREACH(self->control, ev) {
+		if (ev->body.type == self->uris.midi_MidiEvent) {
+			const uint8_t* const msg = (const uint8_t*)(ev + 1);
+			switch (lv2_midi_message_type(msg)) {
+			case LV2_MIDI_MSG_NOTE_ON:
+				++self->n_active_notes;
+				break;
+			case LV2_MIDI_MSG_NOTE_OFF:
+				--self->n_active_notes;
+				break;
+			case LV2_MIDI_MSG_PGM_CHANGE:
+				if (msg[1] == 0 || msg[1] == 1) {
+					self->program = msg[1];
+				}
+				break;
+			default: break;
+			}
+		}
+
+		write_output(self, offset, ev->time.frames - offset);
+		offset = ev->time.frames;
+	}
+
+	write_output(self, offset, sample_count - offset);
+}
+
+/**
+   We have no resources to free on deactivation.
+   Note that the next call to activate will re-initialise the state, namely
+   self->n_active_notes, so there is no need to do so here.
+*/
+static void
+deactivate(LV2_Handle instance)
+{}
+
+static void
+cleanup(LV2_Handle instance)
+{
+	free(instance);
+}
+
+/**
+   This plugin also has no extension data to return.
+*/
+static const void*
+extension_data(const char* uri)
+{
+	return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+	MIDIGATE_URI,
+	instantiate,
+	connect_port,
+	activate,
+	run,
+	deactivate,
+	cleanup,
+	extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+	switch (index) {
+	case 0:
+		return &descriptor;
+	default:
+		return NULL;
+	}
+}
diff --git a/plugins/eg02-midigate.lv2/midigate.ttl b/plugins/eg02-midigate.lv2/midigate.ttl
new file mode 100644
index 0000000..59ac815
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/midigate.ttl
@@ -0,0 +1,80 @@
+# The same set of namespace prefixes with two additions for LV2 extensions this
+# plugin uses: atom and urid.
+
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+<http://lv2plug.in/plugins/eg-midigate>
+	a lv2:Plugin ;
+	doap:name "Example MIDI Gate" ;
+	doap:license <http://opensource.org/licenses/isc> ;
+	lv2:project <http://lv2plug.in/ns/lv2> ;
+	lv2:requiredFeature urid:map ;
+	lv2:optionalFeature lv2:hardRTCapable ;
+# Describe program banks so the host can automate and/or present a user
+# interface.  Describing supported programs (or any other MIDI event) is not
+# required, but is a good idea since it allows hosts to make better use of
+# plugins.  This plugin has a single bank of two programs, which have a
+# (mandatory) label, and an (optional) comment to describe their meaning in
+# more detail.
+#
+# Both programs and the bank have an index, which corresponds to the MIDI bank
+# and program numbers that will activate them.  Since there are other ways to
+# change programs (not used here), an index is not strictly required, but must
+# be present to support program changes from MIDI.
+	lv2:bank [
+		rdfs:label "Default" ;
+		lv2:index 0 ;
+		lv2:program [
+			lv2:index 0 ;
+			rdfs:label "Normal" ;
+			rdfs:comment "Input is passed through if notes are active."
+		] , [
+			lv2:index 1 ;
+			rdfs:label "Inverted" ;
+			rdfs:comment "Input is passed through if no notes are active."
+		]
+	] ;
+# This plugin has three ports.  There is an audio input and output as before,
+# as well as a new AtomPort.  An AtomPort buffer contains an Atom, which is a
+# generic container for any type of data.  In this case, we want to receive
+# MIDI events, so the (mandatory) +atom:bufferType+ is atom:Sequence, which is
+# a series of events with time stamps.
+#
+# Events themselves are also generic and can contain any type of data, but in
+# this case we are only interested in MIDI events.  The (optional)
+# +atom:supports+ property describes which event types are supported.  Though
+# not required, this information should always be given so the host knows what
+# types of event it can expect the plugin to understand.
+#
+# The (optional) +lv2:designation+ of this port is +lv2:control+, which
+# indicates that this is the "main" control port where the host should send
+# events it expects to configure the plugin, in this case changing the MIDI
+# program.  This is necessary since it is possible to have several MIDI input
+# ports, though typically it is best to have one.
+	lv2:port [
+		a lv2:InputPort ,
+			atom:AtomPort ;
+		atom:bufferType atom:Sequence ;
+		atom:supports midi:MidiEvent ;
+		lv2:designation lv2:control ;
+		lv2:index 0 ;
+		lv2:symbol "control" ;
+		lv2:name "Control"
+	] , [
+		a lv2:AudioPort ,
+			lv2:InputPort ;
+		lv2:index 1 ;
+		lv2:symbol "in" ;
+		lv2:name "In"
+	] , [
+		a lv2:AudioPort ,
+			lv2:OutputPort ;
+		lv2:index 2 ;
+		lv2:symbol "out" ;
+		lv2:name "Out"
+	] .
diff --git a/plugins/eg02-midigate.lv2/waf b/plugins/eg02-midigate.lv2/waf
new file mode 120000
index 0000000..59a1ac9
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/waf
@@ -0,0 +1 @@
+../../waf
\ No newline at end of file
diff --git a/plugins/eg02-midigate.lv2/wscript b/plugins/eg02-midigate.lv2/wscript
new file mode 100644
index 0000000..44336af
--- /dev/null
+++ b/plugins/eg02-midigate.lv2/wscript
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import re
+
+# Variables for 'waf dist'
+APPNAME = 'eg-midigate.lv2'
+VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('compiler_c')
+    autowaf.set_options(opt)
+
+def configure(conf):
+    conf.load('compiler_c')
+    autowaf.configure(conf)
+    autowaf.set_c99_mode(conf)
+    autowaf.display_header('Midigate Configuration')
+
+    if not autowaf.is_child():
+        autowaf.check_pkg(conf, 'lv2', uselib_store='LV2')
+        
+    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
+    print('')
+
+def build(bld):
+    bundle = 'eg-midigate.lv2'
+
+    # Make a pattern for shared objects without the 'lib' prefix
+    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
+    module_ext = module_pat[module_pat.rfind('.'):]
+
+    # Build manifest.ttl by substitution (for portable lib extension)
+    bld(features     = 'subst',
+        source       = 'manifest.ttl.in',
+        target       = '%s/%s' % (bundle, 'manifest.ttl'),
+        install_path = '${LV2DIR}/%s' % bundle,
+        LIB_EXT      = module_ext)
+
+    # Copy other data files to build bundle (build/eg-midigate.lv2)
+    for i in ['midigate.ttl']:
+        bld(features     = 'subst',
+            is_copy      = True,
+            source       = i,
+            target       = '%s/%s' % (bundle, i),
+            install_path = '${LV2DIR}/%s' % bundle)
+
+    # Use LV2 headers from parent directory if building as a sub-project
+    includes = None
+    if autowaf.is_child:
+        includes = '../..'
+
+    # Build plugin library
+    obj = bld(features     = 'c cshlib',
+              source       = 'midigate.c',
+              name         = 'midigate',
+              target       = '%s/midigate' % bundle,
+              install_path = '${LV2DIR}/%s' % bundle,
+              uselib       = 'LV2',
+              includes     = includes)
+    obj.env.cshlib_PATTERN = module_pat
+
diff --git a/plugins/eg03-metro.lv2/README.txt b/plugins/eg03-metro.lv2/README.txt
new file mode 100644
index 0000000..5e9a84a
--- /dev/null
+++ b/plugins/eg03-metro.lv2/README.txt
@@ -0,0 +1,9 @@
+== Metronome ==
+
+This plugin demonstrates tempo synchronisation by clicking on every beat.  The
+host sends this information to the plugin as events, so an event with new time
+and tempo information will be received whenever there is a change.
+
+Time is assumed to continue rolling at the tempo and speed defined by the last
+received tempo event, even across cycles, until a new tempo event is received
+or the plugin is deactivated.
diff --git a/plugins/eg03-metro.lv2/manifest.ttl.in b/plugins/eg03-metro.lv2/manifest.ttl.in
new file mode 100644
index 0000000..bd93f66
--- /dev/null
+++ b/plugins/eg03-metro.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://lv2plug.in/plugins/eg-metro>
+	a lv2:Plugin ;
+	lv2:binary <metro@LIB_EXT@> ;
+	rdfs:seeAlso <metro.ttl> .
diff --git a/plugins/eg03-metro.lv2/metro.c b/plugins/eg03-metro.lv2/metro.c
new file mode 100644
index 0000000..d2ac982
--- /dev/null
+++ b/plugins/eg03-metro.lv2/metro.c
@@ -0,0 +1,345 @@
+/*
+  LV2 Metronome Example Plugin
+  Copyright 2012 David Robillard <d@drobilla.net>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __cplusplus
+#    include <stdbool.h>
+#endif
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/util.h"
+#include "lv2/lv2plug.in/ns/ext/time/time.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#ifndef M_PI
+#    define M_PI 3.14159265
+#endif
+
+#define EG_METRO_URI "http://lv2plug.in/plugins/eg-metro"
+
+typedef struct {
+	LV2_URID atom_Blank;
+	LV2_URID atom_Float;
+	LV2_URID atom_Path;
+	LV2_URID atom_Resource;
+	LV2_URID atom_Sequence;
+	LV2_URID time_Position;
+	LV2_URID time_barBeat;
+	LV2_URID time_beatsPerMinute;
+	LV2_URID time_speed;
+} MetroURIs;
+
+static const double attack_s = 0.005;
+static const double decay_s  = 0.075;
+
+enum {
+	METRO_CONTROL = 0,
+	METRO_NOTIFY  = 1,
+	METRO_OUT     = 2
+};
+
+/** During execution this plugin can be in one of 3 states: */
+typedef enum {
+	STATE_ATTACK,  // Envelope rising
+	STATE_DECAY,   // Envelope lowering
+	STATE_OFF      // Silent
+} State;
+
+/** The plugin instance structure: */
+typedef struct {
+	LV2_URID_Map* map;   // URID map feature
+	MetroURIs     uris;  // Cache of mapped URIDs
+
+	struct {
+		LV2_Atom_Sequence* control;
+		LV2_Atom_Sequence* notify;
+		float*             output;
+	} ports;
+
+	/** The rate, bpm, and speed are the basic information sent by the host. */
+	double   rate;
+	float    bpm;
+	float    speed;
+
+	/** To keep track of when to play the next click, we need to keep track of
+	    a few pieces of information: */
+
+	/** - The frames since the start of the last click is stored */
+	uint32_t elapsed_len;
+
+	/** - The current play offset in the wave */
+	uint32_t wave_offset;
+
+	/** - The current play state (attack, decay, or off) */
+	State state;
+
+	/** The wave to play is a simple sine wave generated at instantiation time
+	    based on the sample rate.  The length in frames is stored in order to
+	    continuously play the wave in a cycle to avoid discontinuity clicks. */
+	float*   wave;
+	uint32_t wave_len;
+
+	/** The continuously playing sine wave is enveloped to provide an actual
+	    metronome tick.  This plugin uses a simple AD envelope with fixed
+	    parameters.  A more sophisticated implementation might use a more
+	    advanced envelope and allow the user to modify these parameters. */
+	uint32_t attack_len;
+	uint32_t decay_len;
+} Metro;
+
+static void
+connect_port(LV2_Handle instance,
+             uint32_t   port,
+             void*      data)
+{
+	Metro* self = (Metro*)instance;
+
+	switch (port) {
+	case METRO_CONTROL:
+		self->ports.control = (LV2_Atom_Sequence*)data;
+		break;
+	case METRO_NOTIFY:
+		self->ports.notify = (LV2_Atom_Sequence*)data;
+		break;
+	case METRO_OUT:
+		self->ports.output = (float*)data;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+activate(LV2_Handle instance)
+{
+	Metro* self = (Metro*)instance;
+
+	self->elapsed_len = 0;
+	self->wave_offset = 0;
+	self->state       = STATE_OFF;
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor*     descriptor,
+            double                    rate,
+            const char*               path,
+            const LV2_Feature* const* features)
+{
+	Metro* self = (Metro*)calloc(1, sizeof(Metro));
+	if (!self) {
+		return NULL;
+	}
+
+	/** Scan host features for URID map */
+	LV2_URID_Map* map = NULL;
+	for (int i = 0; features[i]; ++i) {
+		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
+			map = (LV2_URID_Map*)features[i]->data;
+		}
+	}
+	if (!map) {
+		fprintf(stderr, "Host does not support urid:map.\n");
+		free(self);
+		return NULL;
+	}
+
+	/** Map URIS */
+	MetroURIs* const uris = &self->uris;
+	self->map = map;
+	uris->atom_Blank          = map->map(map->handle, LV2_ATOM__Blank);
+	uris->atom_Float          = map->map(map->handle, LV2_ATOM__Float);
+	uris->atom_Path           = map->map(map->handle, LV2_ATOM__Path);
+	uris->atom_Resource       = map->map(map->handle, LV2_ATOM__Resource);
+	uris->atom_Sequence       = map->map(map->handle, LV2_ATOM__Sequence);
+	uris->time_Position       = map->map(map->handle, LV2_TIME__Position);
+	uris->time_barBeat        = map->map(map->handle, LV2_TIME__barBeat);
+	uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
+	uris->time_speed          = map->map(map->handle, LV2_TIME__speed);
+
+	/** Initialise fields */
+	self->rate       = rate;
+	self->bpm        = 120.0f;
+	self->attack_len = attack_s * rate;
+	self->decay_len  = decay_s * rate;
+	self->state      = STATE_OFF;
+
+	/** Generate one cycle of a sine wave at the desired frequency. */
+	const double freq = 440.0 * 2.0;
+	const double amp  = 0.5;
+	self->wave_len = rate / freq;
+	self->wave     = (float*)malloc(self->wave_len * sizeof(float));
+	for (uint32_t i = 0; i < self->wave_len; ++i) {
+		self->wave[i] = sin(i * 2 * M_PI * freq / rate) * amp;
+	}
+
+	return (LV2_Handle)self;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+	free(instance);
+}
+
+static void
+play(Metro* self, uint32_t begin, uint32_t end)
+{
+	float* const   output          = self->ports.output;
+	const uint32_t frames_per_beat = 60.0f / self->bpm * self->rate;
+
+	if (self->speed == 0.0f) {
+		memset(output, 0, (end - begin) * sizeof(float));
+		return;
+	}
+
+	for (uint32_t i = begin; i < end; ++i) {
+		switch (self->state) {
+		case STATE_ATTACK:
+			/** Amplitude increases from 0..1 until attack_len */
+			output[i] = self->wave[self->wave_offset] *
+				self->elapsed_len / (float)self->attack_len;
+			if (self->elapsed_len >= self->attack_len) {
+				self->state = STATE_DECAY;
+			}
+			break;
+		case STATE_DECAY:
+			/** Amplitude decreases from 1..0 until attack_len + decay_len */
+			output[i] = 0.0f;
+			output[i] = self->wave[self->wave_offset] *
+				(1 - ((self->elapsed_len - self->attack_len) /
+				      (float)self->decay_len));
+			if (self->elapsed_len >= self->attack_len + self->decay_len) {
+				self->state = STATE_OFF;
+			}
+			break;
+		case STATE_OFF:
+			output[i] = 0.0f;
+		}
+
+		/** We continuously play the sine wave regardless of envelope */
+		self->wave_offset = (self->wave_offset + 1) % self->wave_len;
+
+		/** Update elapsed time and start attack if necessary */
+		if (++self->elapsed_len == frames_per_beat) {
+			self->state       = STATE_ATTACK;
+			self->elapsed_len = 0;
+		}
+	}
+}
+
+static void
+update_position(Metro* self, const LV2_Atom_Object* obj)
+{
+	const MetroURIs* uris = &self->uris;
+
+	/** Received new transport position/speed */
+	LV2_Atom *beat = NULL, *bpm = NULL, *speed = NULL;
+	lv2_atom_object_get(obj,
+	                    uris->time_barBeat, &beat,
+	                    uris->time_beatsPerMinute, &bpm,
+	                    uris->time_speed, &speed,
+	                    NULL);
+	if (bpm && bpm->type == uris->atom_Float) {
+		/** Tempo changed, update BPM */
+		self->bpm = ((LV2_Atom_Float*)bpm)->body;
+	}
+	if (speed && speed->type == uris->atom_Float) {
+		/** Speed changed, e.g. 0 (stop) to 1 (play) */
+		self->speed = ((LV2_Atom_Float*)speed)->body;
+	}
+	if (beat && beat->type == uris->atom_Float) {
+		/** Received a beat position, synchronise.
+		    This is a simple hard sync that may cause clicks.
+		    A real plugin would do something more graceful.
+		*/
+		const float frames_per_beat = 60.0f / self->bpm * self->rate;
+		const float bar_beats       = ((LV2_Atom_Float*)beat)->body;
+		const float beat_beats      = bar_beats - floorf(bar_beats);
+		self->elapsed_len           = beat_beats * frames_per_beat;
+		if (self->elapsed_len < self->attack_len) {
+			self->state = STATE_ATTACK;
+		} else if (self->elapsed_len < self->attack_len + self->decay_len) {
+			self->state = STATE_DECAY;
+		} else {
+			self->state = STATE_OFF;
+		}
+	}
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+	Metro*           self = (Metro*)instance;
+	const MetroURIs* uris = &self->uris;
+
+	/** Empty notify output for now */
+	LV2_Atom_Sequence* notify = self->ports.notify;
+	notify->atom.type = self->uris.atom_Sequence;
+	notify->atom.size = sizeof(LV2_Atom_Sequence_Body);
+	notify->body.unit = notify->body.pad = 0;
+
+	/** Work forwards in time frame by frame, handling events as we go */
+	const LV2_Atom_Sequence* in     = self->ports.control;
+	uint32_t                 last_t = 0;
+	for (LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body);
+	     !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev);
+	     ev = lv2_atom_sequence_next(ev)) {
+
+		/** Play the click for the time slice from last_t until now */
+		play(self, last_t, ev->time.frames);
+
+		if (ev->body.type == uris->atom_Blank) {
+			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
+			if (obj->body.otype == uris->time_Position) {
+				/** Received position information, update */
+				update_position(self, obj);
+			}
+		}
+
+		/** Update time for next iteration and move to next event*/
+		last_t = ev->time.frames;
+	}
+
+	/** Play for remainder of cycle */
+	play(self, last_t, sample_count);
+}
+
+static const LV2_Descriptor descriptor = {
+	EG_METRO_URI,
+	instantiate,
+	connect_port,
+	activate,
+	run,
+	NULL,  // deactivate,
+	cleanup,
+	NULL,  // extension_data
+};
+
+LV2_SYMBOL_EXPORT const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+	switch (index) {
+	case 0:
+		return &descriptor;
+	default:
+		return NULL;
+	}
+}
diff --git a/plugins/eg03-metro.lv2/metro.ttl b/plugins/eg03-metro.lv2/metro.ttl
new file mode 100644
index 0000000..a6f297f
--- /dev/null
+++ b/plugins/eg03-metro.lv2/metro.ttl
@@ -0,0 +1,39 @@
+@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix time: <http://lv2plug.in/ns/ext/time#> .
+@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
+
+<http://lv2plug.in/plugins/eg-metro>
+	a lv2:Plugin ;
+	doap:name "Example Metronome" ;
+	doap:license <http://opensource.org/licenses/isc> ;
+	lv2:project <http://lv2plug.in/ns/lv2> ;
+	lv2:requiredFeature urid:map ;
+	lv2:optionalFeature lv2:hardRTCapable ;
+	lv2:port [
+		a lv2:InputPort ,
+			atom:AtomPort ;
+		atom:bufferType atom:Sequence ;
+# Since this port supports time:Position, the host knows to deliver time and
+# tempo information
+		atom:supports time:Position ;
+		lv2:index 0 ;
+		lv2:symbol "control" ;
+		lv2:name "Control" ;
+	] , [
+		a lv2:OutputPort ,
+			atom:AtomPort ;
+		atom:bufferType atom:Sequence ;
+		atom:supports <http://lv2plug.in/ns/ext/patch#Patch> ;
+		lv2:portProperty lv2:connectionOptional ;
+		lv2:index 1 ;
+		lv2:symbol "notify" ;
+		lv2:name "Notify" ;
+	] , [
+		a lv2:AudioPort ,
+			lv2:OutputPort ;
+		lv2:index 2 ;
+		lv2:symbol "out" ;
+		lv2:name "Out" ;
+	] .
diff --git a/plugins/eg03-metro.lv2/waf b/plugins/eg03-metro.lv2/waf
new file mode 120000
index 0000000..59a1ac9
--- /dev/null
+++ b/plugins/eg03-metro.lv2/waf
@@ -0,0 +1 @@
+../../waf
\ No newline at end of file
diff --git a/plugins/eg03-metro.lv2/wscript b/plugins/eg03-metro.lv2/wscript
new file mode 100644
index 0000000..40642b6
--- /dev/null
+++ b/plugins/eg03-metro.lv2/wscript
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import re
+
+# Variables for 'waf dist'
+APPNAME = 'eg-metro.lv2'
+VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('compiler_c')
+    autowaf.set_options(opt)
+
+def configure(conf):
+    conf.load('compiler_c')
+    autowaf.configure(conf)
+    autowaf.set_c99_mode(conf)
+    autowaf.display_header('Metro Configuration')
+
+    if not autowaf.is_child():
+        autowaf.check_pkg(conf, 'lv2', atleast_version='0.2.0', uselib_store='LV2')
+
+    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
+    print('')
+
+def build(bld):
+    bundle = 'eg-metro.lv2'
+
+    # Make a pattern for shared objects without the 'lib' prefix
+    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
+    module_ext = module_pat[module_pat.rfind('.'):]
+
+    # Build manifest.ttl by substitution (for portable lib extension)
+    bld(features     = 'subst',
+        source       = 'manifest.ttl.in',
+        target       = '%s/%s' % (bundle, 'manifest.ttl'),
+        install_path = '${LV2DIR}/%s' % bundle,
+        LIB_EXT      = module_ext)
+    
+    # Copy other data files to build bundle (build/eg-metro.lv2)
+    bld(features     = 'subst',
+        is_copy      = True,
+        source       = 'metro.ttl',
+        target       = '%s/metro.ttl' % bundle,
+        install_path = '${LV2DIR}/%s' % bundle)
+
+    # Use LV2 headers from parent directory if building as a sub-project
+    includes = ['.']
+    if autowaf.is_child:
+        includes += ['../..']
+
+    # Build plugin library
+    obj = bld(features     = 'c cshlib',
+              source       = 'metro.c',
+              name         = 'metro',
+              target       = '%s/metro' % bundle,
+              install_path = '${LV2DIR}/%s' % bundle,
+              use          = 'LV2',
+              includes     = includes)
+    obj.env.cshlib_PATTERN = module_pat
+
diff --git a/plugins/eg04-sampler.lv2/README.txt b/plugins/eg04-sampler.lv2/README.txt
new file mode 100644
index 0000000..c1cac46
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/README.txt
@@ -0,0 +1 @@
+== Sampler ==
diff --git a/plugins/eg04-sampler.lv2/click.wav b/plugins/eg04-sampler.lv2/click.wav
new file mode 100644
index 0000000..520a18c
Binary files /dev/null and b/plugins/eg04-sampler.lv2/click.wav differ
diff --git a/plugins/eg04-sampler.lv2/manifest.ttl.in b/plugins/eg04-sampler.lv2/manifest.ttl.in
new file mode 100644
index 0000000..b4fa23e
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/manifest.ttl.in
@@ -0,0 +1,13 @@
+@prefix lv2:  <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://lv2plug.in/plugins/eg-sampler>
+	a lv2:Plugin ;
+	lv2:binary <sampler@LIB_EXT@> ;
+	rdfs:seeAlso <sampler.ttl> .
+
+<http://lv2plug.in/plugins/eg-sampler#ui>
+	a ui:GtkUI ;
+	ui:binary <sampler_ui@LIB_EXT@> ;
+	rdfs:seeAlso <sampler.ttl> .
diff --git a/plugins/eg04-sampler.lv2/sampler.c b/plugins/eg04-sampler.lv2/sampler.c
new file mode 100644
index 0000000..5bb4e54
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/sampler.c
@@ -0,0 +1,498 @@
+/*
+  LV2 Sampler Example Plugin
+  Copyright 2011-2012 David Robillard <d@drobilla.net>
+  Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
+  Copyright 2011 James Morris <jwm.art.net@gmail.com>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+   @file sampler.c Sampler Plugin
+
+   A simple example of an LV2 sampler that dynamically loads a single sample
+   (based on incoming events) and triggers their playback (based on incoming
+   MIDI note events).
+
+   This plugin illustrates:
+   - UI <=> Plugin communication via events
+   - Use of the worker extension for non-realtime tasks (sample loading)
+   - Use of the log extension to print log messages via the host
+   - Saving plugin state via the state extension
+   - Dynamic plugin control via the same properties saved to state
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __cplusplus
+#    include <stdbool.h>
+#endif
+
+#include <sndfile.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/atom/util.h"
+#include "lv2/lv2plug.in/ns/ext/log/log.h"
+#include "lv2/lv2plug.in/ns/ext/log/logger.h"
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#include "./uris.h"
+
+enum {
+	SAMPLER_CONTROL = 0,
+	SAMPLER_NOTIFY  = 1,
+	SAMPLER_OUT     = 2
+};
+
+static const char* default_sample_file = "click.wav";
+
+typedef struct {
+	SF_INFO info;      /**< Info about sample from sndfile */
+	float*  data;      /**< Sample data in float */
+	char*   path;      /**< Path of file */
+	size_t  path_len;  /**< Length of path */
+} Sample;
+
+typedef struct {
+	/* Features */
+	LV2_URID_Map*        map;
+	LV2_Worker_Schedule* schedule;
+	LV2_Log_Log*         log;
+
+	/* Forge for creating atoms */
+	LV2_Atom_Forge forge;
+
+	/* Logger convenience API */
+	LV2_Log_Logger logger;
+
+	/* Sample */
+	Sample* sample;
+
+	/* Ports */
+	const LV2_Atom_Sequence* control_port;
+	LV2_Atom_Sequence*       notify_port;
+	float*                   output_port;
+
+	/* Forge frame for notify port (for writing worker replies). */
+	LV2_Atom_Forge_Frame notify_frame;
+
+	/* URIs */
+	SamplerURIs uris;
+
+	/* Current position in run() */
+	uint32_t frame_offset;
+
+	/* Playback state */
+	sf_count_t frame;
+	bool       play;
+} Sampler;
+
+/**
+   An atom-like message used internally to apply/free samples.
+
+   This is only used internally to communicate with the worker, it is never
+   sent to the outside world via a port since it is not POD.  It is convenient
+   to use an Atom header so actual atoms can be easily sent through the same
+   ringbuffer.
+*/
+typedef struct {
+	LV2_Atom atom;
+	Sample*  sample;
+} SampleMessage;
+
+/**
+   Load a new sample and return it.
+
+   Since this is of course not a real-time safe action, this is called in the
+   worker thread only.  The sample is loaded and returned only, plugin state is
+   not modified.
+*/
+static Sample*
+load_sample(Sampler* self, const char* path)
+{
+	const size_t path_len  = strlen(path);
+
+	lv2_log_trace(&self->logger, "Loading sample %s\n", path);
+
+	Sample* const  sample  = (Sample*)malloc(sizeof(Sample));
+	SF_INFO* const info    = &sample->info;
+	SNDFILE* const sndfile = sf_open(path, SFM_READ, info);
+
+	if (!sndfile || !info->frames || (info->channels != 1)) {
+		lv2_log_error(&self->logger, "Failed to open sample '%s'\n", path);
+		free(sample);
+		return NULL;
+	}
+
+	/* Read data */
+	float* const data = malloc(sizeof(float) * info->frames);
+	if (!data) {
+		lv2_log_error(&self->logger, "Failed to allocate memory for sample\n");
+		return NULL;
+	}
+	sf_seek(sndfile, 0ul, SEEK_SET);
+	sf_read_float(sndfile, data, info->frames);
+	sf_close(sndfile);
+
+	/* Fill sample struct and return it. */
+	sample->data     = data;
+	sample->path     = (char*)malloc(path_len + 1);
+	sample->path_len = path_len;
+	memcpy(sample->path, path, path_len + 1);
+
+	return sample;
+}
+
+static void
+free_sample(Sampler* self, Sample* sample)
+{
+	if (sample) {
+		lv2_log_trace(&self->logger, "Freeing %s\n", sample->path);
+		free(sample->path);
+		free(sample->data);
+		free(sample);
+	}
+}
+
+/**
+   Do work in a non-realtime thread.
+
+   This is called for every piece of work scheduled in the audio thread using
+   self->schedule->schedule_work().  A reply can be sent back to the audio
+   thread using the provided respond function.
+*/
+static LV2_Worker_Status
+work(LV2_Handle                  instance,
+     LV2_Worker_Respond_Function respond,
+     LV2_Worker_Respond_Handle   handle,
+     uint32_t                    size,
+     const void*                 data)
+{
+	Sampler*        self = (Sampler*)instance;
+	const LV2_Atom* atom = (const LV2_Atom*)data;
+	if (atom->type == self->uris.eg_freeSample) {
+		/* Free old sample */
+		const SampleMessage* msg = (const SampleMessage*)data;
+		free_sample(self, msg->sample);
+	} else {
+		/* Handle set message (load sample). */
+		const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data;
+
+		/* Get file path from message */
+		const LV2_Atom* file_path = read_set_file(&self->uris, obj);
+		if (!file_path) {
+			return LV2_WORKER_ERR_UNKNOWN;
+		}
+
+		/* Load sample. */
+		Sample* sample = load_sample(self, LV2_ATOM_BODY_CONST(file_path));
+		if (sample) {
+			/* Loaded sample, send it to run() to be applied. */
+			respond(handle, sizeof(sample), &sample);
+		}
+	}
+
+	return LV2_WORKER_SUCCESS;
+}
+
+/**
+   Handle a response from work() in the audio thread.
+
+   When running normally, this will be called by the host after run().  When
+   freewheeling, this will be called immediately at the point the work was
+   scheduled.
+*/
+static LV2_Worker_Status
+work_response(LV2_Handle  instance,
+              uint32_t    size,
+              const void* data)
+{
+	Sampler* self = (Sampler*)instance;
+
+	SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample },
+	                      self->sample };
+
+	/* Send a message to the worker to free the current sample */
+	self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg);
+
+	/* Install the new sample */
+	self->sample = *(Sample*const*)data;
+
+	/* Send a notification that we're using a new sample. */
+	lv2_atom_forge_frame_time(&self->forge, self->frame_offset);
+	write_set_file(&self->forge, &self->uris,
+	               self->sample->path,
+	               self->sample->path_len);
+
+	return LV2_WORKER_SUCCESS;
+}
+
+static void
+connect_port(LV2_Handle instance,
+             uint32_t   port,
+             void*      data)
+{
+	Sampler* self = (Sampler*)instance;
+	switch (port) {
+	case SAMPLER_CONTROL:
+		self->control_port = (const LV2_Atom_Sequence*)data;
+		break;
+	case SAMPLER_NOTIFY:
+		self->notify_port = (LV2_Atom_Sequence*)data;
+		break;
+	case SAMPLER_OUT:
+		self->output_port = (float*)data;
+		break;
+	default:
+		break;
+	}
+}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor*     descriptor,
+            double                    rate,
+            const char*               path,
+            const LV2_Feature* const* features)
+{
+	/* Allocate and initialise instance structure. */
+	Sampler* self = (Sampler*)malloc(sizeof(Sampler));
+	if (!self) {
+		return NULL;
+	}
+	memset(self, 0, sizeof(Sampler));
+
+	/* Get host features */
+	for (int i = 0; features[i]; ++i) {
+		if (!strcmp(features[i]->URI, LV2_URID__map)) {
+			self->map = (LV2_URID_Map*)features[i]->data;
+		} else if (!strcmp(features[i]->URI, LV2_WORKER__schedule)) {
+			self->schedule = (LV2_Worker_Schedule*)features[i]->data;
+		} else if (!strcmp(features[i]->URI, LV2_LOG__log)) {
+			self->log = (LV2_Log_Log*)features[i]->data;
+		}
+	}
+	if (!self->map) {
+		lv2_log_error(&self->logger, "Missing feature urid:map\n");
+		goto fail;
+	} else if (!self->schedule) {
+		lv2_log_error(&self->logger, "Missing feature work:schedule\n");
+		goto fail;
+	}
+
+	/* Map URIs and initialise forge/logger */
+	map_sampler_uris(self->map, &self->uris);
+	lv2_atom_forge_init(&self->forge, self->map);
+	lv2_log_logger_init(&self->logger, self->map, self->log);
+
+	/* Load the default sample file */
+	const size_t path_len    = strlen(path);
+	const size_t file_len    = strlen(default_sample_file);
+	const size_t len         = path_len + file_len;
+	char*        sample_path = (char*)malloc(len + 1);
+	snprintf(sample_path, len + 1, "%s%s", path, default_sample_file);
+	self->sample = load_sample(self, sample_path);
+	free(sample_path);
+
+	return (LV2_Handle)self;
+
+fail:
+	free(self);
+	return 0;
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+	Sampler* self = (Sampler*)instance;
+	free_sample(self, self->sample);
+	free(self);
+}
+
+static void
+run(LV2_Handle instance,
+    uint32_t   sample_count)
+{
+	Sampler*     self        = (Sampler*)instance;
+	SamplerURIs* uris        = &self->uris;
+	sf_count_t   start_frame = 0;
+	sf_count_t   pos         = 0;
+	float*       output      = self->output_port;
+
+	/* Set up forge to write directly to notify output port. */
+	const uint32_t notify_capacity = self->notify_port->atom.size;
+	lv2_atom_forge_set_buffer(&self->forge,
+	                          (uint8_t*)self->notify_port,
+	                          notify_capacity);
+
+	/* Start a sequence in the notify output port. */
+	lv2_atom_forge_sequence_head(&self->forge, &self->notify_frame, 0);
+
+	/* Read incoming events */
+	LV2_ATOM_SEQUENCE_FOREACH(self->control_port, ev) {
+		self->frame_offset = ev->time.frames;
+		if (ev->body.type == uris->midi_Event) {
+			const uint8_t* const msg = (const uint8_t*)(ev + 1);
+			switch (lv2_midi_message_type(msg)) {
+			case LV2_MIDI_MSG_NOTE_ON:
+				start_frame = ev->time.frames;
+				self->frame = 0;
+				self->play  = true;
+				break;
+			default:
+				break;
+			}
+		} else if (is_object_type(uris, ev->body.type)) {
+			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
+			if (obj->body.otype == uris->patch_Set) {
+				/* Received a set message, send it to the worker. */
+				lv2_log_trace(&self->logger, "Queueing set message\n");
+				self->schedule->schedule_work(self->schedule->handle,
+				                              lv2_atom_total_size(&ev->body),
+				                              &ev->body);
+			} else {
+				lv2_log_trace(&self->logger,
+				              "Unknown object type %d\n", obj->body.otype);
+			}
+		} else {
+			lv2_log_trace(&self->logger,
+			              "Unknown event type %d\n", ev->body.type);
+		}
+	}
+
+	/* Render the sample (possibly already in progress) */
+	if (self->play) {
+		uint32_t       f  = self->frame;
+		const uint32_t lf = self->sample->info.frames;
+
+		for (pos = 0; pos < start_frame; ++pos) {
+			output[pos] = 0;
+		}
+
+		for (; pos < sample_count && f < lf; ++pos, ++f) {
+			output[pos] = self->sample->data[f];
+		}
+
+		self->frame = f;
+
+		if (f == lf) {
+			self->play = false;
+		}
+	}
+
+	/* Add zeros to end if sample not long enough (or not playing) */
+	for (; pos < sample_count; ++pos) {
+		output[pos] = 0.0f;
+	}
+}
+
+static LV2_State_Status
+save(LV2_Handle                instance,
+     LV2_State_Store_Function  store,
+     LV2_State_Handle          handle,
+     uint32_t                  flags,
+     const LV2_Feature* const* features)
+{
+	Sampler* self = (Sampler*)instance;
+	if (!self->sample) {
+		return LV2_STATE_SUCCESS;
+	}
+
+	LV2_State_Map_Path* map_path = NULL;
+	for (int i = 0; features[i]; ++i) {
+		if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) {
+			map_path = (LV2_State_Map_Path*)features[i]->data;
+		}
+	}
+
+	char* apath = map_path->abstract_path(map_path->handle, self->sample->path);
+
+	store(handle,
+	      self->uris.eg_sample,
+	      apath,
+	      strlen(self->sample->path) + 1,
+	      self->uris.atom_Path,
+	      LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
+
+	free(apath);
+
+	return LV2_STATE_SUCCESS;
+}
+
+static LV2_State_Status
+restore(LV2_Handle                  instance,
+        LV2_State_Retrieve_Function retrieve,
+        LV2_State_Handle            handle,
+        uint32_t                    flags,
+        const LV2_Feature* const*   features)
+{
+	Sampler* self = (Sampler*)instance;
+
+	size_t   size;
+	uint32_t type;
+	uint32_t valflags;
+
+	const void* value = retrieve(
+		handle,
+		self->uris.eg_sample,
+		&size, &type, &valflags);
+
+	if (value) {
+		const char* path = (const char*)value;
+		lv2_log_trace(&self->logger, "Restoring file %s\n", path);
+		free_sample(self, self->sample);
+		self->sample = load_sample(self, path);
+	}
+
+	return LV2_STATE_SUCCESS;
+}
+
+static const void*
+extension_data(const char* uri)
+{
+	static const LV2_State_Interface  state  = { save, restore };
+	static const LV2_Worker_Interface worker = { work, work_response, NULL };
+	if (!strcmp(uri, LV2_STATE__interface)) {
+		return &state;
+	} else if (!strcmp(uri, LV2_WORKER__interface)) {
+		return &worker;
+	}
+	return NULL;
+}
+
+static const LV2_Descriptor descriptor = {
+	EG_SAMPLER_URI,
+	instantiate,
+	connect_port,
+	NULL,  // activate,
+	run,
+	NULL,  // deactivate,
+	cleanup,
+	extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+	switch (index) {
+	case 0:
+		return &descriptor;
+	default:
+		return NULL;
+	}
+}
diff --git a/plugins/eg04-sampler.lv2/sampler.ttl b/plugins/eg04-sampler.lv2/sampler.ttl
new file mode 100644
index 0000000..e008de0
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/sampler.ttl
@@ -0,0 +1,83 @@
+# LV2 Sampler Example Plugin
+# Copyright 2011-2012 David Robillard <d@drobilla.net>
+# Copyright 2011 Gabriel M. Beddingfield <gabriel@teuton.org>
+# Copyright 2011 James Morris <jwm.art.net@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+@prefix atom:  <http://lv2plug.in/ns/ext/atom#> .
+@prefix doap:  <http://usefulinc.com/ns/doap#> .
+@prefix lv2:   <http://lv2plug.in/ns/lv2core#> .
+@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix state: <http://lv2plug.in/ns/ext/state#> .
+@prefix ui:    <http://lv2plug.in/ns/extensions/ui#> .
+@prefix urid:  <http://lv2plug.in/ns/ext/urid#> .
+@prefix work:  <http://lv2plug.in/ns/ext/worker#> .
+
+<http://lv2plug.in/plugins/eg-sampler#sample>
+	a lv2:Parameter ;
+	rdfs:label "sample" ;
+	rdfs:range atom:Path .
+
+<http://lv2plug.in/plugins/eg-sampler>
+	a lv2:Plugin ;
+	doap:name "Example Sampler" ;
+	doap:license <http://opensource.org/licenses/isc> ;
+	lv2:project <http://lv2plug.in/ns/lv2> ;
+	lv2:requiredFeature urid:map ,
+		work:schedule ;
+	lv2:optionalFeature lv2:hardRTCapable ,
+		state:loadDefaultState ;
+	lv2:extensionData state:interface ,
+		work:interface ;
+	ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ;
+	patch:writable <http://lv2plug.in/plugins/eg-sampler#sample> ;
+	lv2:port [
+		a lv2:InputPort ,
+			atom:AtomPort ;
+		atom:bufferType atom:Sequence ;
+		atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ,
+			patch:Message ;
+		lv2:designation lv2:control ;
+		lv2:index 0 ;
+		lv2:symbol "control" ;
+		lv2:name "Control"
+	] , [
+		a lv2:OutputPort ,
+			atom:AtomPort ;
+		atom:bufferType atom:Sequence ;
+		atom:supports patch:Message ;
+		lv2:designation lv2:control ;
+		lv2:index 1 ;
+		lv2:symbol "notify" ;
+		lv2:name "Notify"
+	] , [
+		a lv2:AudioPort ,
+			lv2:OutputPort ;
+		lv2:index 2 ;
+		lv2:symbol "out" ;
+		lv2:name "Out"
+	] ;
+	state:state [
+		<http://lv2plug.in/plugins/eg-sampler#sample> <click.wav>
+	] .
+
+<http://lv2plug.in/plugins/eg-sampler#ui>
+	a ui:GtkUI ;
+	lv2:requiredFeature urid:map ;
+	ui:portNotification [
+		ui:plugin <http://lv2plug.in/plugins/eg-sampler> ;
+		lv2:symbol "notify" ;
+		ui:notifyType atom:Blank
+	] .
diff --git a/plugins/eg04-sampler.lv2/sampler_ui.c b/plugins/eg04-sampler.lv2/sampler_ui.c
new file mode 100644
index 0000000..40922ae
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/sampler_ui.c
@@ -0,0 +1,201 @@
+/*
+  LV2 Sampler Example Plugin UI
+  Copyright 2011-2012 David Robillard <d@drobilla.net>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+   @file ui.c Sampler Plugin UI
+*/
+
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/atom/util.h"
+#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#include "./uris.h"
+
+#define SAMPLER_UI_URI "http://lv2plug.in/plugins/eg-sampler#ui"
+
+typedef struct {
+	LV2_Atom_Forge forge;
+
+	LV2_URID_Map* map;
+	SamplerURIs   uris;
+
+	LV2UI_Write_Function write;
+	LV2UI_Controller     controller;
+
+	GtkWidget* box;
+	GtkWidget* button;
+	GtkWidget* label;
+} SamplerUI;
+
+static void
+on_load_clicked(GtkWidget* widget,
+                void*      handle)
+{
+	SamplerUI* ui = (SamplerUI*)handle;
+
+	/* Create a dialog to select a sample file. */
+	GtkWidget* dialog = gtk_file_chooser_dialog_new(
+		"Load Sample",
+		NULL,
+		GTK_FILE_CHOOSER_ACTION_OPEN,
+		GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+		GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+		NULL);
+
+	/* Run the dialog, and return if it is cancelled. */
+	if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
+		gtk_widget_destroy(dialog);
+		return;
+	}
+
+	/* Get the file path from the dialog. */
+	char* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+
+	/* Got what we need, destroy the dialog. */
+	gtk_widget_destroy(dialog);
+
+#define OBJ_BUF_SIZE 1024
+	uint8_t obj_buf[OBJ_BUF_SIZE];
+	lv2_atom_forge_set_buffer(&ui->forge, obj_buf, OBJ_BUF_SIZE);
+
+	LV2_Atom* msg = write_set_file(&ui->forge, &ui->uris,
+	                               filename, strlen(filename));
+
+	ui->write(ui->controller, 0, lv2_atom_total_size(msg),
+	          ui->uris.atom_eventTransfer,
+	          msg);
+
+	g_free(filename);
+}
+
+static LV2UI_Handle
+instantiate(const LV2UI_Descriptor*   descriptor,
+            const char*               plugin_uri,
+            const char*               bundle_path,
+            LV2UI_Write_Function      write_function,
+            LV2UI_Controller          controller,
+            LV2UI_Widget*             widget,
+            const LV2_Feature* const* features)
+{
+	SamplerUI* ui = (SamplerUI*)malloc(sizeof(SamplerUI));
+	ui->map        = NULL;
+	ui->write      = write_function;
+	ui->controller = controller;
+	ui->box        = NULL;
+	ui->button     = NULL;
+	ui->label      = NULL;
+
+	*widget = NULL;
+
+	for (int i = 0; features[i]; ++i) {
+		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
+			ui->map = (LV2_URID_Map*)features[i]->data;
+		}
+	}
+
+	if (!ui->map) {
+		fprintf(stderr, "sampler_ui: Host does not support urid:Map\n");
+		free(ui);
+		return NULL;
+	}
+
+	map_sampler_uris(ui->map, &ui->uris);
+
+	lv2_atom_forge_init(&ui->forge, ui->map);
+
+	ui->box = gtk_vbox_new(FALSE, 4);
+	ui->label = gtk_label_new("?");
+	ui->button = gtk_button_new_with_label("Load Sample");
+	gtk_box_pack_start(GTK_BOX(ui->box), ui->label, TRUE, TRUE, 4);
+	gtk_box_pack_start(GTK_BOX(ui->box), ui->button, FALSE, FALSE, 4);
+	g_signal_connect(ui->button, "clicked",
+	                 G_CALLBACK(on_load_clicked),
+	                 ui);
+
+	*widget = ui->box;
+
+	return ui;
+}
+
+static void
+cleanup(LV2UI_Handle handle)
+{
+	SamplerUI* ui = (SamplerUI*)handle;
+	gtk_widget_destroy(ui->button);
+	free(ui);
+}
+
+static void
+port_event(LV2UI_Handle handle,
+           uint32_t     port_index,
+           uint32_t     buffer_size,
+           uint32_t     format,
+           const void*  buffer)
+{
+	SamplerUI* ui = (SamplerUI*)handle;
+	if (format == ui->uris.atom_eventTransfer) {
+		LV2_Atom* atom = (LV2_Atom*)buffer;
+		if (atom->type == ui->uris.atom_Blank) {
+			LV2_Atom_Object* obj      = (LV2_Atom_Object*)atom;
+			const LV2_Atom*  file_uri = read_set_file(&ui->uris, obj);
+			if (!file_uri) {
+				fprintf(stderr, "Unknown message sent to UI.\n");
+				return;
+			}
+
+			const char* uri = (const char*)LV2_ATOM_BODY(file_uri);
+			gtk_label_set_text(GTK_LABEL(ui->label), uri);
+		} else {
+			fprintf(stderr, "Unknown message type.\n");
+		}
+	} else {
+		fprintf(stderr, "Unknown format.\n");
+	}
+}
+
+static const void*
+extension_data(const char* uri)
+{
+	return NULL;
+}
+
+static const LV2UI_Descriptor descriptor = {
+	SAMPLER_UI_URI,
+	instantiate,
+	cleanup,
+	port_event,
+	extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2UI_Descriptor*
+lv2ui_descriptor(uint32_t index)
+{
+	switch (index) {
+	case 0:
+		return &descriptor;
+	default:
+		return NULL;
+	}
+}
diff --git a/plugins/eg04-sampler.lv2/uris.h b/plugins/eg04-sampler.lv2/uris.h
new file mode 100644
index 0000000..e2ec6d0
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/uris.h
@@ -0,0 +1,142 @@
+/*
+  LV2 Sampler Example Plugin
+  Copyright 2011-2012 David Robillard <d@drobilla.net>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef SAMPLER_URIS_H
+#define SAMPLER_URIS_H
+
+#include "lv2/lv2plug.in/ns/ext/log/log.h"
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+
+#define EG_SAMPLER_URI          "http://lv2plug.in/plugins/eg-sampler"
+#define EG_SAMPLER__sample      EG_SAMPLER_URI "#sample"
+#define EG_SAMPLER__applySample EG_SAMPLER_URI "#applySample"
+#define EG_SAMPLER__freeSample  EG_SAMPLER_URI "#freeSample"
+
+typedef struct {
+	LV2_URID atom_Blank;
+	LV2_URID atom_Path;
+	LV2_URID atom_Resource;
+	LV2_URID atom_Sequence;
+	LV2_URID atom_URID;
+	LV2_URID atom_eventTransfer;
+	LV2_URID eg_applySample;
+	LV2_URID eg_sample;
+	LV2_URID eg_freeSample;
+	LV2_URID midi_Event;
+	LV2_URID patch_Set;
+	LV2_URID patch_property;
+	LV2_URID patch_value;
+} SamplerURIs;
+
+static inline void
+map_sampler_uris(LV2_URID_Map* map, SamplerURIs* uris)
+{
+	uris->atom_Blank         = map->map(map->handle, LV2_ATOM__Blank);
+	uris->atom_Path          = map->map(map->handle, LV2_ATOM__Path);
+	uris->atom_Resource      = map->map(map->handle, LV2_ATOM__Resource);
+	uris->atom_Sequence      = map->map(map->handle, LV2_ATOM__Sequence);
+	uris->atom_URID          = map->map(map->handle, LV2_ATOM__URID);
+	uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer);
+	uris->eg_applySample     = map->map(map->handle, EG_SAMPLER__applySample);
+	uris->eg_freeSample      = map->map(map->handle, EG_SAMPLER__freeSample);
+	uris->eg_sample          = map->map(map->handle, EG_SAMPLER__sample);
+	uris->midi_Event         = map->map(map->handle, LV2_MIDI__MidiEvent);
+	uris->patch_Set          = map->map(map->handle, LV2_PATCH__Set);
+	uris->patch_property     = map->map(map->handle, LV2_PATCH__property);
+	uris->patch_value        = map->map(map->handle, LV2_PATCH__value);
+}
+
+static inline bool
+is_object_type(const SamplerURIs* uris, LV2_URID type)
+{
+	return type == uris->atom_Resource
+		|| type == uris->atom_Blank;
+}
+
+/**
+ * Write a message like the following to @p forge:
+ * []
+ *     a patch:Set ;
+ *     patch:property eg:sample ;
+ *     patch:value </home/me/foo.wav> .
+ */
+static inline LV2_Atom*
+write_set_file(LV2_Atom_Forge*    forge,
+               const SamplerURIs* uris,
+               const char*        filename,
+               const size_t       filename_len)
+{
+	LV2_Atom_Forge_Frame frame;
+	LV2_Atom* set = (LV2_Atom*)lv2_atom_forge_blank(
+		forge, &frame, 1, uris->patch_Set);
+
+	lv2_atom_forge_property_head(forge, uris->patch_property, 0);
+	lv2_atom_forge_urid(forge, uris->eg_sample);
+	lv2_atom_forge_property_head(forge, uris->patch_value, 0);
+	lv2_atom_forge_path(forge, filename, filename_len);
+
+	lv2_atom_forge_pop(forge, &frame);
+
+	return set;
+}
+
+/**
+ * Get the file path from a message like:
+ * []
+ *     a patch:Set ;
+ *     patch:property eg:sample ;
+ *     patch:value </home/me/foo.wav> .
+ */
+static inline const LV2_Atom*
+read_set_file(const SamplerURIs*     uris,
+              const LV2_Atom_Object* obj)
+{
+	if (obj->body.otype != uris->patch_Set) {
+		fprintf(stderr, "Ignoring unknown message type %d\n", obj->body.otype);
+		return NULL;
+	}
+
+	/* Get property URI. */
+	const LV2_Atom* property = NULL;
+	lv2_atom_object_get(obj, uris->patch_property, &property, 0);
+	if (!property) {
+		fprintf(stderr, "Malformed set message has no body.\n");
+		return NULL;
+	} else if (property->type != uris->atom_URID) {
+		fprintf(stderr, "Malformed set message has non-URID property.\n");
+		return NULL;
+	} else if (((LV2_Atom_URID*)property)->body != uris->eg_sample) {
+		fprintf(stderr, "Set message for unknown property.\n");
+		return NULL;
+	}
+
+	/* Get value. */
+	const LV2_Atom* file_path = NULL;
+	lv2_atom_object_get(obj, uris->patch_value, &file_path, 0);
+	if (!file_path) {
+		fprintf(stderr, "Malformed set message has no value.\n");
+		return NULL;
+	} else if (file_path->type != uris->atom_Path) {
+		fprintf(stderr, "Set message value is not a Path.\n");
+		return NULL;
+	}
+
+	return file_path;
+}
+
+#endif  /* SAMPLER_URIS_H */
diff --git a/plugins/eg04-sampler.lv2/waf b/plugins/eg04-sampler.lv2/waf
new file mode 120000
index 0000000..59a1ac9
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/waf
@@ -0,0 +1 @@
+../../waf
\ No newline at end of file
diff --git a/plugins/eg04-sampler.lv2/wscript b/plugins/eg04-sampler.lv2/wscript
new file mode 100644
index 0000000..732c904
--- /dev/null
+++ b/plugins/eg04-sampler.lv2/wscript
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import re
+
+# Variables for 'waf dist'
+APPNAME = 'eg-sampler.lv2'
+VERSION = '1.0.0'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('compiler_c')
+    autowaf.set_options(opt)
+
+def configure(conf):
+    conf.load('compiler_c')
+    autowaf.configure(conf)
+    autowaf.set_c99_mode(conf)
+    autowaf.display_header('Sampler Configuration')
+
+    if not autowaf.is_child():
+        autowaf.check_pkg(conf, 'lv2', atleast_version='1.2.1', uselib_store='LV2')
+
+    autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE',
+                      atleast_version='1.0.0', mandatory=True)
+    autowaf.check_pkg(conf, 'gtk+-2.0', uselib_store='GTK2',
+                      atleast_version='2.18.0', mandatory=False)
+
+    autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR)
+    print('')
+
+def build(bld):
+    bundle = 'eg-sampler.lv2'
+
+    # Make a pattern for shared objects without the 'lib' prefix
+    module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN)
+    module_ext = module_pat[module_pat.rfind('.'):]
+
+    # Build manifest.ttl by substitution (for portable lib extension)
+    bld(features     = 'subst',
+        source       = 'manifest.ttl.in',
+        target       = '%s/%s' % (bundle, 'manifest.ttl'),
+        install_path = '${LV2DIR}/%s' % bundle,
+        LIB_EXT      = module_ext)
+    
+    # Copy other data files to build bundle (build/eg-sampler.lv2)
+    for i in ['sampler.ttl', 'click.wav']:
+        bld(features     = 'subst',
+            is_copy      = True,
+            source       = i,
+            target       = '%s/%s' % (bundle, i),
+            install_path = '${LV2DIR}/%s' % bundle)
+
+    # Use LV2 headers from parent directory if building as a sub-project
+    includes = ['.']
+    if autowaf.is_child:
+        includes += ['../..']
+
+    # Build plugin library
+    obj = bld(features     = 'c cshlib',
+              source       = 'sampler.c',
+              name         = 'sampler',
+              target       = '%s/sampler' % bundle,
+              install_path = '${LV2DIR}/%s' % bundle,
+              use          = 'SNDFILE LV2',
+              includes     = includes)
+    obj.env.cshlib_PATTERN = module_pat
+
+    # Build UI library
+    if bld.is_defined('HAVE_GTK2'):
+        obj = bld(features     = 'c cshlib',
+                  source       = 'sampler_ui.c',
+                  name         = 'sampler_ui',
+                  target       = '%s/sampler_ui' % bundle,
+                  install_path = '${LV2DIR}/%s' % bundle,
+                  use          = 'GTK2 LV2',
+                  includes     = includes)
+    obj.env.cshlib_PATTERN = module_pat
-- 
cgit v1.2.1