refreshed LV2 sources
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Sat, 10 Feb 2018 21:07:57 +0000 (22:07 +0100)
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Sat, 10 Feb 2018 21:22:25 +0000 (22:22 +0100)
debian/extra/includes/lv2_external_ui.h [deleted file]
debian/extra/includes/lv2_programs.h [deleted file]
debian/extra/juce_LV2_Wrapper.cpp [deleted file]
debian/extra/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h [new file with mode: 0644]
debian/extra/juce_audio_plugin_client/LV2/includes/lv2_programs.h [new file with mode: 0644]
debian/extra/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp [new file with mode: 0644]
debian/extra/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp [new file with mode: 0644]
debian/patches/LV2-audioprocessor.patch [new file with mode: 0644]
debian/patches/series

diff --git a/debian/extra/includes/lv2_external_ui.h b/debian/extra/includes/lv2_external_ui.h
deleted file mode 100644 (file)
index 789cd3f..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-  LV2 External UI extension
-  This work is in public domain.
-
-  This file is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-  If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com>
-  or ask in #lad channel, FreeNode IRC network.
-*/
-
-/**
-   @file lv2_external_ui.h
-   C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>.
-*/
-
-#ifndef LV2_EXTERNAL_UI_H
-#define LV2_EXTERNAL_UI_H
-
-#include "ui.h"
-
-#define LV2_EXTERNAL_UI_URI     "http://kxstudio.sf.net/ns/lv2ext/external-ui"
-#define LV2_EXTERNAL_UI_PREFIX  LV2_EXTERNAL_UI_URI "#"
-
-#define LV2_EXTERNAL_UI__Host   LV2_EXTERNAL_UI_PREFIX "Host"
-#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget"
-
-/** This extension used to be defined by a lv2plug.in URI */
-#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned
- * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget.
- * UI is created in invisible state.
- */
-typedef struct _LV2_External_UI_Widget {
-  /**
-   * Host calls this function regulary. UI library implementing the
-   * callback may do IPC or redraw the UI.
-   *
-   * @param _this_ the UI context
-   */
-  void (*run)(struct _LV2_External_UI_Widget * _this_);
-
-  /**
-   * Host calls this function to make the plugin UI visible.
-   *
-   * @param _this_ the UI context
-   */
-  void (*show)(struct _LV2_External_UI_Widget * _this_);
-
-  /**
-   * Host calls this function to make the plugin UI invisible again.
-   *
-   * @param _this_ the UI context
-   */
-  void (*hide)(struct _LV2_External_UI_Widget * _this_);
-
-} LV2_External_UI_Widget;
-
-#define LV2_EXTERNAL_UI_RUN(ptr)  (ptr)->run(ptr)
-#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
-#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
-
-/**
- * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature.
- * LV2_Feature::data must be pointer to LV2_External_UI_Host.
- */
-typedef struct _LV2_External_UI_Host {
-  /**
-   * Callback that plugin UI will call when UI (GUI window) is closed by user.
-   * This callback will be called during execution of LV2_External_UI_Widget::run()
-   * (i.e. not from background thread).
-   *
-   * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup().
-   * If host wants to make the UI visible again, the UI must be reinstantiated.
-   *
-   * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI,
-   *       some hosts will not call LV2UI_Descriptor::cleanup() as they should,
-   *       and may call show() again without re-initialization.
-   *
-   * @param controller Host context associated with plugin UI, as
-   *                   supplied to LV2UI_Descriptor::instantiate().
-   */
-  void (*ui_closed)(LV2UI_Controller controller);
-
-  /**
-   * Optional (may be NULL) "user friendly" identifier which the UI
-   * may display to allow a user to easily associate this particular
-   * UI instance with the correct plugin instance as it is represented
-   * by the host (e.g. "track 1" or "channel 4").
-   *
-   * If supplied by host, the string will be referenced only during
-   * LV2UI_Descriptor::instantiate()
-   */
-  const char * plugin_human_id;
-
-} LV2_External_UI_Host;
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* LV2_EXTERNAL_UI_H */
diff --git a/debian/extra/includes/lv2_programs.h b/debian/extra/includes/lv2_programs.h
deleted file mode 100644 (file)
index 01e2877..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
-  LV2 Programs Extension
-  Copyright 2012 Filipe Coelho <falktx@falktx.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 lv2_programs.h
-   C header for the LV2 programs extension <http://kxstudio.sf.net/ns/lv2ext/programs>.
-*/
-
-#ifndef LV2_PROGRAMS_H
-#define LV2_PROGRAMS_H
-
-#include "lv2.h"
-#include "ui.h"
-
-#define LV2_PROGRAMS_URI    "http://kxstudio.sf.net/ns/lv2ext/programs"
-#define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#"
-
-#define LV2_PROGRAMS__Host        LV2_PROGRAMS_PREFIX "Host"
-#define LV2_PROGRAMS__Interface   LV2_PROGRAMS_PREFIX "Interface"
-#define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef void* LV2_Programs_Handle;
-
-typedef struct _LV2_Program_Descriptor {
-
-    /** Bank number for this program. Note that this extension does not
-        support MIDI-style separation of bank LSB and MSB values. There is
-        no restriction on the set of available banks: the numbers do not
-        need to be contiguous, there does not need to be a bank 0, etc. */
-    uint32_t bank;
-
-    /** Program number (unique within its bank) for this program. There is
-        no restriction on the set of available programs: the numbers do not
-        need to be contiguous, there does not need to be a program 0, etc. */
-    uint32_t program;
-
-    /** Name of the program. */
-    const char * name;
-
-} LV2_Program_Descriptor;
-
-/**
-   Programs extension, plugin data.
-
-   When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface,
-   the plugin MUST return an LV2_Programs_Instance structure, which remains valid
-   for the lifetime of the plugin.
-*/
-typedef struct _LV2_Programs_Interface {
-    /**
-     * get_program()
-     *
-     * This member is a function pointer that provides a description
-     * of a program (named preset sound) available on this plugin.
-     *
-     * The index argument is an index into the plugin's list of
-     * programs, not a program number as represented by the Program
-     * field of the LV2_Program_Descriptor. (This distinction is
-     * needed to support plugins that use non-contiguous program or
-     * bank numbers.)
-     *
-     * This function returns a LV2_Program_Descriptor pointer that is
-     * guaranteed to be valid only until the next call to get_program
-     * or deactivate, on the same plugin instance. This function must
-     * return NULL if passed an index argument out of range, so that
-     * the host can use it to query the number of programs as well as
-     * their properties.
-     */
-    const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle,
-                                                 uint32_t index);
-
-    /**
-     * select_program()
-     *
-     * This member is a function pointer that selects a new program
-     * for this plugin. The program change should take effect
-     * immediately at the start of the next run() call. (This
-     * means that a host providing the capability of changing programs
-     * between any two notes on a track must vary the block size so as
-     * to place the program change at the right place. A host that
-     * wanted to avoid this would probably just instantiate a plugin
-     * for each program.)
-     *
-     * Plugins should ignore a select_program() call with an invalid
-     * bank or program.
-     *
-     * A plugin is not required to select any particular default
-     * program on activate(): it's the host's duty to set a program
-     * explicitly.
-     *
-     * A plugin is permitted to re-write the values of its input
-     * control ports when select_program is called. The host should
-     * re-read the input control port values and update its own
-     * records appropriately. (This is the only circumstance in which
-     * a LV2 plugin is allowed to modify its own control-input ports.)
-     */
-    void (*select_program)(LV2_Handle handle,
-                           uint32_t bank,
-                           uint32_t program);
-
-} LV2_Programs_Interface;
-
-/**
-   Programs extension, UI data.
-
-   When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface,
-   the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid
-   for the lifetime of the UI.
-*/
-typedef struct _LV2_Programs_UI_Interface {
-    /**
-     * select_program()
-     *
-     * This is exactly the same as select_program in LV2_Programs_Instance,
-     * but this struct relates to the UI instead of the plugin.
-     *
-     * When called, UIs should update their state to match the selected program.
-     */
-    void (*select_program)(LV2UI_Handle handle,
-                           uint32_t bank,
-                           uint32_t program);
-
-} LV2_Programs_UI_Interface;
-
-/**
-    Feature data for LV2_PROGRAMS__Host.
-*/
-typedef struct _LV2_Programs_Host {
-    /**
-     *  Opaque host data.
-     */
-    LV2_Programs_Handle handle;
-
-    /**
-     * program_changed()
-     *
-     * Tell the host to reload a plugin's program.
-     * Parameter handle MUST be the 'handle' member of this struct.
-     * Parameter index is program index to change.
-     * When index is -1, host should reload all the programs.
-     *
-     * The plugin MUST NEVER call this function on a RT context or during run().
-     *
-     * NOTE: This call is to inform the host about a program's bank, program or name change.
-     *       It DOES NOT change the current selected program.
-     */
-    void (*program_changed)(LV2_Programs_Handle handle,
-                            int32_t index);
-
-} LV2_Programs_Host;
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* LV2_PROGRAMS_H */
diff --git a/debian/extra/juce_LV2_Wrapper.cpp b/debian/extra/juce_LV2_Wrapper.cpp
deleted file mode 100644 (file)
index f8441ad..0000000
+++ /dev/null
@@ -1,2114 +0,0 @@
-/*
-  ==============================================================================
-
-   Juce LV2 Wrapper
-
-  ==============================================================================
-*/
-
-// Your project must contain an AppConfig.h file with your project-specific settings in it,
-// and your header search path must make it accessible to the module's files.
-#include "AppConfig.h"
-
-#include "juce_audio_plugin_client/utility/juce_CheckSettingMacros.h"
-#include "juce_core/system/juce_TargetPlatform.h" // for JUCE_LINUX
-
-#if JucePlugin_Build_LV2
-
-/** Plugin requires processing with a fixed/constant block size */
-#ifndef JucePlugin_WantsLV2FixedBlockSize
- #define JucePlugin_WantsLV2FixedBlockSize 0
-#endif
-
-/** Use non-parameter states */
-#ifndef JucePlugin_WantsLV2State
- #define JucePlugin_WantsLV2State 1
-#endif
-
-/** States are strings, needs custom get/setStateInformationString */
-#ifndef JucePlugin_WantsLV2StateString
- #define JucePlugin_WantsLV2StateString 0
-#endif
-
-/** Export presets */
-#ifndef JucePlugin_WantsLV2Presets
- #define JucePlugin_WantsLV2Presets 1
-#endif
-
-/** Request time position */
-#ifndef JucePlugin_WantsLV2TimePos
- #define JucePlugin_WantsLV2TimePos 1
-#endif
-
-/** Using string states require enabling states first */
-#if JucePlugin_WantsLV2StateString && ! JucePlugin_WantsLV2State
- #undef JucePlugin_WantsLV2State
- #define JucePlugin_WantsLV2State 1
-#endif
-
-#if JUCE_LINUX
- #include <X11/Xlib.h>
- #undef KeyPress
-#endif
-
-#include <fstream>
-#include <iostream>
-
-// LV2 includes..
-#include <lv2/lv2plug.in/ns/lv2core/lv2.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/buf-size/buf-size.h>
-#include <lv2/lv2plug.in/ns/ext/instance-access/instance-access.h>
-#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
-#include <lv2/lv2plug.in/ns/ext/options/options.h>
-#include <lv2/lv2plug.in/ns/ext/port-props/port-props.h>
-#include <lv2/lv2plug.in/ns/ext/presets/presets.h>
-#include <lv2/lv2plug.in/ns/ext/state/state.h>
-#include <lv2/lv2plug.in/ns/ext/time/time.h>
-#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
-#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
-#include "includes/lv2_external_ui.h"
-#include "includes/lv2_programs.h"
-
-#include "juce_audio_plugin_client/utility/juce_IncludeModuleHeaders.h"
-
-namespace juce
-{
- #if JUCE_LINUX
-  extern Display* display;
- #endif
-}
-
-#define JUCE_LV2_STATE_STRING_URI "urn:juce:stateString"
-#define JUCE_LV2_STATE_BINARY_URI "urn:juce:stateBinary"
-
-#if JucePlugin_WantsLV2State && ! JucePlugin_WantsLV2StateString
- // FIXME - juce base64 algorithm does not conform to RFC used in LV2
- #include "base64/Base64.cpp"
-#endif
-
-//==============================================================================
-// Various helper functions for creating the ttl files
-
-#if JUCE_MAC
- #define PLUGIN_EXT ".dylib"
-#elif JUCE_LINUX
- #define PLUGIN_EXT ".so"
-#elif JUCE_WINDOWS
- #define PLUGIN_EXT ".dll"
-#endif
-
-/** Returns plugin type, defined in AppConfig.h or JucePluginCharacteristics.h */
-const String getPluginType()
-{
-    String pluginType;
-#ifdef JucePlugin_LV2Category
-    pluginType  = "lv2:" JucePlugin_LV2Category;
-    pluginType += ", ";
-#elif JucePlugin_IsSynth
-    pluginType  = "lv2:InstrumentPlugin, ";
-#endif
-    pluginType += "lv2:Plugin";
-    return pluginType;
-}
-
-/** Returns plugin URI */
-static const String& getPluginURI()
-{
-    // JucePlugin_LV2URI might be defined as a function (eg. allowing dynamic URIs based on filename)
-    static const String pluginURI(JucePlugin_LV2URI);
-    return pluginURI;
-}
-
-static Array<String> usedSymbols;
-
-/** Converts a parameter name to an LV2 compatible symbol. */
-const String nameToSymbol (const String& name, const uint32 portIndex)
-{
-    String symbol, trimmedName = name.trimStart().trimEnd().toLowerCase();
-
-    if (trimmedName.isEmpty())
-    {
-        symbol += "lv2_port_";
-        symbol += String(portIndex+1);
-    }
-    else
-    {
-        for (int i=0; i < trimmedName.length(); ++i)
-        {
-            const juce_wchar c = trimmedName[i];
-            if (i == 0 && std::isdigit(c))
-                symbol += "_";
-            else if (std::isalpha(c) || std::isdigit(c))
-                symbol += c;
-            else
-                symbol += "_";
-        }
-    }
-
-    // Do not allow identical symbols
-    if (usedSymbols.contains(symbol))
-    {
-        int offset = 2;
-        String offsetStr = "_2";
-        symbol += offsetStr;
-
-        while (usedSymbols.contains(symbol))
-        {
-            offset += 1;
-            String newOffsetStr = "_" + String(offset);
-            symbol = symbol.replace(offsetStr, newOffsetStr);
-            offsetStr = newOffsetStr;
-        }
-    }
-    usedSymbols.add(symbol);
-
-    return symbol;
-}
-
-/** Prevents NaN or out of 0.0<->1.0 bounds parameter values. */
-float safeParamValue (float value)
-{
-    if (std::isnan(value))
-        value = 0.0f;
-    else if (value < 0.0f)
-        value = 0.0f;
-    else if (value > 1.0f)
-        value = 1.0f;
-    return value;
-}
-
-/** Create the manifest.ttl file contents */
-const String makeManifestFile (AudioProcessor* const filter, const String& binary)
-{
-    const String& pluginURI(getPluginURI());
-    String text;
-
-    // Header
-    text += "@prefix lv2:  <" LV2_CORE_PREFIX "> .\n";
-    text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
-    text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
-    text += "@prefix ui:   <" LV2_UI_PREFIX "> .\n";
-    text += "\n";
-
-    // Plugin
-    text += "<" + pluginURI + ">\n";
-    text += "    a lv2:Plugin ;\n";
-    text += "    lv2:binary <" + binary + PLUGIN_EXT "> ;\n";
-    text += "    rdfs:seeAlso <" + binary + ".ttl> .\n";
-    text += "\n";
-
-    // UIs
-    if (filter->hasEditor())
-    {
-        text += "<" + pluginURI + "#ExternalUI>\n";
-        text += "    a <" LV2_EXTERNAL_UI__Widget "> ;\n";
-        text += "    ui:binary <" + binary + PLUGIN_EXT "> ;\n";
-        text += "    lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
-        text += "    lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
-        text += "\n";
-
-        text += "<" + pluginURI + "#ParentUI>\n";
-#if JUCE_MAC
-        text += "    a ui:CocoaUI ;\n";
-#elif JUCE_LINUX
-        text += "    a ui:X11UI ;\n";
-#elif JUCE_WINDOWS
-        text += "    a ui:WindowsUI ;\n";
-#endif
-        text += "    ui:binary <" + binary + PLUGIN_EXT "> ;\n";
-        text += "    lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
-        text += "    lv2:optionalFeature ui:noUserResize ;\n";
-        text += "    lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
-        text += "\n";
-    }
-
-#if JucePlugin_WantsLV2Presets
-    const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
-
-    // Presets
-    for (int i = 0; i < filter->getNumPrograms(); ++i)
-    {
-        text += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + ">\n";
-        text += "    a pset:Preset ;\n";
-        text += "    lv2:appliesTo <" + pluginURI + "> ;\n";
-        text += "    rdfs:label \"" + filter->getProgramName(i) + "\" ;\n";
-        text += "    rdfs:seeAlso <presets.ttl> .\n";
-        text += "\n";
-    }
-#endif
-
-    return text;
-}
-
-/** Create the -plugin-.ttl file contents */
-const String makePluginFile (AudioProcessor* const filter)
-{
-    const String& pluginURI(getPluginURI());
-    String text;
-
-    // Header
-    text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
-    text += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
-    text += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
-    text += "@prefix lv2:  <" LV2_CORE_PREFIX "> .\n";
-    text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
-    text += "@prefix ui:   <" LV2_UI_PREFIX "> .\n";
-    text += "\n";
-
-    // Plugin
-    text += "<" + pluginURI + ">\n";
-    text += "    a " + getPluginType() + " ;\n";
-    text += "    lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n";
-#if JucePlugin_WantsLV2FixedBlockSize
-    text += "                        <" LV2_BUF_SIZE__fixedBlockLength "> ,\n";
-#endif
-    text += "                        <" LV2_URID__map "> ;\n";
-    text += "    lv2:extensionData <" LV2_OPTIONS__interface "> ,\n";
-#if JucePlugin_WantsLV2State
-    text += "                      <" LV2_STATE__interface "> ,\n";
-#endif
-    text += "                      <" LV2_PROGRAMS__Interface "> ;\n";
-    text += "\n";
-
-    // UIs
-    if (filter->hasEditor())
-    {
-        text += "    ui:ui <" + pluginURI + "#ExternalUI> ,\n";
-        text += "          <" + pluginURI + "#ParentUI> ;\n";
-        text += "\n";
-    }
-
-    uint32 portIndex = 0;
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-    // MIDI input
-    text += "    lv2:port [\n";
-    text += "        a lv2:InputPort, atom:AtomPort ;\n";
-    text += "        atom:bufferType atom:Sequence ;\n";
- #if JucePlugin_WantsMidiInput
-    text += "        atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
- #endif
- #if JucePlugin_WantsLV2TimePos
-    text += "        atom:supports <" LV2_TIME__Position "> ;\n";
- #endif
-    text += "        lv2:index " + String(portIndex++) + " ;\n";
-    text += "        lv2:symbol \"lv2_events_in\" ;\n";
-    text += "        lv2:name \"Events Input\" ;\n";
-    text += "        lv2:designation lv2:control ;\n";
- #if ! JucePlugin_IsSynth
-    text += "        lv2:portProperty lv2:connectionOptional ;\n";
- #endif
-    text += "    ] ;\n";
-    text += "\n";
-#endif
-
-#if JucePlugin_ProducesMidiOutput
-    // MIDI output
-    text += "    lv2:port [\n";
-    text += "        a lv2:OutputPort, atom:AtomPort ;\n";
-    text += "        atom:bufferType atom:Sequence ;\n";
-    text += "        atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
-    text += "        lv2:index " + String(portIndex++) + " ;\n";
-    text += "        lv2:symbol \"lv2_midi_out\" ;\n";
-    text += "        lv2:name \"MIDI Output\" ;\n";
-    text += "    ] ;\n";
-    text += "\n";
-#endif
-
-    // Freewheel and latency ports
-    text += "    lv2:port [\n";
-    text += "        a lv2:InputPort, lv2:ControlPort ;\n";
-    text += "        lv2:index " + String(portIndex++) + " ;\n";
-    text += "        lv2:symbol \"lv2_freewheel\" ;\n";
-    text += "        lv2:name \"Freewheel\" ;\n";
-    text += "        lv2:default 0.0 ;\n";
-    text += "        lv2:minimum 0.0 ;\n";
-    text += "        lv2:maximum 1.0 ;\n";
-    text += "        lv2:designation <" LV2_CORE__freeWheeling "> ;\n";
-    text += "        lv2:portProperty lv2:toggled, <" LV2_PORT_PROPS__notOnGUI "> ;\n";
-    text += "    ] ,\n";
-    text += "    [\n";
-    text += "        a lv2:OutputPort, lv2:ControlPort ;\n";
-    text += "        lv2:index " + String(portIndex++) + " ;\n";
-    text += "        lv2:symbol \"lv2_latency\" ;\n";
-    text += "        lv2:name \"Latency\" ;\n";
-    text += "        lv2:designation <" LV2_CORE__latency "> ;\n";
-    text += "        lv2:portProperty lv2:reportsLatency, lv2:integer ;\n";
-    text += "    ] ;\n";
-    text += "\n";
-
-    // Audio inputs
-    for (int i=0; i < JucePlugin_MaxNumInputChannels; ++i)
-    {
-        if (i == 0)
-            text += "    lv2:port [\n";
-        else
-            text += "    [\n";
-
-        text += "        a lv2:InputPort, lv2:AudioPort ;\n";
-        text += "        lv2:index " + String(portIndex++) + " ;\n";
-        text += "        lv2:symbol \"lv2_audio_in_" + String(i+1) + "\" ;\n";
-        text += "        lv2:name \"Audio Input " + String(i+1) + "\" ;\n";
-
-        if (i+1 == JucePlugin_MaxNumInputChannels)
-            text += "    ] ;\n\n";
-        else
-            text += "    ] ,\n";
-    }
-
-    // Audio outputs
-    for (int i=0; i < JucePlugin_MaxNumOutputChannels; ++i)
-    {
-        if (i == 0)
-            text += "    lv2:port [\n";
-        else
-            text += "    [\n";
-
-        text += "        a lv2:OutputPort, lv2:AudioPort ;\n";
-        text += "        lv2:index " + String(portIndex++) + " ;\n";
-        text += "        lv2:symbol \"lv2_audio_out_" + String(i+1) + "\" ;\n";
-        text += "        lv2:name \"Audio Output " + String(i+1) + "\" ;\n";
-
-        if (i+1 == JucePlugin_MaxNumOutputChannels)
-            text += "    ] ;\n\n";
-        else
-            text += "    ] ,\n";
-    }
-
-    // Parameters
-    for (int i=0; i < filter->getNumParameters(); ++i)
-    {
-        if (i == 0)
-            text += "    lv2:port [\n";
-        else
-            text += "    [\n";
-
-        text += "        a lv2:InputPort, lv2:ControlPort ;\n";
-        text += "        lv2:index " + String(portIndex++) + " ;\n";
-        text += "        lv2:symbol \"" + nameToSymbol(filter->getParameterName(i), i) + "\" ;\n";
-
-        if (filter->getParameterName(i).isNotEmpty())
-            text += "        lv2:name \"" + filter->getParameterName(i) + "\" ;\n";
-        else
-            text += "        lv2:name \"Port " + String(i+1) + "\" ;\n";
-
-        text += "        lv2:default " + String::formatted("%f", safeParamValue(filter->getParameter(i))) + " ;\n";
-        text += "        lv2:minimum 0.0 ;\n";
-        text += "        lv2:maximum 1.0 ;\n";
-
-        if (! filter->isParameterAutomatable(i))
-            text += "        lv2:portProperty <" LV2_PORT_PROPS__expensive "> ;\n";
-
-        if (i+1 == filter->getNumParameters())
-            text += "    ] ;\n\n";
-        else
-            text += "    ] ,\n";
-    }
-
-    text += "    doap:name \"" + filter->getName() + "\" ;\n";
-    text += "    doap:maintainer [ foaf:name \"" JucePlugin_Manufacturer "\" ] .\n";
-
-    return text;
-}
-
-/** Create the presets.ttl file contents */
-const String makePresetsFile (AudioProcessor* const filter)
-{
-    const String& pluginURI(getPluginURI());
-    String text;
-
-    // Header
-    text += "@prefix atom:  <" LV2_ATOM_PREFIX "> .\n";
-    text += "@prefix lv2:   <" LV2_CORE_PREFIX "> .\n";
-    text += "@prefix pset:  <" LV2_PRESETS_PREFIX "> .\n";
-    text += "@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
-    text += "@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .\n";
-    text += "@prefix state: <" LV2_STATE_PREFIX "> .\n";
-    text += "@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .\n";
-    text += "\n";
-
-    // Presets
-    const int numPrograms = filter->getNumPrograms();
-    const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
-
-    for (int i = 0; i < numPrograms; ++i)
-    {
-        std::cout << "\nSaving preset " << i+1 << "/" << numPrograms+1 << "...";
-        std::cout.flush();
-
-        String preset;
-
-        // Label
-        filter->setCurrentProgram(i);
-        preset += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + "> a pset:Preset ;\n";
-
-        // State
-#if JucePlugin_WantsLV2State
-        preset += "    state:state [\n";
- #if JucePlugin_WantsLV2StateString
-        preset += "        <" JUCE_LV2_STATE_STRING_URI ">\n";
-        preset += "\"\"\"\n";
-        preset += filter->getStateInformationString().replace("\r\n","\n");
-        preset += "\"\"\"\n";
- #else
-        MemoryBlock chunkMemory;
-        filter->getCurrentProgramStateInformation(chunkMemory);
-        const String chunkString(Base64Encode(chunkMemory));
-
-        preset += "        <" JUCE_LV2_STATE_BINARY_URI "> [\n";
-        preset += "            a atom:Chunk ;\n";
-        preset += "            rdf:value \"" + chunkString + "\"^^xsd:base64Binary ;\n";
-        preset += "        ] ;\n";
- #endif
-        if (filter->getNumParameters() == 0)
-        {
-            preset += "    ] .\n\n";
-            continue;
-        }
-
-        preset += "    ] ;\n\n";
-#endif
-
-        // Port values
-        usedSymbols.clear();
-
-        for (int j=0; j < filter->getNumParameters(); ++j)
-        {
-              if (j == 0)
-                preset += "    lv2:port [\n";
-            else
-                preset += "    [\n";
-
-            preset += "        lv2:symbol \"" + nameToSymbol(filter->getParameterName(j), j) + "\" ;\n";
-            preset += "        pset:value " + String::formatted("%f", safeParamValue(filter->getParameter(j))) + " ;\n";
-
-            if (j+1 == filter->getNumParameters())
-                preset += "    ] ";
-            else
-                preset += "    ] ,\n";
-        }
-        preset += ".\n\n";
-
-        text += preset;
-    }
-
-    return text;
-}
-
-/** Creates manifest.ttl, plugin.ttl and presets.ttl files */
-void createLv2Files(const char* basename)
-{
-    const ScopedJuceInitialiser_GUI juceInitialiser;
-    ScopedPointer<AudioProcessor> filter (createPluginFilterOfType (AudioProcessor::wrapperType_VST)); // FIXME
-
-    String binary(basename);
-    String binaryTTL(binary + ".ttl");
-
-    std::cout << "Writing manifest.ttl..."; std::cout.flush();
-    std::fstream manifest("manifest.ttl", std::ios::out);
-    manifest << makeManifestFile(filter, binary) << std::endl;
-    manifest.close();
-    std::cout << " done!" << std::endl;
-
-    std::cout << "Writing " << binary << ".ttl..."; std::cout.flush();
-    std::fstream plugin(binaryTTL.toUTF8(), std::ios::out);
-    plugin << makePluginFile(filter) << std::endl;
-    plugin.close();
-    std::cout << " done!" << std::endl;
-
-#if JucePlugin_WantsLV2Presets
-    std::cout << "Writing presets.ttl..."; std::cout.flush();
-    std::fstream presets("presets.ttl", std::ios::out);
-    presets << makePresetsFile(filter) << std::endl;
-    presets.close();
-    std::cout << " done!" << std::endl;
-#endif
-}
-
-//==============================================================================
-#if JUCE_LINUX
-
-class SharedMessageThread : public Thread
-{
-public:
-    SharedMessageThread()
-      : Thread ("Lv2MessageThread"),
-        initialised (false)
-    {
-        startThread (7);
-
-        while (! initialised)
-            sleep (1);
-    }
-
-    ~SharedMessageThread()
-    {
-        MessageManager::getInstance()->stopDispatchLoop();
-        waitForThreadToExit (5000);
-    }
-
-    void run() override
-    {
-        const ScopedJuceInitialiser_GUI juceInitialiser;
-
-        MessageManager::getInstance()->setCurrentThreadAsMessageThread();
-        initialised = true;
-
-        MessageManager::getInstance()->runDispatchLoop();
-    }
-
-private:
-    bool initialised;
-};
-#endif
-
-//==============================================================================
-/**
-    Lightweight DocumentWindow subclass for external ui
-*/
-class JuceLv2ExternalUIWindow : public DocumentWindow
-{
-public:
-    /** Creates a Document Window wrapper */
-    JuceLv2ExternalUIWindow (AudioProcessorEditor* editor, const String& title) :
-            DocumentWindow (title, Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, false),
-            closed (false),
-            lastPos (0, 0)
-    {
-        setOpaque (true);
-        setContentNonOwned (editor, true);
-        setSize (editor->getWidth(), editor->getHeight());
-        setUsingNativeTitleBar (true);
-    }
-
-    /** Close button handler */
-    void closeButtonPressed()
-    {
-        saveLastPos();
-        removeFromDesktop();
-        closed = true;
-    }
-
-    void saveLastPos()
-    {
-        lastPos = getScreenPosition();
-    }
-
-    void restoreLastPos()
-    {
-        setTopLeftPosition (lastPos.getX(), lastPos.getY());
-    }
-
-    Point<int> getLastPos()
-    {
-        return lastPos;
-    }
-
-    bool isClosed()
-    {
-        return closed;
-    }
-
-    void reset()
-    {
-        closed = false;
-    }
-
-private:
-    bool closed;
-    Point<int> lastPos;
-
-    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWindow);
-};
-
-//==============================================================================
-/**
-    Juce LV2 External UI handle
-*/
-class JuceLv2ExternalUIWrapper : public LV2_External_UI_Widget
-{
-public:
-    JuceLv2ExternalUIWrapper (AudioProcessorEditor* editor, const String& title)
-        : window (editor, title)
-    {
-        // external UI calls
-        run  = doRun;
-        show = doShow;
-        hide = doHide;
-    }
-
-    ~JuceLv2ExternalUIWrapper()
-    {
-        if (window.isOnDesktop())
-            window.removeFromDesktop();
-    }
-
-    void close()
-    {
-        window.closeButtonPressed();
-    }
-
-    bool isClosed()
-    {
-        return window.isClosed();
-    }
-
-    void reset(const String& title)
-    {
-        window.reset();
-        window.setName(title);
-    }
-
-    void repaint()
-    {
-        window.repaint();
-    }
-
-    Point<int> getScreenPosition()
-    {
-        if (window.isClosed())
-            return window.getLastPos();
-        else
-            return window.getScreenPosition();
-    }
-
-    void setScreenPos (int x, int y)
-    {
-        if (! window.isClosed())
-            window.setTopLeftPosition(x, y);
-    }
-
-    //==============================================================================
-    static void doRun (LV2_External_UI_Widget* _this_)
-    {
-        const MessageManagerLock mmLock;
-        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
-
-        if (! self->isClosed())
-            self->window.repaint();
-    }
-
-    static void doShow (LV2_External_UI_Widget* _this_)
-    {
-        const MessageManagerLock mmLock;
-        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
-
-        if (! self->isClosed())
-        {
-            if (! self->window.isOnDesktop())
-                self->window.addToDesktop();
-
-            self->window.restoreLastPos();
-            self->window.setVisible(true);
-        }
-    }
-
-    static void doHide (LV2_External_UI_Widget* _this_)
-    {
-        const MessageManagerLock mmLock;
-        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
-
-        if (! self->isClosed())
-        {
-            self->window.saveLastPos();
-            self->window.setVisible(false);
-        }
-    }
-
-private:
-    JuceLv2ExternalUIWindow window;
-
-    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWrapper);
-};
-
-//==============================================================================
-/**
-    Juce LV2 Parent UI container, listens for resize events and passes them to ui-resize
-*/
-class JuceLv2ParentContainer : public Component
-{
-public:
-    JuceLv2ParentContainer (AudioProcessorEditor* editor, const LV2UI_Resize* uiResize_)
-        : uiResize(uiResize_)
-    {
-        setOpaque (true);
-        editor->setOpaque (true);
-        setBounds (editor->getBounds());
-
-        editor->setTopLeftPosition (0, 0);
-        addAndMakeVisible (editor);
-    }
-
-    ~JuceLv2ParentContainer()
-    {
-    }
-
-    void paint (Graphics&) {}
-    void paintOverChildren (Graphics&) {}
-
-    void childBoundsChanged (Component* child)
-    {
-        const int cw = child->getWidth();
-        const int ch = child->getHeight();
-
-#if JUCE_LINUX
-        XResizeWindow (display, (Window) getWindowHandle(), cw, ch);
-#else
-        setSize (cw, ch);
-#endif
-
-        if (uiResize != nullptr)
-            uiResize->ui_resize (uiResize->handle, cw, ch);
-    }
-
-    void reset (const LV2UI_Resize* uiResize_)
-    {
-        uiResize = uiResize_;
-
-        if (uiResize != nullptr)
-            uiResize->ui_resize (uiResize->handle, getWidth(), getHeight());
-    }
-
-private:
-    //==============================================================================
-    const LV2UI_Resize* uiResize;
-
-    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ParentContainer);
-};
-
-//==============================================================================
-/**
-    Juce LV2 UI handle
-*/
-class JuceLv2UIWrapper : public AudioProcessorListener,
-                         public Timer
-{
-public:
-    JuceLv2UIWrapper (AudioProcessor* filter_, LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_,
-                      LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal_)
-        : filter (filter_),
-          writeFunction (writeFunction_),
-          controller (controller_),
-          isExternal (isExternal_),
-          controlPortOffset (0),
-          lastProgramCount (0),
-          uiTouch (nullptr),
-          programsHost (nullptr),
-          externalUIHost (nullptr),
-          lastExternalUIPos (-1, -1),
-          uiResize (nullptr)
-    {
-        jassert (filter != nullptr);
-
-        filter->addListener(this);
-
-        if (filter->hasEditor())
-        {
-            editor = filter->createEditorIfNeeded();
-
-            if (editor == nullptr)
-            {
-                *widget = nullptr;
-                return;
-            }
-        }
-
-        for (int i = 0; features[i] != nullptr; ++i)
-        {
-            if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
-                uiTouch = (const LV2UI_Touch*)features[i]->data;
-
-            else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
-                programsHost = (const LV2_Programs_Host*)features[i]->data;
-        }
-
-        if (isExternal)
-        {
-            resetExternalUI (features);
-
-            if (externalUIHost != nullptr)
-            {
-                String title (filter->getName());
-
-                if (externalUIHost->plugin_human_id != nullptr)
-                    title = externalUIHost->plugin_human_id;
-
-                externalUI = new JuceLv2ExternalUIWrapper (editor, title);
-                *widget = externalUI;
-                startTimer (100);
-            }
-            else
-            {
-                *widget = nullptr;
-            }
-        }
-        else
-        {
-            resetParentUI (features);
-
-            if (parentContainer != nullptr)
-               *widget = parentContainer->getWindowHandle();
-            else
-               *widget = nullptr;
-        }
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-        controlPortOffset += 1;
-#endif
-#if JucePlugin_ProducesMidiOutput
-        controlPortOffset += 1;
-#endif
-        controlPortOffset += 2; // freewheel and latency
-        controlPortOffset += JucePlugin_MaxNumInputChannels;
-        controlPortOffset += JucePlugin_MaxNumOutputChannels;
-
-        lastProgramCount = filter->getNumPrograms();
-    }
-
-    ~JuceLv2UIWrapper()
-    {
-        PopupMenu::dismissAllActiveMenus();
-
-        filter->removeListener(this);
-
-        parentContainer = nullptr;
-        externalUI = nullptr;
-        externalUIHost = nullptr;
-
-        if (editor != nullptr)
-        {
-            filter->editorBeingDeleted (editor);
-            editor = nullptr;
-        }
-    }
-
-    //==============================================================================
-    // LV2 core calls
-
-    void lv2Cleanup()
-    {
-        const MessageManagerLock mmLock;
-
-        if (isExternal)
-        {
-            if (isTimerRunning())
-                stopTimer();
-
-            externalUIHost = nullptr;
-
-            if (externalUI != nullptr)
-            {
-                lastExternalUIPos = externalUI->getScreenPosition();
-                externalUI->close();
-            }
-        }
-        else
-        {
-            if (parentContainer != nullptr && parentContainer->isOnDesktop())
-                parentContainer->removeFromDesktop();
-        }
-    }
-
-    //==============================================================================
-    // Juce calls
-
-    void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
-    {
-        if (writeFunction != nullptr && controller != nullptr)
-            writeFunction (controller, index + controlPortOffset, sizeof (float), 0, &newValue);
-    }
-
-    void audioProcessorChanged (AudioProcessor*)
-    {
-        if (filter != nullptr && programsHost != nullptr)
-        {
-            if (filter->getNumPrograms() != lastProgramCount)
-            {
-                programsHost->program_changed (programsHost->handle, -1);
-                lastProgramCount = filter->getNumPrograms();
-            }
-            else
-                programsHost->program_changed (programsHost->handle, filter->getCurrentProgram());
-        }
-    }
-
-    void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex)
-    {
-        if (uiTouch != nullptr)
-            uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, true);
-    }
-
-    void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int parameterIndex)
-    {
-        if (uiTouch != nullptr)
-            uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, false);
-    }
-
-    void timerCallback()
-    {
-        if (externalUI != nullptr && externalUI->isClosed())
-        {
-            if (externalUIHost != nullptr)
-                externalUIHost->ui_closed (controller);
-
-            if (isTimerRunning())
-                stopTimer();
-        }
-    }
-
-    //==============================================================================
-    void resetIfNeeded (LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_, LV2UI_Widget* widget,
-                        const LV2_Feature* const* features)
-    {
-        writeFunction = writeFunction_;
-        controller = controller_;
-        uiTouch = nullptr;
-        programsHost = nullptr;
-
-        for (int i = 0; features[i] != nullptr; ++i)
-        {
-            if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
-                uiTouch = (const LV2UI_Touch*)features[i]->data;
-
-            else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
-                programsHost = (const LV2_Programs_Host*)features[i]->data;
-        }
-
-        if (isExternal)
-        {
-            resetExternalUI (features);
-            *widget = externalUI;
-        }
-        else
-        {
-            resetParentUI (features);
-            *widget = parentContainer->getWindowHandle();
-        }
-    }
-
-    void repaint()
-    {
-        const MessageManagerLock mmLock;
-
-        if (editor != nullptr)
-            editor->repaint();
-
-        if (parentContainer != nullptr)
-            parentContainer->repaint();
-
-        if (externalUI != nullptr)
-            externalUI->repaint();
-    }
-
-private:
-    AudioProcessor* const filter;
-    ScopedPointer<AudioProcessorEditor> editor;
-
-    LV2UI_Write_Function writeFunction;
-    LV2UI_Controller controller;
-    const bool isExternal;
-
-    uint32 controlPortOffset;
-    int lastProgramCount;
-
-    const LV2UI_Touch* uiTouch;
-    const LV2_Programs_Host* programsHost;
-
-    ScopedPointer<JuceLv2ExternalUIWrapper> externalUI;
-    const LV2_External_UI_Host* externalUIHost;
-    Point<int> lastExternalUIPos;
-
-    ScopedPointer<JuceLv2ParentContainer> parentContainer;
-    const LV2UI_Resize* uiResize;
-
-    //==============================================================================
-    void resetExternalUI (const LV2_Feature* const* features)
-    {
-        externalUIHost = nullptr;
-
-        for (int i = 0; features[i] != nullptr; ++i)
-        {
-            if (strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0)
-            {
-                externalUIHost = (const LV2_External_UI_Host*)features[i]->data;
-                break;
-            }
-        }
-
-        if (externalUI != nullptr)
-        {
-            String title(filter->getName());
-
-            if (externalUIHost->plugin_human_id != nullptr)
-                title = externalUIHost->plugin_human_id;
-
-            if (lastExternalUIPos.getX() != -1 && lastExternalUIPos.getY() != -1)
-                externalUI->setScreenPos(lastExternalUIPos.getX(), lastExternalUIPos.getY());
-
-            externalUI->reset(title);
-            startTimer (100);
-        }
-    }
-
-    void resetParentUI (const LV2_Feature* const* features)
-    {
-        void* parent = nullptr;
-        uiResize = nullptr;
-
-        for (int i = 0; features[i] != nullptr; ++i)
-        {
-            if (strcmp(features[i]->URI, LV2_UI__parent) == 0)
-                parent = features[i]->data;
-
-            else if (strcmp(features[i]->URI, LV2_UI__resize) == 0)
-                uiResize = (const LV2UI_Resize*)features[i]->data;
-        }
-
-        if (parent != nullptr)
-        {
-            if (parentContainer == nullptr)
-                parentContainer = new JuceLv2ParentContainer (editor, uiResize);
-
-            parentContainer->setVisible (false);
-
-            if (parentContainer->isOnDesktop())
-                parentContainer->removeFromDesktop();
-
-            parentContainer->addToDesktop (0, parent);
-
-#if JUCE_LINUX
-            Window hostWindow = (Window) parent;
-            Window editorWnd  = (Window) parentContainer->getWindowHandle();
-            XReparentWindow (display, editorWnd, hostWindow, 0, 0);
-#endif
-
-            parentContainer->reset (uiResize);
-            parentContainer->setVisible (true);
-        }
-    }
-
-    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2UIWrapper)
-};
-
-//==============================================================================
-/**
-    Juce LV2 handle
-*/
-class JuceLv2Wrapper : public AudioPlayHead
-{
-public:
-    //==============================================================================
-    JuceLv2Wrapper (double sampleRate_, const LV2_Feature* const* features)
-        : numInChans (JucePlugin_MaxNumInputChannels),
-          numOutChans (JucePlugin_MaxNumOutputChannels),
-          bufferSize (2048),
-          sampleRate (sampleRate_),
-          uridMap (nullptr),
-          uridAtomBlank (0),
-          uridAtomObject (0),
-          uridAtomDouble (0),
-          uridAtomFloat (0),
-          uridAtomInt (0),
-          uridAtomLong (0),
-          uridAtomSequence (0),
-          uridMidiEvent (0),
-          uridTimePos (0),
-          uridTimeBar (0),
-          uridTimeBarBeat (0),
-          uridTimeBeatsPerBar (0),
-          uridTimeBeatsPerMinute (0),
-          uridTimeBeatUnit (0),
-          uridTimeFrame (0),
-          uridTimeSpeed (0),
-          usingNominalBlockLength (false)
-    {
-        filter = createPluginFilterOfType (AudioProcessor::wrapperType_VST); // FIXME
-        jassert (filter != nullptr);
-
-        filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0);
-        filter->setPlayHead (this);
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-        portEventsIn = nullptr;
-#endif
-#if JucePlugin_ProducesMidiOutput
-        portMidiOut = nullptr;
-#endif
-
-        portFreewheel = nullptr;
-        portLatency   = nullptr;
-
-        for (int i=0; i < numInChans; ++i)
-            portAudioIns[i] = nullptr;
-        for (int i=0; i < numOutChans; ++i)
-            portAudioOuts[i] = nullptr;
-
-        portControls.insertMultiple (0, nullptr, filter->getNumParameters());
-
-        for (int i=0; i < filter->getNumParameters(); ++i)
-            lastControlValues.add (filter->getParameter(i));
-
-        curPosInfo.resetToDefault();
-
-        // we need URID_Map first
-        for (int i=0; features[i] != nullptr; ++i)
-        {
-            if (strcmp(features[i]->URI, LV2_URID__map) == 0)
-            {
-                uridMap = (const LV2_URID_Map*)features[i]->data;
-                break;
-            }
-        }
-
-        // we require uridMap to work properly (it's set as required feature)
-        jassert (uridMap != nullptr);
-
-        if (uridMap != nullptr)
-        {
-            uridAtomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank);
-            uridAtomObject = uridMap->map(uridMap->handle, LV2_ATOM__Object);
-            uridAtomDouble = uridMap->map(uridMap->handle, LV2_ATOM__Double);
-            uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
-            uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
-            uridAtomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long);
-            uridAtomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence);
-            uridMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent);
-            uridTimePos = uridMap->map(uridMap->handle, LV2_TIME__Position);
-            uridTimeBar = uridMap->map(uridMap->handle, LV2_TIME__bar);
-            uridTimeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat);
-            uridTimeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar);
-            uridTimeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute);
-            uridTimeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit);
-            uridTimeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame);
-            uridTimeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed);
-
-            for (int i=0; features[i] != nullptr; ++i)
-            {
-                if (strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
-                {
-                    const LV2_Options_Option* options = (const LV2_Options_Option*)features[i]->data;
-
-                    for (int j=0; options[j].key != 0; ++j)
-                    {
-                        if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
-                        {
-                            if (options[j].type == uridAtomInt)
-                            {
-                                bufferSize = *(int*)options[j].value;
-                                usingNominalBlockLength = true;
-                            }
-                            else
-                            {
-                                std::cerr << "Host provides nominalBlockLength but has wrong value type" << std::endl;
-                            }
-                            break;
-                        }
-
-                        if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
-                        {
-                            if (options[j].type == uridAtomInt)
-                                bufferSize = *(int*)options[j].value;
-                            else
-                                std::cerr << "Host provides maxBlockLength but has wrong value type" << std::endl;
-
-                            // no break, continue in case host supports nominalBlockLength
-                        }
-                    }
-                    break;
-                }
-            }
-        }
-
-        progDesc.bank = 0;
-        progDesc.program = 0;
-        progDesc.name = nullptr;
-    }
-
-    ~JuceLv2Wrapper ()
-    {
-        const MessageManagerLock mmLock;
-
-        ui = nullptr;
-        filter = nullptr;
-
-        if (progDesc.name != nullptr)
-            free((void*)progDesc.name);
-
-        portControls.clear();
-        lastControlValues.clear();
-    }
-
-    //==============================================================================
-    // LV2 core calls
-
-    void lv2ConnectPort (uint32 portId, void* dataLocation)
-    {
-        uint32 index = 0;
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-        if (portId == index++)
-        {
-            portEventsIn = (LV2_Atom_Sequence*)dataLocation;
-            return;
-        }
-#endif
-
-#if JucePlugin_ProducesMidiOutput
-        if (portId == index++)
-        {
-            portMidiOut = (LV2_Atom_Sequence*)dataLocation;
-            return;
-        }
-#endif
-
-        if (portId == index++)
-        {
-            portFreewheel = (float*)dataLocation;
-            return;
-        }
-
-        if (portId == index++)
-        {
-            portLatency = (float*)dataLocation;
-            return;
-        }
-
-        for (int i=0; i < numInChans; ++i)
-        {
-            if (portId == index++)
-            {
-                portAudioIns[i] = (float*)dataLocation;
-                return;
-            }
-        }
-
-        for (int i=0; i < numOutChans; ++i)
-        {
-            if (portId == index++)
-            {
-                portAudioOuts[i] = (float*)dataLocation;
-                return;
-            }
-        }
-
-        for (int i=0; i < filter->getNumParameters(); ++i)
-        {
-            if (portId == index++)
-            {
-                portControls.set(i, (float*)dataLocation);
-                return;
-            }
-        }
-    }
-
-    void lv2Activate()
-    {
-        jassert (filter != nullptr);
-
-        filter->prepareToPlay (sampleRate, bufferSize);
-        filter->setPlayConfigDetails (numInChans, numOutChans, sampleRate, bufferSize);
-
-        channels.calloc (numInChans + numOutChans);
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_ProducesMidiOutput)
-        midiEvents.ensureSize (2048);
-        midiEvents.clear();
-#endif
-    }
-
-    void lv2Deactivate()
-    {
-        jassert (filter != nullptr);
-
-        filter->releaseResources();
-
-        channels.free();
-    }
-
-    void lv2Run (uint32 sampleCount)
-    {
-        jassert (filter != nullptr);
-
-        if (portLatency != nullptr)
-            *portLatency = filter->getLatencySamples();
-
-        if (portFreewheel != nullptr)
-            filter->setNonRealtime (*portFreewheel >= 0.5f);
-
-        if (sampleCount == 0)
-        {
-            /**
-               LV2 pre-roll
-               Hosts might use this to force plugins to update its output control ports.
-               (plugins can only access port locations during run) */
-            return;
-        }
-
-        // Check for updated parameters
-        {
-            float curValue;
-
-            for (int i = 0; i < portControls.size(); ++i)
-            {
-                if (portControls[i] != nullptr)
-                {
-                    curValue = *portControls[i];
-
-                    if (lastControlValues[i] != curValue)
-                    {
-                        filter->setParameter (i, curValue);
-                        lastControlValues.setUnchecked (i, curValue);
-                    }
-                }
-            }
-        }
-
-        {
-            const ScopedLock sl (filter->getCallbackLock());
-
-            if (filter->isSuspended() && false)
-            {
-                for (int i = 0; i < numOutChans; ++i)
-                    zeromem (portAudioOuts[i], sizeof (float) * sampleCount);
-            }
-            else
-            {
-                int i;
-                for (i = 0; i < numOutChans; ++i)
-                {
-                    channels[i] = portAudioOuts[i];
-
-                    if (i < numInChans && portAudioIns[i] != portAudioOuts[i])
-                        FloatVectorOperations::copy (portAudioOuts [i], portAudioIns[i], sampleCount);
-                }
-
-                for (; i < numInChans; ++i)
-                    channels [i] = portAudioIns[i];
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-                if (portEventsIn != nullptr)
-                {
-                    midiEvents.clear();
-
-                    LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter)
-                    {
-                        const LV2_Atom_Event* event = (const LV2_Atom_Event*)iter;
-
-                        if (event == nullptr)
-                            continue;
-                        if (event->time.frames >= sampleCount)
-                            break;
-
- #if JucePlugin_WantsMidiInput
-                        if (event->body.type == uridMidiEvent)
-                        {
-                            const uint8* data = (const uint8*)(event + 1);
-                            midiEvents.addEvent(data, event->body.size, event->time.frames);
-                            continue;
-                        }
- #endif
- #if JucePlugin_WantsLV2TimePos
-                        if (event->body.type == uridAtomBlank || event->body.type == uridAtomObject)
-                        {
-                            const LV2_Atom_Object* obj = (LV2_Atom_Object*)&event->body;
-
-                            if (obj->body.otype != uridTimePos)
-                                continue;
-
-                            LV2_Atom* bar = nullptr;
-                            LV2_Atom* barBeat = nullptr;
-                            LV2_Atom* beatUnit = nullptr;
-                            LV2_Atom* beatsPerBar = nullptr;
-                            LV2_Atom* beatsPerMinute = nullptr;
-                            LV2_Atom* frame = nullptr;
-                            LV2_Atom* speed = nullptr;
-
-                            lv2_atom_object_get (obj,
-                                                 uridTimeBar, &bar,
-                                                 uridTimeBarBeat, &barBeat,
-                                                 uridTimeBeatUnit, &beatUnit,
-                                                 uridTimeBeatsPerBar, &beatsPerBar,
-                                                 uridTimeBeatsPerMinute, &beatsPerMinute,
-                                                 uridTimeFrame, &frame,
-                                                 uridTimeSpeed, &speed,
-                                                 nullptr);
-
-                            // need to handle this first as other values depend on it
-                            if (speed != nullptr)
-                            {
-                                /**/ if (speed->type == uridAtomDouble)
-                                    lastPositionData.speed = ((LV2_Atom_Double*)speed)->body;
-                                else if (speed->type == uridAtomFloat)
-                                    lastPositionData.speed = ((LV2_Atom_Float*)speed)->body;
-                                else if (speed->type == uridAtomInt)
-                                    lastPositionData.speed = ((LV2_Atom_Int*)speed)->body;
-                                else if (speed->type == uridAtomLong)
-                                    lastPositionData.speed = ((LV2_Atom_Long*)speed)->body;
-
-                                curPosInfo.isPlaying = lastPositionData.speed != 0.0;
-                            }
-
-                            if (bar != nullptr)
-                            {
-                                /**/ if (bar->type == uridAtomDouble)
-                                    lastPositionData.bar = ((LV2_Atom_Double*)bar)->body;
-                                else if (bar->type == uridAtomFloat)
-                                    lastPositionData.bar = ((LV2_Atom_Float*)bar)->body;
-                                else if (bar->type == uridAtomInt)
-                                    lastPositionData.bar = ((LV2_Atom_Int*)bar)->body;
-                                else if (bar->type == uridAtomLong)
-                                    lastPositionData.bar = ((LV2_Atom_Long*)bar)->body;
-                            }
-
-                            if (barBeat != nullptr)
-                            {
-                                /**/ if (barBeat->type == uridAtomDouble)
-                                    lastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body;
-                                else if (barBeat->type == uridAtomFloat)
-                                    lastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body;
-                                else if (barBeat->type == uridAtomInt)
-                                    lastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body;
-                                else if (barBeat->type == uridAtomLong)
-                                    lastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body;
-                            }
-
-                            if (beatUnit != nullptr)
-                            {
-                                /**/ if (beatUnit->type == uridAtomDouble)
-                                    lastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body;
-                                else if (beatUnit->type == uridAtomFloat)
-                                    lastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body;
-                                else if (beatUnit->type == uridAtomInt)
-                                    lastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body;
-                                else if (beatUnit->type == uridAtomLong)
-                                    lastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body;
-
-                                if (lastPositionData.beatUnit > 0)
-                                    curPosInfo.timeSigDenominator = lastPositionData.beatUnit;
-                            }
-
-                            if (beatsPerBar != nullptr)
-                            {
-                                /**/ if (beatsPerBar->type == uridAtomDouble)
-                                    lastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body;
-                                else if (beatsPerBar->type == uridAtomFloat)
-                                    lastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body;
-                                else if (beatsPerBar->type == uridAtomInt)
-                                    lastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body;
-                                else if (beatsPerBar->type == uridAtomLong)
-                                    lastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body;
-
-                                if (lastPositionData.beatsPerBar > 0.0f)
-                                    curPosInfo.timeSigNumerator = lastPositionData.beatsPerBar;
-                            }
-
-                            if (beatsPerMinute != nullptr)
-                            {
-                                /**/ if (beatsPerMinute->type == uridAtomDouble)
-                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body;
-                                else if (beatsPerMinute->type == uridAtomFloat)
-                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body;
-                                else if (beatsPerMinute->type == uridAtomInt)
-                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body;
-                                else if (beatsPerMinute->type == uridAtomLong)
-                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body;
-
-                                if (lastPositionData.beatsPerMinute > 0.0f)
-                                {
-                                    curPosInfo.bpm = lastPositionData.beatsPerMinute;
-
-                                    if (lastPositionData.speed != 0)
-                                        curPosInfo.bpm *= std::abs(lastPositionData.speed);
-                                }
-                            }
-
-                            if (frame != nullptr)
-                            {
-                                /**/ if (frame->type == uridAtomDouble)
-                                    lastPositionData.frame = ((LV2_Atom_Double*)frame)->body;
-                                else if (frame->type == uridAtomFloat)
-                                    lastPositionData.frame = ((LV2_Atom_Float*)frame)->body;
-                                else if (frame->type == uridAtomInt)
-                                    lastPositionData.frame = ((LV2_Atom_Int*)frame)->body;
-                                else if (frame->type == uridAtomLong)
-                                    lastPositionData.frame = ((LV2_Atom_Long*)frame)->body;
-
-                                if (lastPositionData.frame >= 0)
-                                {
-                                    curPosInfo.timeInSamples = lastPositionData.frame;
-                                    curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
-                                }
-                            }
-
-                            if (lastPositionData.bar >= 0 && lastPositionData.beatsPerBar > 0.0f)
-                            {
-                                curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
-
-                                if (lastPositionData.barBeat >= 0.0f)
-                                    curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
-                            }
-
-                            lastPositionData.extraValid = (lastPositionData.beatsPerMinute > 0.0 &&
-                                                           lastPositionData.beatUnit > 0 &&
-                                                           lastPositionData.beatsPerBar > 0.0f);
-                        }
- #endif
-                    }
-                }
-#endif
-                {
-                    AudioSampleBuffer chans (channels, jmax (numInChans, numOutChans), sampleCount);
-                    filter->processBlock (chans, midiEvents);
-                }
-            }
-        }
-
-#if JucePlugin_WantsLV2TimePos
-        // update timePos for next callback
-        if (lastPositionData.speed != 0.0)
-        {
-            if (lastPositionData.speed > 0.0)
-            {
-                // playing forwards
-                lastPositionData.frame += sampleCount;
-            }
-            else
-            {
-                // playing backwards
-                lastPositionData.frame -= sampleCount;
-
-                if (lastPositionData.frame < 0)
-                    lastPositionData.frame = 0;
-            }
-
-            curPosInfo.timeInSamples = lastPositionData.frame;
-            curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
-
-            if (lastPositionData.extraValid)
-            {
-                const double beatsPerMinute = lastPositionData.beatsPerMinute * lastPositionData.speed;
-                const double framesPerBeat  = 60.0 * sampleRate / beatsPerMinute;
-                const double addedBarBeats  = double(sampleCount) / framesPerBeat;
-
-                if (lastPositionData.bar >= 0 && lastPositionData.barBeat >= 0.0f)
-                {
-                    lastPositionData.bar    += std::floor((lastPositionData.barBeat+addedBarBeats)/
-                                                           lastPositionData.beatsPerBar);
-                    lastPositionData.barBeat = std::fmod(lastPositionData.barBeat+addedBarBeats,
-                                                         lastPositionData.beatsPerBar);
-
-                    if (lastPositionData.bar < 0)
-                        lastPositionData.bar = 0;
-
-                    curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
-                    curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
-                }
-
-                curPosInfo.bpm = std::abs(beatsPerMinute);
-            }
-        }
-#endif
-
-        if (! midiEvents.isEmpty())
-        {
-#if JucePlugin_ProducesMidiOutput
-            if (portMidiOut != nullptr)
-            {
-                const uint8* midiEventData;
-                int midiEventSize, midiEventPosition;
-                MidiBuffer::Iterator i (midiEvents);
-
-                uint32_t size, offset = 0;
-                LV2_Atom_Event* aev;
-
-                const uint32_t capacity = portMidiOut->atom.size;
-
-                portMidiOut->atom.size = 0;
-                portMidiOut->atom.type = uridAtomSequence;
-                portMidiOut->body.unit = 0;
-                portMidiOut->body.pad  = 0;
-
-                while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
-                {
-                    jassert (midiEventPosition >= 0 && midiEventPosition < (int)sampleCount);
-
-                    if (sizeof(LV2_Atom_Event) + midiEventSize > capacity - offset)
-                        break;
-
-                    aev = (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, portMidiOut) + offset);
-                    aev->time.frames = midiEventPosition;
-                    aev->body.type   = uridMidiEvent;
-                    aev->body.size   = midiEventSize;
-                    memcpy(LV2_ATOM_BODY(&aev->body), midiEventData, midiEventSize);
-
-                    size    = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEventSize);
-                    offset += size;
-                    portMidiOut->atom.size += size;
-                }
-            }
-#endif
-            midiEvents.clear();
-        }
-    }
-
-    //==============================================================================
-    // LV2 extended calls
-
-    uint32_t lv2GetOptions (LV2_Options_Option* options)
-    {
-        // currently unused
-        return LV2_OPTIONS_SUCCESS;
-    }
-
-    uint32_t lv2SetOptions (const LV2_Options_Option* options)
-    {
-        for (int j=0; options[j].key != 0; ++j)
-        {
-            if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
-            {
-                if (options[j].type == uridAtomInt)
-                    bufferSize = *(int*)options[j].value;
-                else
-                    std::cerr << "Host changed nominalBlockLength but with wrong value type" << std::endl;
-            }
-            else if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! usingNominalBlockLength)
-            {
-                if (options[j].type == uridAtomInt)
-                    bufferSize = *(int*)options[j].value;
-                else
-                    std::cerr << "Host changed maxBlockLength but with wrong value type" << std::endl;
-            }
-            else if (options[j].key == uridMap->map(uridMap->handle, LV2_CORE__sampleRate))
-            {
-                if (options[j].type == uridAtomDouble)
-                    sampleRate = *(double*)options[j].value;
-                else
-                    std::cerr << "Host changed sampleRate but with wrong value type" << std::endl;
-            }
-        }
-
-        return LV2_OPTIONS_SUCCESS;
-    }
-
-    const LV2_Program_Descriptor* lv2GetProgram (uint32_t index)
-    {
-        jassert (filter != nullptr);
-
-        if (progDesc.name != nullptr)
-        {
-            free((void*)progDesc.name);
-            progDesc.name = nullptr;
-        }
-
-        if ((int)index < filter->getNumPrograms())
-        {
-            progDesc.bank    = index / 128;
-            progDesc.program = index % 128;
-            progDesc.name    = strdup(filter->getProgramName(index).toUTF8());
-            return &progDesc;
-        }
-
-        return nullptr;
-    }
-
-    void lv2SelectProgram (uint32_t bank, uint32_t program)
-    {
-        jassert (filter != nullptr);
-
-        int realProgram = bank * 128 + program;
-
-        if (realProgram < filter->getNumPrograms())
-        {
-            filter->setCurrentProgram(realProgram);
-
-            // update input control ports now
-            for (int i = 0; i < portControls.size(); ++i)
-            {
-                float value = filter->getParameter(i);
-
-                if (portControls[i] != nullptr)
-                    *portControls[i] = value;
-
-                lastControlValues.set(i, value);
-            }
-        }
-    }
-
-    LV2_State_Status lv2SaveState (LV2_State_Store_Function store, LV2_State_Handle stateHandle)
-    {
-        jassert (filter != nullptr);
-
-#if JucePlugin_WantsLV2StateString
-        String stateData(filter->getStateInformationString().replace("\r\n","\n"));
-        CharPointer_UTF8 charData(stateData.toUTF8());
-
-        store (stateHandle,
-               uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
-               charData.getAddress(),
-               charData.sizeInBytes(),
-               uridMap->map(uridMap->handle, LV2_ATOM__String),
-               LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
-#else
-        MemoryBlock chunkMemory;
-        filter->getCurrentProgramStateInformation (chunkMemory);
-
-        store (stateHandle,
-               uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
-               chunkMemory.getData(),
-               chunkMemory.getSize(),
-               uridMap->map(uridMap->handle, LV2_ATOM__Chunk),
-               LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
-#endif
-
-        return LV2_STATE_SUCCESS;
-    }
-
-    LV2_State_Status lv2RestoreState (LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle, uint32_t flags)
-    {
-        jassert (filter != nullptr);
-
-        size_t size = 0;
-        uint32 type = 0;
-        const void* data = retrieve (stateHandle,
-#if JucePlugin_WantsLV2StateString
-                                     uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
-#else
-                                     uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
-#endif
-                                     &size, &type, &flags);
-
-        if (data == nullptr || size == 0 || type == 0)
-            return LV2_STATE_ERR_UNKNOWN;
-
-#if JucePlugin_WantsLV2StateString
-        if (type == uridMap->map (uridMap->handle, LV2_ATOM__String))
-        {
-            String stateData (CharPointer_UTF8(static_cast<const char*>(data)));
-            filter->setStateInformationString (stateData);
-
-            if (ui != nullptr)
-                ui->repaint();
-
-            return LV2_STATE_SUCCESS;
-        }
-#else
-        if (type == uridMap->map (uridMap->handle, LV2_ATOM__Chunk))
-        {
-            filter->setCurrentProgramStateInformation (data, size);
-
-            if (ui != nullptr)
-                ui->repaint();
-
-            return LV2_STATE_SUCCESS;
-        }
-#endif
-
-        return LV2_STATE_ERR_BAD_TYPE;
-    }
-
-    //==============================================================================
-    // Juce calls
-
-    bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
-    {
-#if JucePlugin_WantsLV2TimePos
-        info = curPosInfo;
-        return true;
-#else
-        return false;
-#endif
-    }
-
-    //==============================================================================
-    JuceLv2UIWrapper* getUI (LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget,
-                             const LV2_Feature* const* features, bool isExternal)
-    {
-        const MessageManagerLock mmLock;
-
-        if (ui != nullptr)
-            ui->resetIfNeeded (writeFunction, controller, widget, features);
-        else
-            ui = new JuceLv2UIWrapper (filter, writeFunction, controller, widget, features, isExternal);
-
-        return ui;
-    }
-
-private:
-#if JUCE_LINUX
-    SharedResourcePointer<SharedMessageThread> msgThread;
-#else
-    SharedResourcePointer<ScopedJuceInitialiser_GUI> sharedJuceGUI;
-#endif
-
-    ScopedPointer<AudioProcessor> filter;
-    ScopedPointer<JuceLv2UIWrapper> ui;
-    HeapBlock<float*> channels;
-    MidiBuffer midiEvents;
-    int numInChans, numOutChans;
-
-#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
-    LV2_Atom_Sequence* portEventsIn;
-#endif
-#if JucePlugin_ProducesMidiOutput
-    LV2_Atom_Sequence* portMidiOut;
-#endif
-    float* portFreewheel;
-    float* portLatency;
-    float* portAudioIns[JucePlugin_MaxNumInputChannels];
-    float* portAudioOuts[JucePlugin_MaxNumOutputChannels];
-    Array<float*> portControls;
-
-    uint32 bufferSize;
-    double sampleRate;
-    Array<float> lastControlValues;
-    AudioPlayHead::CurrentPositionInfo curPosInfo;
-
-    struct Lv2PositionData {
-        int64_t  bar;
-        float    barBeat;
-        uint32_t beatUnit;
-        float    beatsPerBar;
-        float    beatsPerMinute;
-        int64_t  frame;
-        double   speed;
-        bool     extraValid;
-
-        Lv2PositionData()
-            : bar(-1),
-              barBeat(-1.0f),
-              beatUnit(0),
-              beatsPerBar(0.0f),
-              beatsPerMinute(0.0f),
-              frame(-1),
-              speed(0.0),
-              extraValid(false) {}
-    };
-    Lv2PositionData lastPositionData;
-
-    const LV2_URID_Map* uridMap;
-    LV2_URID uridAtomBlank;
-    LV2_URID uridAtomObject;
-    LV2_URID uridAtomDouble;
-    LV2_URID uridAtomFloat;
-    LV2_URID uridAtomInt;
-    LV2_URID uridAtomLong;
-    LV2_URID uridAtomSequence;
-    LV2_URID uridMidiEvent;
-    LV2_URID uridTimePos;
-    LV2_URID uridTimeBar;
-    LV2_URID uridTimeBarBeat;
-    LV2_URID uridTimeBeatsPerBar;    // timeSigNumerator
-    LV2_URID uridTimeBeatsPerMinute; // bpm
-    LV2_URID uridTimeBeatUnit;       // timeSigDenominator
-    LV2_URID uridTimeFrame;          // timeInSamples
-    LV2_URID uridTimeSpeed;
-
-    bool usingNominalBlockLength; // if false use maxBlockLength
-
-    LV2_Program_Descriptor progDesc;
-
-    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Wrapper)
-};
-
-//==============================================================================
-// LV2 descriptor functions
-
-static LV2_Handle juceLV2_Instantiate (const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features)
-{
-    return new JuceLv2Wrapper (sampleRate, features);
-}
-
-#define handlePtr ((JuceLv2Wrapper*)handle)
-
-static void juceLV2_ConnectPort (LV2_Handle handle, uint32 port, void* dataLocation)
-{
-    handlePtr->lv2ConnectPort (port, dataLocation);
-}
-
-static void juceLV2_Activate (LV2_Handle handle)
-{
-    handlePtr->lv2Activate();
-}
-
-static void juceLV2_Run( LV2_Handle handle, uint32 sampleCount)
-{
-    handlePtr->lv2Run (sampleCount);
-}
-
-static void juceLV2_Deactivate (LV2_Handle handle)
-{
-    handlePtr->lv2Deactivate();
-}
-
-static void juceLV2_Cleanup (LV2_Handle handle)
-{
-    delete handlePtr;
-}
-
-//==============================================================================
-// LV2 extended functions
-
-static uint32_t juceLV2_getOptions (LV2_Handle handle, LV2_Options_Option* options)
-{
-    return handlePtr->lv2GetOptions(options);
-}
-
-static uint32_t juceLV2_setOptions (LV2_Handle handle, const LV2_Options_Option* options)
-{
-    return handlePtr->lv2SetOptions(options);
-}
-
-static const LV2_Program_Descriptor* juceLV2_getProgram (LV2_Handle handle, uint32_t index)
-{
-    return handlePtr->lv2GetProgram(index);
-}
-
-static void juceLV2_selectProgram (LV2_Handle handle, uint32_t bank, uint32_t program)
-{
-    handlePtr->lv2SelectProgram(bank, program);
-}
-
-static LV2_State_Status juceLV2_SaveState (LV2_Handle handle, LV2_State_Store_Function store, LV2_State_Handle stateHandle,
-                                           uint32_t, const LV2_Feature* const*)
-{
-    return handlePtr->lv2SaveState(store, stateHandle);
-}
-
-static LV2_State_Status juceLV2_RestoreState (LV2_Handle handle, LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle,
-                                              uint32_t flags, const LV2_Feature* const*)
-{
-    return handlePtr->lv2RestoreState(retrieve, stateHandle, flags);
-}
-
-#undef handlePtr
-
-static const void* juceLV2_ExtensionData (const char* uri)
-{
-    static const LV2_Options_Interface options = { juceLV2_getOptions, juceLV2_setOptions };
-    static const LV2_Programs_Interface programs = { juceLV2_getProgram, juceLV2_selectProgram };
-    static const LV2_State_Interface state = { juceLV2_SaveState, juceLV2_RestoreState };
-
-    if (strcmp(uri, LV2_OPTIONS__interface) == 0)
-        return &options;
-    if (strcmp(uri, LV2_PROGRAMS__Interface) == 0)
-        return &programs;
-    if (strcmp(uri, LV2_STATE__interface) == 0)
-        return &state;
-
-    return nullptr;
-}
-
-//==============================================================================
-// LV2 UI descriptor functions
-
-static LV2UI_Handle juceLV2UI_Instantiate (LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
-                                           LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal)
-{
-    for (int i = 0; features[i] != nullptr; ++i)
-    {
-        if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr)
-        {
-            JuceLv2Wrapper* wrapper = (JuceLv2Wrapper*)features[i]->data;
-            return wrapper->getUI(writeFunction, controller, widget, features, isExternal);
-        }
-    }
-
-    std::cerr << "Host does not support instance-access, cannot use UI" << std::endl;
-    return nullptr;
-}
-
-static LV2UI_Handle juceLV2UI_InstantiateExternal (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
-                                                   LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
-{
-    return juceLV2UI_Instantiate(writeFunction, controller, widget, features, true);
-}
-
-static LV2UI_Handle juceLV2UI_InstantiateParent (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
-                                                 LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
-{
-    return juceLV2UI_Instantiate(writeFunction, controller, widget, features, false);
-}
-
-static void juceLV2UI_Cleanup (LV2UI_Handle handle)
-{
-    ((JuceLv2UIWrapper*)handle)->lv2Cleanup();
-}
-
-//==============================================================================
-// static LV2 Descriptor objects
-
-static const LV2_Descriptor JuceLv2Plugin = {
-    strdup(getPluginURI().toRawUTF8()),
-    juceLV2_Instantiate,
-    juceLV2_ConnectPort,
-    juceLV2_Activate,
-    juceLV2_Run,
-    juceLV2_Deactivate,
-    juceLV2_Cleanup,
-    juceLV2_ExtensionData
-};
-
-static const LV2UI_Descriptor JuceLv2UI_External = {
-    strdup(String(getPluginURI() + "#ExternalUI").toRawUTF8()),
-    juceLV2UI_InstantiateExternal,
-    juceLV2UI_Cleanup,
-    nullptr,
-    nullptr
-};
-
-static const LV2UI_Descriptor JuceLv2UI_Parent = {
-    strdup(String(getPluginURI() + "#ParentUI").toRawUTF8()),
-    juceLV2UI_InstantiateParent,
-    juceLV2UI_Cleanup,
-    nullptr,
-    nullptr
-};
-
-static const struct DescriptorCleanup {
-    DescriptorCleanup() {}
-    ~DescriptorCleanup()
-    {
-        free((void*)JuceLv2Plugin.URI);
-        free((void*)JuceLv2UI_External.URI);
-        free((void*)JuceLv2UI_Parent.URI);
-    }
-} _descCleanup;
-
-#if JUCE_WINDOWS
- #define JUCE_EXPORTED_FUNCTION extern "C" __declspec (dllexport)
-#else
- #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility("default")))
-#endif
-
-//==============================================================================
-// startup code..
-
-JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename);
-JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename)
-{
-    createLv2Files (basename);
-}
-
-JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index);
-JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index)
-{
-    return (index == 0) ? &JuceLv2Plugin : nullptr;
-}
-
-JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index);
-JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index)
-{
-    switch (index)
-    {
-    case 0:
-        return &JuceLv2UI_External;
-    case 1:
-        return &JuceLv2UI_Parent;
-    default:
-        return nullptr;
-    }
-}
-
-#endif
diff --git a/debian/extra/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h b/debian/extra/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h
new file mode 100644 (file)
index 0000000..789cd3f
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+  LV2 External UI extension
+  This work is in public domain.
+
+  This file is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+  If you have questions, contact Filipe Coelho (aka falkTX) <falktx@falktx.com>
+  or ask in #lad channel, FreeNode IRC network.
+*/
+
+/**
+   @file lv2_external_ui.h
+   C header for the LV2 External UI extension <http://kxstudio.sf.net/ns/lv2ext/external-ui>.
+*/
+
+#ifndef LV2_EXTERNAL_UI_H
+#define LV2_EXTERNAL_UI_H
+
+#include "ui.h"
+
+#define LV2_EXTERNAL_UI_URI     "http://kxstudio.sf.net/ns/lv2ext/external-ui"
+#define LV2_EXTERNAL_UI_PREFIX  LV2_EXTERNAL_UI_URI "#"
+
+#define LV2_EXTERNAL_UI__Host   LV2_EXTERNAL_UI_PREFIX "Host"
+#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget"
+
+/** This extension used to be defined by a lv2plug.in URI */
+#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned
+ * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget.
+ * UI is created in invisible state.
+ */
+typedef struct _LV2_External_UI_Widget {
+  /**
+   * Host calls this function regulary. UI library implementing the
+   * callback may do IPC or redraw the UI.
+   *
+   * @param _this_ the UI context
+   */
+  void (*run)(struct _LV2_External_UI_Widget * _this_);
+
+  /**
+   * Host calls this function to make the plugin UI visible.
+   *
+   * @param _this_ the UI context
+   */
+  void (*show)(struct _LV2_External_UI_Widget * _this_);
+
+  /**
+   * Host calls this function to make the plugin UI invisible again.
+   *
+   * @param _this_ the UI context
+   */
+  void (*hide)(struct _LV2_External_UI_Widget * _this_);
+
+} LV2_External_UI_Widget;
+
+#define LV2_EXTERNAL_UI_RUN(ptr)  (ptr)->run(ptr)
+#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr)
+#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr)
+
+/**
+ * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature.
+ * LV2_Feature::data must be pointer to LV2_External_UI_Host.
+ */
+typedef struct _LV2_External_UI_Host {
+  /**
+   * Callback that plugin UI will call when UI (GUI window) is closed by user.
+   * This callback will be called during execution of LV2_External_UI_Widget::run()
+   * (i.e. not from background thread).
+   *
+   * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup().
+   * If host wants to make the UI visible again, the UI must be reinstantiated.
+   *
+   * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI,
+   *       some hosts will not call LV2UI_Descriptor::cleanup() as they should,
+   *       and may call show() again without re-initialization.
+   *
+   * @param controller Host context associated with plugin UI, as
+   *                   supplied to LV2UI_Descriptor::instantiate().
+   */
+  void (*ui_closed)(LV2UI_Controller controller);
+
+  /**
+   * Optional (may be NULL) "user friendly" identifier which the UI
+   * may display to allow a user to easily associate this particular
+   * UI instance with the correct plugin instance as it is represented
+   * by the host (e.g. "track 1" or "channel 4").
+   *
+   * If supplied by host, the string will be referenced only during
+   * LV2UI_Descriptor::instantiate()
+   */
+  const char * plugin_human_id;
+
+} LV2_External_UI_Host;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV2_EXTERNAL_UI_H */
diff --git a/debian/extra/juce_audio_plugin_client/LV2/includes/lv2_programs.h b/debian/extra/juce_audio_plugin_client/LV2/includes/lv2_programs.h
new file mode 100644 (file)
index 0000000..01e2877
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+  LV2 Programs Extension
+  Copyright 2012 Filipe Coelho <falktx@falktx.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 lv2_programs.h
+   C header for the LV2 programs extension <http://kxstudio.sf.net/ns/lv2ext/programs>.
+*/
+
+#ifndef LV2_PROGRAMS_H
+#define LV2_PROGRAMS_H
+
+#include "lv2.h"
+#include "ui.h"
+
+#define LV2_PROGRAMS_URI    "http://kxstudio.sf.net/ns/lv2ext/programs"
+#define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#"
+
+#define LV2_PROGRAMS__Host        LV2_PROGRAMS_PREFIX "Host"
+#define LV2_PROGRAMS__Interface   LV2_PROGRAMS_PREFIX "Interface"
+#define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void* LV2_Programs_Handle;
+
+typedef struct _LV2_Program_Descriptor {
+
+    /** Bank number for this program. Note that this extension does not
+        support MIDI-style separation of bank LSB and MSB values. There is
+        no restriction on the set of available banks: the numbers do not
+        need to be contiguous, there does not need to be a bank 0, etc. */
+    uint32_t bank;
+
+    /** Program number (unique within its bank) for this program. There is
+        no restriction on the set of available programs: the numbers do not
+        need to be contiguous, there does not need to be a program 0, etc. */
+    uint32_t program;
+
+    /** Name of the program. */
+    const char * name;
+
+} LV2_Program_Descriptor;
+
+/**
+   Programs extension, plugin data.
+
+   When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface,
+   the plugin MUST return an LV2_Programs_Instance structure, which remains valid
+   for the lifetime of the plugin.
+*/
+typedef struct _LV2_Programs_Interface {
+    /**
+     * get_program()
+     *
+     * This member is a function pointer that provides a description
+     * of a program (named preset sound) available on this plugin.
+     *
+     * The index argument is an index into the plugin's list of
+     * programs, not a program number as represented by the Program
+     * field of the LV2_Program_Descriptor. (This distinction is
+     * needed to support plugins that use non-contiguous program or
+     * bank numbers.)
+     *
+     * This function returns a LV2_Program_Descriptor pointer that is
+     * guaranteed to be valid only until the next call to get_program
+     * or deactivate, on the same plugin instance. This function must
+     * return NULL if passed an index argument out of range, so that
+     * the host can use it to query the number of programs as well as
+     * their properties.
+     */
+    const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle,
+                                                 uint32_t index);
+
+    /**
+     * select_program()
+     *
+     * This member is a function pointer that selects a new program
+     * for this plugin. The program change should take effect
+     * immediately at the start of the next run() call. (This
+     * means that a host providing the capability of changing programs
+     * between any two notes on a track must vary the block size so as
+     * to place the program change at the right place. A host that
+     * wanted to avoid this would probably just instantiate a plugin
+     * for each program.)
+     *
+     * Plugins should ignore a select_program() call with an invalid
+     * bank or program.
+     *
+     * A plugin is not required to select any particular default
+     * program on activate(): it's the host's duty to set a program
+     * explicitly.
+     *
+     * A plugin is permitted to re-write the values of its input
+     * control ports when select_program is called. The host should
+     * re-read the input control port values and update its own
+     * records appropriately. (This is the only circumstance in which
+     * a LV2 plugin is allowed to modify its own control-input ports.)
+     */
+    void (*select_program)(LV2_Handle handle,
+                           uint32_t bank,
+                           uint32_t program);
+
+} LV2_Programs_Interface;
+
+/**
+   Programs extension, UI data.
+
+   When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface,
+   the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid
+   for the lifetime of the UI.
+*/
+typedef struct _LV2_Programs_UI_Interface {
+    /**
+     * select_program()
+     *
+     * This is exactly the same as select_program in LV2_Programs_Instance,
+     * but this struct relates to the UI instead of the plugin.
+     *
+     * When called, UIs should update their state to match the selected program.
+     */
+    void (*select_program)(LV2UI_Handle handle,
+                           uint32_t bank,
+                           uint32_t program);
+
+} LV2_Programs_UI_Interface;
+
+/**
+    Feature data for LV2_PROGRAMS__Host.
+*/
+typedef struct _LV2_Programs_Host {
+    /**
+     *  Opaque host data.
+     */
+    LV2_Programs_Handle handle;
+
+    /**
+     * program_changed()
+     *
+     * Tell the host to reload a plugin's program.
+     * Parameter handle MUST be the 'handle' member of this struct.
+     * Parameter index is program index to change.
+     * When index is -1, host should reload all the programs.
+     *
+     * The plugin MUST NEVER call this function on a RT context or during run().
+     *
+     * NOTE: This call is to inform the host about a program's bank, program or name change.
+     *       It DOES NOT change the current selected program.
+     */
+    void (*program_changed)(LV2_Programs_Handle handle,
+                            int32_t index);
+
+} LV2_Programs_Host;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LV2_PROGRAMS_H */
diff --git a/debian/extra/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp b/debian/extra/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp
new file mode 100644 (file)
index 0000000..1c1f616
--- /dev/null
@@ -0,0 +1,2174 @@
+/*
+  ==============================================================================
+
+   Juce LV2 Wrapper
+
+  ==============================================================================
+*/
+
+// Your project must contain an AppConfig.h file with your project-specific settings in it,
+// and your header search path must make it accessible to the module's files.
+#include "AppConfig.h"
+
+#include "juce_audio_plugin_client/utility/juce_CheckSettingMacros.h"
+#include "juce_core/system/juce_TargetPlatform.h" // for JUCE_LINUX
+
+
+#if JucePlugin_Build_LV2
+
+/** Plugin requires processing with a fixed/constant block size */
+#ifndef JucePlugin_WantsLV2FixedBlockSize
+ #define JucePlugin_WantsLV2FixedBlockSize 0
+#endif
+
+/** Enable latency port */
+#ifndef JucePlugin_WantsLV2Latency
+ #define JucePlugin_WantsLV2Latency 1
+#endif
+
+/** Use non-parameter states */
+#ifndef JucePlugin_WantsLV2State
+ #define JucePlugin_WantsLV2State 1
+#endif
+
+/** States are strings, needs custom get/setStateInformationString */
+#ifndef JucePlugin_WantsLV2StateString
+ #define JucePlugin_WantsLV2StateString 0
+#endif
+
+/** Export presets */
+#ifndef JucePlugin_WantsLV2Presets
+ #define JucePlugin_WantsLV2Presets 1
+#endif
+
+/** Request time position */
+#ifndef JucePlugin_WantsLV2TimePos
+ #define JucePlugin_WantsLV2TimePos 1
+#endif
+
+/** Using string states require enabling states first */
+#if JucePlugin_WantsLV2StateString && ! JucePlugin_WantsLV2State
+ #undef JucePlugin_WantsLV2State
+ #define JucePlugin_WantsLV2State 1
+#endif
+
+#if JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI
+ #include <X11/Xlib.h>
+ #undef KeyPress
+#endif
+
+#include <fstream>
+#include <iostream>
+
+// LV2 includes..
+#include <lv2/lv2plug.in/ns/lv2core/lv2.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/buf-size/buf-size.h>
+#include <lv2/lv2plug.in/ns/ext/instance-access/instance-access.h>
+#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
+#include <lv2/lv2plug.in/ns/ext/options/options.h>
+#include <lv2/lv2plug.in/ns/ext/port-props/port-props.h>
+#include <lv2/lv2plug.in/ns/ext/presets/presets.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
+#include <lv2/lv2plug.in/ns/ext/time/time.h>
+#include <lv2/lv2plug.in/ns/extensions/ui/ui.h>
+#include <lv2/lv2plug.in/ns/ext/urid/urid.h>
+#include "includes/lv2_external_ui.h"
+#include "includes/lv2_programs.h"
+
+#include "../utility/juce_IncludeModuleHeaders.h"
+
+#define JUCE_LV2_STATE_STRING_URI "urn:juce:stateString"
+#define JUCE_LV2_STATE_BINARY_URI "urn:juce:stateBinary"
+
+//==============================================================================
+// Various helper functions for creating the ttl files
+
+#if JUCE_MAC
+ #define PLUGIN_EXT ".dylib"
+#elif JUCE_LINUX
+ #define PLUGIN_EXT ".so"
+#elif JUCE_WINDOWS
+ #define PLUGIN_EXT ".dll"
+#endif
+
+using namespace juce;
+
+/** Returns plugin type, defined in AppConfig.h or JucePluginCharacteristics.h */
+const String getPluginType()
+{
+    String pluginType;
+#ifdef JucePlugin_LV2Category
+    pluginType  = "lv2:" JucePlugin_LV2Category;
+    pluginType += ", ";
+#elif JucePlugin_IsSynth
+    pluginType  = "lv2:InstrumentPlugin, ";
+#endif
+    pluginType += "lv2:Plugin";
+    return pluginType;
+}
+
+/** Returns plugin URI */
+static const String& getPluginURI()
+{
+    // JucePlugin_LV2URI might be defined as a function (eg. allowing dynamic URIs based on filename)
+    static const String pluginURI(JucePlugin_LV2URI);
+    return pluginURI;
+}
+
+static Array<String> usedSymbols;
+
+/** Converts a parameter name to an LV2 compatible symbol. */
+const String nameToSymbol (const String& name, const uint32 portIndex)
+{
+    String symbol, trimmedName = name.trimStart().trimEnd().toLowerCase();
+
+    if (trimmedName.isEmpty())
+    {
+        symbol += "lv2_port_";
+        symbol += String(portIndex+1);
+    }
+    else
+    {
+        for (int i=0; i < trimmedName.length(); ++i)
+        {
+            const juce_wchar c = trimmedName[i];
+            if (i == 0 && std::isdigit(c))
+                symbol += "_";
+            else if (std::isalpha(c) || std::isdigit(c))
+                symbol += c;
+            else
+                symbol += "_";
+        }
+    }
+
+    // Do not allow identical symbols
+    if (usedSymbols.contains(symbol))
+    {
+        int offset = 2;
+        String offsetStr = "_2";
+        symbol += offsetStr;
+
+        while (usedSymbols.contains(symbol))
+        {
+            offset += 1;
+            String newOffsetStr = "_" + String(offset);
+            symbol = symbol.replace(offsetStr, newOffsetStr);
+            offsetStr = newOffsetStr;
+        }
+    }
+    usedSymbols.add(symbol);
+
+    return symbol;
+}
+
+/** Prevents NaN or out of 0.0<->1.0 bounds parameter values. */
+float safeParamValue (float value)
+{
+    if (std::isnan(value))
+        value = 0.0f;
+    else if (value < 0.0f)
+        value = 0.0f;
+    else if (value > 1.0f)
+        value = 1.0f;
+    return value;
+}
+
+/** Create the manifest.ttl file contents */
+const String makeManifestFile (AudioProcessor* const filter, const String& binary)
+{
+    const String& pluginURI(getPluginURI());
+    String text;
+
+    // Header
+    text += "@prefix lv2:  <" LV2_CORE_PREFIX "> .\n";
+    text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n";
+    text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
+    text += "@prefix ui:   <" LV2_UI_PREFIX "> .\n";
+    text += "\n";
+
+    // Plugin
+    text += "<" + pluginURI + ">\n";
+    text += "    a lv2:Plugin ;\n";
+    text += "    lv2:binary <" + binary + PLUGIN_EXT "> ;\n";
+    text += "    rdfs:seeAlso <" + binary + ".ttl> .\n";
+    text += "\n";
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+    // UIs
+    if (filter->hasEditor())
+    {
+        text += "<" + pluginURI + "#ExternalUI>\n";
+        text += "    a <" LV2_EXTERNAL_UI__Widget "> ;\n";
+        text += "    ui:binary <" + binary + PLUGIN_EXT "> ;\n";
+        text += "    lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
+        text += "    lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
+        text += "\n";
+
+        text += "<" + pluginURI + "#ParentUI>\n";
+ #if JUCE_MAC
+        text += "    a ui:CocoaUI ;\n";
+ #elif JUCE_LINUX
+        text += "    a ui:X11UI ;\n";
+ #elif JUCE_WINDOWS
+        text += "    a ui:WindowsUI ;\n";
+ #endif
+        text += "    ui:binary <" + binary + PLUGIN_EXT "> ;\n";
+        text += "    lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n";
+        text += "    lv2:optionalFeature ui:noUserResize ;\n";
+        text += "    lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n";
+        text += "\n";
+    }
+#endif
+
+#if JucePlugin_WantsLV2Presets
+    const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
+
+    // Presets
+    for (int i = 0; i < filter->getNumPrograms(); ++i)
+    {
+        text += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + ">\n";
+        text += "    a pset:Preset ;\n";
+        text += "    lv2:appliesTo <" + pluginURI + "> ;\n";
+        text += "    rdfs:label \"" + filter->getProgramName(i) + "\" ;\n";
+        text += "    rdfs:seeAlso <presets.ttl> .\n";
+        text += "\n";
+    }
+#endif
+
+    return text;
+}
+
+/** Create the -plugin-.ttl file contents */
+const String makePluginFile (AudioProcessor* const filter, const int maxNumInputChannels, const int maxNumOutputChannels)
+{
+    const String& pluginURI(getPluginURI());
+    String text;
+
+    // Header
+    text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
+    text += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
+    text += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
+    text += "@prefix lv2:  <" LV2_CORE_PREFIX "> .\n";
+    text += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
+    text += "@prefix ui:   <" LV2_UI_PREFIX "> .\n";
+    text += "\n";
+
+    // Plugin
+    text += "<" + pluginURI + ">\n";
+    text += "    a " + getPluginType() + " ;\n";
+    text += "    lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n";
+#if JucePlugin_WantsLV2FixedBlockSize
+    text += "                        <" LV2_BUF_SIZE__fixedBlockLength "> ,\n";
+#endif
+    text += "                        <" LV2_URID__map "> ;\n";
+    text += "    lv2:extensionData <" LV2_OPTIONS__interface "> ,\n";
+#if JucePlugin_WantsLV2State
+    text += "                      <" LV2_STATE__interface "> ,\n";
+#endif
+    text += "                      <" LV2_PROGRAMS__Interface "> ;\n";
+    text += "\n";
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+    // UIs
+    if (filter->hasEditor())
+    {
+        text += "    ui:ui <" + pluginURI + "#ExternalUI> ,\n";
+        text += "          <" + pluginURI + "#ParentUI> ;\n";
+        text += "\n";
+    }
+#endif
+
+    uint32 portIndex = 0;
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+    // MIDI input
+    text += "    lv2:port [\n";
+    text += "        a lv2:InputPort, atom:AtomPort ;\n";
+    text += "        atom:bufferType atom:Sequence ;\n";
+ #if JucePlugin_WantsMidiInput
+    text += "        atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
+ #endif
+ #if JucePlugin_WantsLV2TimePos
+    text += "        atom:supports <" LV2_TIME__Position "> ;\n";
+ #endif
+    text += "        lv2:index " + String(portIndex++) + " ;\n";
+    text += "        lv2:symbol \"lv2_events_in\" ;\n";
+    text += "        lv2:name \"Events Input\" ;\n";
+    text += "        lv2:designation lv2:control ;\n";
+ #if ! JucePlugin_IsSynth
+    text += "        lv2:portProperty lv2:connectionOptional ;\n";
+ #endif
+    text += "    ] ;\n";
+    text += "\n";
+#endif
+
+#if JucePlugin_ProducesMidiOutput
+    // MIDI output
+    text += "    lv2:port [\n";
+    text += "        a lv2:OutputPort, atom:AtomPort ;\n";
+    text += "        atom:bufferType atom:Sequence ;\n";
+    text += "        atom:supports <" LV2_MIDI__MidiEvent "> ;\n";
+    text += "        lv2:index " + String(portIndex++) + " ;\n";
+    text += "        lv2:symbol \"lv2_midi_out\" ;\n";
+    text += "        lv2:name \"MIDI Output\" ;\n";
+    text += "    ] ;\n";
+    text += "\n";
+#endif
+
+    // Freewheel port
+    text += "    lv2:port [\n";
+    text += "        a lv2:InputPort, lv2:ControlPort ;\n";
+    text += "        lv2:index " + String(portIndex++) + " ;\n";
+    text += "        lv2:symbol \"lv2_freewheel\" ;\n";
+    text += "        lv2:name \"Freewheel\" ;\n";
+    text += "        lv2:default 0.0 ;\n";
+    text += "        lv2:minimum 0.0 ;\n";
+    text += "        lv2:maximum 1.0 ;\n";
+    text += "        lv2:designation <" LV2_CORE__freeWheeling "> ;\n";
+    text += "        lv2:portProperty lv2:toggled, <" LV2_PORT_PROPS__notOnGUI "> ;\n";
+    text += "    ] ;\n";
+    text += "\n";
+
+#if JucePlugin_WantsLV2Latency
+    // Latency port
+    text += "    lv2:port [\n";
+    text += "        a lv2:OutputPort, lv2:ControlPort ;\n";
+    text += "        lv2:index " + String(portIndex++) + " ;\n";
+    text += "        lv2:symbol \"lv2_latency\" ;\n";
+    text += "        lv2:name \"Latency\" ;\n";
+    text += "        lv2:designation <" LV2_CORE__latency "> ;\n";
+    text += "        lv2:portProperty lv2:reportsLatency, lv2:integer ;\n";
+    text += "    ] ;\n";
+    text += "\n";
+#endif
+
+    // Audio inputs
+    for (int i=0; i < maxNumInputChannels; ++i)
+    {
+        if (i == 0)
+            text += "    lv2:port [\n";
+        else
+            text += "    [\n";
+
+        text += "        a lv2:InputPort, lv2:AudioPort ;\n";
+        text += "        lv2:index " + String(portIndex++) + " ;\n";
+        text += "        lv2:symbol \"lv2_audio_in_" + String(i+1) + "\" ;\n";
+        text += "        lv2:name \"Audio Input " + String(i+1) + "\" ;\n";
+
+        if (i+1 == maxNumInputChannels)
+            text += "    ] ;\n\n";
+        else
+            text += "    ] ,\n";
+    }
+
+    // Audio outputs
+    for (int i=0; i < maxNumOutputChannels; ++i)
+    {
+        if (i == 0)
+            text += "    lv2:port [\n";
+        else
+            text += "    [\n";
+
+        text += "        a lv2:OutputPort, lv2:AudioPort ;\n";
+        text += "        lv2:index " + String(portIndex++) + " ;\n";
+        text += "        lv2:symbol \"lv2_audio_out_" + String(i+1) + "\" ;\n";
+        text += "        lv2:name \"Audio Output " + String(i+1) + "\" ;\n";
+
+        if (i+1 == maxNumOutputChannels)
+            text += "    ] ;\n\n";
+        else
+            text += "    ] ,\n";
+    }
+
+    // Parameters
+    for (int i=0; i < filter->getNumParameters(); ++i)
+    {
+        if (i == 0)
+            text += "    lv2:port [\n";
+        else
+            text += "    [\n";
+
+        text += "        a lv2:InputPort, lv2:ControlPort ;\n";
+        text += "        lv2:index " + String(portIndex++) + " ;\n";
+        text += "        lv2:symbol \"" + nameToSymbol(filter->getParameterName(i), i) + "\" ;\n";
+
+        if (filter->getParameterName(i).isNotEmpty())
+            text += "        lv2:name \"" + filter->getParameterName(i) + "\" ;\n";
+        else
+            text += "        lv2:name \"Port " + String(i+1) + "\" ;\n";
+
+        text += "        lv2:default " + String::formatted("%f", safeParamValue(filter->getParameter(i))) + " ;\n";
+        text += "        lv2:minimum 0.0 ;\n";
+        text += "        lv2:maximum 1.0 ;\n";
+
+        if (! filter->isParameterAutomatable(i))
+            text += "        lv2:portProperty <" LV2_PORT_PROPS__expensive "> ;\n";
+
+        if (i+1 == filter->getNumParameters())
+            text += "    ] ;\n\n";
+        else
+            text += "    ] ,\n";
+    }
+
+    text += "    doap:name \"" + filter->getName() + "\" ;\n";
+    text += "    doap:maintainer [ foaf:name \"" JucePlugin_Manufacturer "\" ] .\n";
+
+    return text;
+}
+
+/** Create the presets.ttl file contents */
+const String makePresetsFile (AudioProcessor* const filter)
+{
+    const String& pluginURI(getPluginURI());
+    String text;
+
+    // Header
+    text += "@prefix atom:  <" LV2_ATOM_PREFIX "> .\n";
+    text += "@prefix lv2:   <" LV2_CORE_PREFIX "> .\n";
+    text += "@prefix pset:  <" LV2_PRESETS_PREFIX "> .\n";
+    text += "@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n";
+    text += "@prefix rdfs:  <http://www.w3.org/2000/01/rdf-schema#> .\n";
+    text += "@prefix state: <" LV2_STATE_PREFIX "> .\n";
+    text += "@prefix xsd:   <http://www.w3.org/2001/XMLSchema#> .\n";
+    text += "\n";
+
+    // Presets
+    const int numPrograms = filter->getNumPrograms();
+    const String presetSeparator(pluginURI.contains("#") ? ":" : "#");
+
+    for (int i = 0; i < numPrograms; ++i)
+    {
+        std::cout << "\nSaving preset " << i+1 << "/" << numPrograms+1 << "...";
+        std::cout.flush();
+
+        String preset;
+
+        // Label
+        filter->setCurrentProgram(i);
+        preset += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + "> a pset:Preset ;\n";
+
+        // State
+#if JucePlugin_WantsLV2State
+        preset += "    state:state [\n";
+ #if JucePlugin_WantsLV2StateString
+        preset += "        <" JUCE_LV2_STATE_STRING_URI ">\n";
+        preset += "\"\"\"\n";
+        preset += filter->getStateInformationString().replace("\r\n","\n");
+        preset += "\"\"\"\n";
+ #else
+        MemoryBlock chunkMemory;
+        filter->getCurrentProgramStateInformation(chunkMemory);
+        const String chunkString(Base64::toBase64(chunkMemory.getData(), chunkMemory.getSize()));
+
+        preset += "        <" JUCE_LV2_STATE_BINARY_URI "> [\n";
+        preset += "            a atom:Chunk ;\n";
+        preset += "            rdf:value \"" + chunkString + "\"^^xsd:base64Binary ;\n";
+        preset += "        ] ;\n";
+ #endif
+        if (filter->getNumParameters() == 0)
+        {
+            preset += "    ] .\n\n";
+            continue;
+        }
+
+        preset += "    ] ;\n\n";
+#endif
+
+        // Port values
+        usedSymbols.clear();
+
+        for (int j=0; j < filter->getNumParameters(); ++j)
+        {
+              if (j == 0)
+                preset += "    lv2:port [\n";
+            else
+                preset += "    [\n";
+
+            preset += "        lv2:symbol \"" + nameToSymbol(filter->getParameterName(j), j) + "\" ;\n";
+            preset += "        pset:value " + String::formatted("%f", safeParamValue(filter->getParameter(j))) + " ;\n";
+
+            if (j+1 == filter->getNumParameters())
+                preset += "    ] ";
+            else
+                preset += "    ] ,\n";
+        }
+        preset += ".\n\n";
+
+        text += preset;
+    }
+
+    return text;
+}
+
+/** Creates manifest.ttl, plugin.ttl and presets.ttl files */
+void createLv2Files(const char* basename)
+{
+    const ScopedJuceInitialiser_GUI juceInitialiser;
+    ScopedPointer<AudioProcessor> filter (createPluginFilterOfType (AudioProcessor::wrapperType_LV2));
+
+    String binary(basename);
+    String binaryTTL(binary + ".ttl");
+
+    std::cout << "Writing manifest.ttl..."; std::cout.flush();
+    std::fstream manifest("manifest.ttl", std::ios::out);
+    manifest << makeManifestFile(filter, binary) << std::endl;
+    manifest.close();
+    std::cout << " done!" << std::endl;
+
+    std::cout << "Writing " << binary << ".ttl..."; std::cout.flush();
+    std::fstream plugin(binaryTTL.toUTF8(), std::ios::out);
+    plugin << makePluginFile(filter, JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) << std::endl;
+    plugin.close();
+    std::cout << " done!" << std::endl;
+
+#if JucePlugin_WantsLV2Presets
+    std::cout << "Writing presets.ttl..."; std::cout.flush();
+    std::fstream presets("presets.ttl", std::ios::out);
+    presets << makePresetsFile(filter) << std::endl;
+    presets.close();
+    std::cout << " done!" << std::endl;
+#endif
+}
+
+//==============================================================================
+#if JUCE_LINUX
+
+class SharedMessageThread : public Thread
+{
+public:
+    SharedMessageThread()
+      : Thread ("Lv2MessageThread"),
+        initialised (false)
+    {
+        startThread (7);
+
+        while (! initialised)
+            sleep (1);
+    }
+
+    ~SharedMessageThread()
+    {
+        MessageManager::getInstance()->stopDispatchLoop();
+        waitForThreadToExit (5000);
+    }
+
+    void run() override
+    {
+        const ScopedJuceInitialiser_GUI juceInitialiser;
+
+        MessageManager::getInstance()->setCurrentThreadAsMessageThread();
+        initialised = true;
+
+        MessageManager::getInstance()->runDispatchLoop();
+    }
+
+private:
+    volatile bool initialised;
+};
+#endif
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+//==============================================================================
+/**
+    Lightweight DocumentWindow subclass for external ui
+*/
+class JuceLv2ExternalUIWindow : public DocumentWindow
+{
+public:
+    /** Creates a Document Window wrapper */
+    JuceLv2ExternalUIWindow (AudioProcessorEditor* editor, const String& title) :
+            DocumentWindow (title, Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, false),
+            closed (false),
+            lastPos (0, 0)
+    {
+        setOpaque (true);
+        setContentNonOwned (editor, true);
+        setSize (editor->getWidth(), editor->getHeight());
+        setUsingNativeTitleBar (true);
+    }
+
+    /** Close button handler */
+    void closeButtonPressed()
+    {
+        saveLastPos();
+        removeFromDesktop();
+        closed = true;
+    }
+
+    void saveLastPos()
+    {
+        lastPos = getScreenPosition();
+    }
+
+    void restoreLastPos()
+    {
+        setTopLeftPosition (lastPos.getX(), lastPos.getY());
+    }
+
+    Point<int> getLastPos()
+    {
+        return lastPos;
+    }
+
+    bool isClosed()
+    {
+        return closed;
+    }
+
+    void reset()
+    {
+        closed = false;
+    }
+
+private:
+    bool closed;
+    Point<int> lastPos;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWindow);
+};
+
+//==============================================================================
+/**
+    Juce LV2 External UI handle
+*/
+class JuceLv2ExternalUIWrapper : public LV2_External_UI_Widget
+{
+public:
+    JuceLv2ExternalUIWrapper (AudioProcessorEditor* editor, const String& title)
+        : window (editor, title)
+    {
+        // external UI calls
+        run  = doRun;
+        show = doShow;
+        hide = doHide;
+    }
+
+    ~JuceLv2ExternalUIWrapper()
+    {
+        if (window.isOnDesktop())
+            window.removeFromDesktop();
+    }
+
+    void close()
+    {
+        window.closeButtonPressed();
+    }
+
+    bool isClosed()
+    {
+        return window.isClosed();
+    }
+
+    void reset(const String& title)
+    {
+        window.reset();
+        window.setName(title);
+    }
+
+    void repaint()
+    {
+        window.repaint();
+    }
+
+    Point<int> getScreenPosition()
+    {
+        if (window.isClosed())
+            return window.getLastPos();
+        else
+            return window.getScreenPosition();
+    }
+
+    void setScreenPos (int x, int y)
+    {
+        if (! window.isClosed())
+            window.setTopLeftPosition(x, y);
+    }
+
+    //==============================================================================
+    static void doRun (LV2_External_UI_Widget* _this_)
+    {
+        const MessageManagerLock mmLock;
+        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
+
+        if (! self->isClosed())
+            self->window.repaint();
+    }
+
+    static void doShow (LV2_External_UI_Widget* _this_)
+    {
+        const MessageManagerLock mmLock;
+        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
+
+        if (! self->isClosed())
+        {
+            if (! self->window.isOnDesktop())
+                self->window.addToDesktop();
+
+            self->window.restoreLastPos();
+            self->window.setVisible(true);
+        }
+    }
+
+    static void doHide (LV2_External_UI_Widget* _this_)
+    {
+        const MessageManagerLock mmLock;
+        JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_;
+
+        if (! self->isClosed())
+        {
+            self->window.saveLastPos();
+            self->window.setVisible(false);
+        }
+    }
+
+private:
+    JuceLv2ExternalUIWindow window;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWrapper);
+};
+
+//==============================================================================
+/**
+    Juce LV2 Parent UI container, listens for resize events and passes them to ui-resize
+*/
+class JuceLv2ParentContainer : public Component
+{
+public:
+    JuceLv2ParentContainer (AudioProcessorEditor* editor, const LV2UI_Resize* uiResize_)
+        : uiResize(uiResize_)
+    {
+        setOpaque (true);
+        editor->setOpaque (true);
+        setBounds (editor->getBounds());
+
+        editor->setTopLeftPosition (0, 0);
+        addAndMakeVisible (editor);
+    }
+
+    ~JuceLv2ParentContainer()
+    {
+    }
+
+    void paint (Graphics&) {}
+    void paintOverChildren (Graphics&) {}
+
+    void childBoundsChanged (Component* child)
+    {
+        const int cw = child->getWidth();
+        const int ch = child->getHeight();
+
+#if JUCE_LINUX
+        XResizeWindow (display.display, (Window) getWindowHandle(), cw, ch);
+#else
+        setSize (cw, ch);
+#endif
+
+        if (uiResize != nullptr)
+            uiResize->ui_resize (uiResize->handle, cw, ch);
+    }
+
+    void reset (const LV2UI_Resize* uiResize_)
+    {
+        uiResize = uiResize_;
+
+        if (uiResize != nullptr)
+            uiResize->ui_resize (uiResize->handle, getWidth(), getHeight());
+    }
+
+private:
+    //==============================================================================
+    const LV2UI_Resize* uiResize;
+#if JUCE_LINUX
+    ScopedXDisplay display;
+#endif
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ParentContainer);
+};
+
+//==============================================================================
+/**
+    Juce LV2 UI handle
+*/
+class JuceLv2UIWrapper : public AudioProcessorListener,
+                         public Timer
+{
+public:
+    JuceLv2UIWrapper (AudioProcessor* filter_, LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_,
+                      LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal_)
+        : filter (filter_),
+          writeFunction (writeFunction_),
+          controller (controller_),
+          isExternal (isExternal_),
+          controlPortOffset (0),
+          lastProgramCount (0),
+          uiTouch (nullptr),
+          programsHost (nullptr),
+          externalUIHost (nullptr),
+          lastExternalUIPos (-1, -1),
+          uiResize (nullptr)
+    {
+        jassert (filter != nullptr);
+
+        filter->addListener(this);
+
+        if (filter->hasEditor())
+        {
+            editor = filter->createEditorIfNeeded();
+
+            if (editor == nullptr)
+            {
+                *widget = nullptr;
+                return;
+            }
+        }
+
+        for (int i = 0; features[i] != nullptr; ++i)
+        {
+            if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
+                uiTouch = (const LV2UI_Touch*)features[i]->data;
+
+            else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
+                programsHost = (const LV2_Programs_Host*)features[i]->data;
+        }
+
+        if (isExternal)
+        {
+            resetExternalUI (features);
+
+            if (externalUIHost != nullptr)
+            {
+                String title (filter->getName());
+
+                if (externalUIHost->plugin_human_id != nullptr)
+                    title = externalUIHost->plugin_human_id;
+
+                externalUI = new JuceLv2ExternalUIWrapper (editor, title);
+                *widget = externalUI;
+                startTimer (100);
+            }
+            else
+            {
+                *widget = nullptr;
+            }
+        }
+        else
+        {
+            resetParentUI (features);
+
+            if (parentContainer != nullptr)
+               *widget = parentContainer->getWindowHandle();
+            else
+               *widget = nullptr;
+        }
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+        controlPortOffset += 1;
+#endif
+#if JucePlugin_ProducesMidiOutput
+        controlPortOffset += 1;
+#endif
+        controlPortOffset += 1; // freewheel
+#if JucePlugin_WantsLV2Latency
+        controlPortOffset += 1;
+#endif
+        controlPortOffset += JucePlugin_MaxNumInputChannels;
+        controlPortOffset += JucePlugin_MaxNumOutputChannels;
+
+        lastProgramCount = filter->getNumPrograms();
+    }
+
+    ~JuceLv2UIWrapper()
+    {
+        PopupMenu::dismissAllActiveMenus();
+
+        filter->removeListener(this);
+
+        parentContainer = nullptr;
+        externalUI = nullptr;
+        externalUIHost = nullptr;
+
+        if (editor != nullptr)
+        {
+            filter->editorBeingDeleted (editor);
+            editor = nullptr;
+        }
+    }
+
+    //==============================================================================
+    // LV2 core calls
+
+    void lv2Cleanup()
+    {
+        const MessageManagerLock mmLock;
+
+        if (isExternal)
+        {
+            if (isTimerRunning())
+                stopTimer();
+
+            externalUIHost = nullptr;
+
+            if (externalUI != nullptr)
+            {
+                lastExternalUIPos = externalUI->getScreenPosition();
+                externalUI->close();
+            }
+        }
+        else
+        {
+            if (parentContainer)
+            {
+                parentContainer->setVisible (false);
+                if (parentContainer->isOnDesktop())
+                    parentContainer->removeFromDesktop();
+            }
+        }
+    }
+
+    //==============================================================================
+    // Juce calls
+
+    void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue)
+    {
+        if (writeFunction != nullptr && controller != nullptr)
+            writeFunction (controller, index + controlPortOffset, sizeof (float), 0, &newValue);
+    }
+
+    void audioProcessorChanged (AudioProcessor*)
+    {
+        if (filter != nullptr && programsHost != nullptr)
+        {
+            if (filter->getNumPrograms() != lastProgramCount)
+            {
+                programsHost->program_changed (programsHost->handle, -1);
+                lastProgramCount = filter->getNumPrograms();
+            }
+            else
+                programsHost->program_changed (programsHost->handle, filter->getCurrentProgram());
+        }
+    }
+
+    void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex)
+    {
+        if (uiTouch != nullptr)
+            uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, true);
+    }
+
+    void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int parameterIndex)
+    {
+        if (uiTouch != nullptr)
+            uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, false);
+    }
+
+    void timerCallback()
+    {
+        if (externalUI != nullptr && externalUI->isClosed())
+        {
+            if (externalUIHost != nullptr)
+                externalUIHost->ui_closed (controller);
+
+            if (isTimerRunning())
+                stopTimer();
+        }
+    }
+
+    //==============================================================================
+    void resetIfNeeded (LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_, LV2UI_Widget* widget,
+                        const LV2_Feature* const* features)
+    {
+        writeFunction = writeFunction_;
+        controller = controller_;
+        uiTouch = nullptr;
+        programsHost = nullptr;
+
+        for (int i = 0; features[i] != nullptr; ++i)
+        {
+            if (strcmp(features[i]->URI, LV2_UI__touch) == 0)
+                uiTouch = (const LV2UI_Touch*)features[i]->data;
+
+            else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0)
+                programsHost = (const LV2_Programs_Host*)features[i]->data;
+        }
+
+        if (isExternal)
+        {
+            resetExternalUI (features);
+            *widget = externalUI;
+        }
+        else
+        {
+            resetParentUI (features);
+            *widget = parentContainer->getWindowHandle();
+        }
+    }
+
+    void repaint()
+    {
+        const MessageManagerLock mmLock;
+
+        if (editor != nullptr)
+            editor->repaint();
+
+        if (parentContainer != nullptr)
+            parentContainer->repaint();
+
+        if (externalUI != nullptr)
+            externalUI->repaint();
+    }
+
+private:
+    AudioProcessor* const filter;
+    ScopedPointer<AudioProcessorEditor> editor;
+
+    LV2UI_Write_Function writeFunction;
+    LV2UI_Controller controller;
+    const bool isExternal;
+
+    uint32 controlPortOffset;
+    int lastProgramCount;
+
+    const LV2UI_Touch* uiTouch;
+    const LV2_Programs_Host* programsHost;
+
+    ScopedPointer<JuceLv2ExternalUIWrapper> externalUI;
+    const LV2_External_UI_Host* externalUIHost;
+    Point<int> lastExternalUIPos;
+
+    ScopedPointer<JuceLv2ParentContainer> parentContainer;
+    const LV2UI_Resize* uiResize;
+
+#if JUCE_LINUX
+    ScopedXDisplay display;
+#endif
+
+    //==============================================================================
+    void resetExternalUI (const LV2_Feature* const* features)
+    {
+        externalUIHost = nullptr;
+
+        for (int i = 0; features[i] != nullptr; ++i)
+        {
+            if (strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0)
+            {
+                externalUIHost = (const LV2_External_UI_Host*)features[i]->data;
+                break;
+            }
+        }
+
+        if (externalUI != nullptr)
+        {
+            String title(filter->getName());
+
+            if (externalUIHost->plugin_human_id != nullptr)
+                title = externalUIHost->plugin_human_id;
+
+            if (lastExternalUIPos.getX() != -1 && lastExternalUIPos.getY() != -1)
+                externalUI->setScreenPos(lastExternalUIPos.getX(), lastExternalUIPos.getY());
+
+            externalUI->reset(title);
+            startTimer (100);
+        }
+    }
+
+    void resetParentUI (const LV2_Feature* const* features)
+    {
+        void* parent = nullptr;
+        uiResize = nullptr;
+
+        for (int i = 0; features[i] != nullptr; ++i)
+        {
+            if (strcmp(features[i]->URI, LV2_UI__parent) == 0)
+                parent = features[i]->data;
+
+            else if (strcmp(features[i]->URI, LV2_UI__resize) == 0)
+                uiResize = (const LV2UI_Resize*)features[i]->data;
+        }
+
+        if (parent != nullptr)
+        {
+            if (parentContainer == nullptr)
+                parentContainer = new JuceLv2ParentContainer (editor, uiResize);
+
+            parentContainer->setVisible (false);
+
+            if (parentContainer->isOnDesktop())
+                parentContainer->removeFromDesktop();
+
+            parentContainer->addToDesktop (0, parent);
+
+#if JUCE_LINUX
+            Window hostWindow = (Window) parent;
+            Window editorWnd  = (Window) parentContainer->getWindowHandle();
+            XReparentWindow (display.display, editorWnd, hostWindow, 0, 0);
+#endif
+
+            parentContainer->reset (uiResize);
+            parentContainer->setVisible (true);
+        }
+    }
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2UIWrapper)
+};
+
+#endif /* JUCE_AUDIOPROCESSOR_NO_GUI */
+
+//==============================================================================
+/**
+    Juce LV2 handle
+*/
+class JuceLv2Wrapper : public AudioPlayHead
+{
+public:
+    //==============================================================================
+    JuceLv2Wrapper (double sampleRate_, const LV2_Feature* const* features)
+        : numInChans (JucePlugin_MaxNumInputChannels),
+          numOutChans (JucePlugin_MaxNumOutputChannels),
+          bufferSize (2048),
+          sampleRate (sampleRate_),
+          uridMap (nullptr),
+          uridAtomBlank (0),
+          uridAtomObject (0),
+          uridAtomDouble (0),
+          uridAtomFloat (0),
+          uridAtomInt (0),
+          uridAtomLong (0),
+          uridAtomSequence (0),
+          uridMidiEvent (0),
+          uridTimePos (0),
+          uridTimeBar (0),
+          uridTimeBarBeat (0),
+          uridTimeBeatsPerBar (0),
+          uridTimeBeatsPerMinute (0),
+          uridTimeBeatUnit (0),
+          uridTimeFrame (0),
+          uridTimeSpeed (0),
+          usingNominalBlockLength (false)
+    {
+        {
+            const MessageManagerLock mmLock;
+            filter = createPluginFilterOfType (AudioProcessor::wrapperType_LV2);
+        }
+        jassert (filter != nullptr);
+
+        filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0);
+        filter->setPlayHead (this);
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+        portEventsIn = nullptr;
+#endif
+#if JucePlugin_ProducesMidiOutput
+        portMidiOut = nullptr;
+#endif
+
+        portFreewheel = nullptr;
+
+#if JucePlugin_WantsLV2Latency
+        portLatency = nullptr;
+#endif
+
+        for (int i=0; i < numInChans; ++i)
+            portAudioIns[i] = nullptr;
+        for (int i=0; i < numOutChans; ++i)
+            portAudioOuts[i] = nullptr;
+
+        portControls.insertMultiple (0, nullptr, filter->getNumParameters());
+
+        for (int i=0; i < filter->getNumParameters(); ++i)
+            lastControlValues.add (filter->getParameter(i));
+
+        curPosInfo.resetToDefault();
+
+        // we need URID_Map first
+        for (int i=0; features[i] != nullptr; ++i)
+        {
+            if (strcmp(features[i]->URI, LV2_URID__map) == 0)
+            {
+                uridMap = (const LV2_URID_Map*)features[i]->data;
+                break;
+            }
+        }
+
+        // we require uridMap to work properly (it's set as required feature)
+        jassert (uridMap != nullptr);
+
+        if (uridMap != nullptr)
+        {
+            uridAtomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank);
+            uridAtomObject = uridMap->map(uridMap->handle, LV2_ATOM__Object);
+            uridAtomDouble = uridMap->map(uridMap->handle, LV2_ATOM__Double);
+            uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
+            uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
+            uridAtomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long);
+            uridAtomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence);
+            uridMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent);
+            uridTimePos = uridMap->map(uridMap->handle, LV2_TIME__Position);
+            uridTimeBar = uridMap->map(uridMap->handle, LV2_TIME__bar);
+            uridTimeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat);
+            uridTimeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar);
+            uridTimeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute);
+            uridTimeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit);
+            uridTimeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame);
+            uridTimeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed);
+
+            for (int i=0; features[i] != nullptr; ++i)
+            {
+                if (strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
+                {
+                    const LV2_Options_Option* options = (const LV2_Options_Option*)features[i]->data;
+
+                    for (int j=0; options[j].key != 0; ++j)
+                    {
+                        if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
+                        {
+                            if (options[j].type == uridAtomInt)
+                            {
+                                bufferSize = *(int*)options[j].value;
+                                usingNominalBlockLength = true;
+                            }
+                            else
+                            {
+                                std::cerr << "Host provides nominalBlockLength but has wrong value type" << std::endl;
+                            }
+                            break;
+                        }
+
+                        if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
+                        {
+                            if (options[j].type == uridAtomInt)
+                                bufferSize = *(int*)options[j].value;
+                            else
+                                std::cerr << "Host provides maxBlockLength but has wrong value type" << std::endl;
+
+                            // no break, continue in case host supports nominalBlockLength
+                        }
+                    }
+                    break;
+                }
+            }
+        }
+
+        progDesc.bank = 0;
+        progDesc.program = 0;
+        progDesc.name = nullptr;
+    }
+
+    ~JuceLv2Wrapper ()
+    {
+        const MessageManagerLock mmLock;
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+        ui = nullptr;
+#endif
+        filter = nullptr;
+
+        if (progDesc.name != nullptr)
+            free((void*)progDesc.name);
+
+        portControls.clear();
+        lastControlValues.clear();
+    }
+
+    //==============================================================================
+    // LV2 core calls
+
+    void lv2ConnectPort (uint32 portId, void* dataLocation)
+    {
+        uint32 index = 0;
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+        if (portId == index++)
+        {
+            portEventsIn = (LV2_Atom_Sequence*)dataLocation;
+            return;
+        }
+#endif
+
+#if JucePlugin_ProducesMidiOutput
+        if (portId == index++)
+        {
+            portMidiOut = (LV2_Atom_Sequence*)dataLocation;
+            return;
+        }
+#endif
+
+        if (portId == index++)
+        {
+            portFreewheel = (float*)dataLocation;
+            return;
+        }
+
+#if JucePlugin_WantsLV2Latency
+        if (portId == index++)
+        {
+            portLatency = (float*)dataLocation;
+            return;
+        }
+#endif
+
+        for (int i=0; i < numInChans; ++i)
+        {
+            if (portId == index++)
+            {
+                portAudioIns[i] = (float*)dataLocation;
+                return;
+            }
+        }
+
+        for (int i=0; i < numOutChans; ++i)
+        {
+            if (portId == index++)
+            {
+                portAudioOuts[i] = (float*)dataLocation;
+                return;
+            }
+        }
+
+        for (int i=0; i < filter->getNumParameters(); ++i)
+        {
+            if (portId == index++)
+            {
+                portControls.set(i, (float*)dataLocation);
+                return;
+            }
+        }
+    }
+
+    void lv2Activate()
+    {
+        jassert (filter != nullptr);
+
+        filter->prepareToPlay (sampleRate, bufferSize);
+        filter->setPlayConfigDetails (numInChans, numOutChans, sampleRate, bufferSize);
+
+        channels.calloc (numInChans + numOutChans);
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_ProducesMidiOutput)
+        midiEvents.ensureSize (2048);
+        midiEvents.clear();
+#endif
+    }
+
+    void lv2Deactivate()
+    {
+        jassert (filter != nullptr);
+
+        filter->releaseResources();
+
+        channels.free();
+    }
+
+    void lv2Run (uint32 sampleCount)
+    {
+        jassert (filter != nullptr);
+
+#if JucePlugin_WantsLV2Latency
+        if (portLatency != nullptr)
+            *portLatency = filter->getLatencySamples();
+#endif
+
+        if (portFreewheel != nullptr)
+            filter->setNonRealtime (*portFreewheel >= 0.5f);
+
+        if (sampleCount == 0)
+        {
+            /**
+               LV2 pre-roll
+               Hosts might use this to force plugins to update its output control ports.
+               (plugins can only access port locations during run) */
+            return;
+        }
+
+        // Check for updated parameters
+        {
+            float curValue;
+
+            for (int i = 0; i < portControls.size(); ++i)
+            {
+                if (portControls[i] != nullptr)
+                {
+                    curValue = *portControls[i];
+
+                    if (lastControlValues[i] != curValue)
+                    {
+                        filter->setParameter (i, curValue);
+                        lastControlValues.setUnchecked (i, curValue);
+                    }
+                }
+            }
+        }
+
+        {
+            const ScopedLock sl (filter->getCallbackLock());
+
+            if (filter->isSuspended() && false)
+            {
+                for (int i = 0; i < numOutChans; ++i)
+                    zeromem (portAudioOuts[i], sizeof (float) * sampleCount);
+            }
+            else
+            {
+                int i;
+                for (i = 0; i < numOutChans; ++i)
+                {
+                    channels[i] = portAudioOuts[i];
+
+                    if (i < numInChans && portAudioIns[i] != portAudioOuts[i])
+                        FloatVectorOperations::copy (portAudioOuts [i], portAudioIns[i], sampleCount);
+                }
+
+                for (; i < numInChans; ++i)
+                    channels [i] = portAudioIns[i];
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+                if (portEventsIn != nullptr)
+                {
+                    midiEvents.clear();
+
+                    LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter)
+                    {
+                        const LV2_Atom_Event* event = (const LV2_Atom_Event*)iter;
+
+                        if (event == nullptr)
+                            continue;
+                        if (event->time.frames >= sampleCount)
+                            break;
+
+ #if JucePlugin_WantsMidiInput
+                        if (event->body.type == uridMidiEvent)
+                        {
+                            const uint8* data = (const uint8*)(event + 1);
+                            midiEvents.addEvent(data, event->body.size, event->time.frames);
+                            continue;
+                        }
+ #endif
+ #if JucePlugin_WantsLV2TimePos
+                        if (event->body.type == uridAtomBlank || event->body.type == uridAtomObject)
+                        {
+                            const LV2_Atom_Object* obj = (LV2_Atom_Object*)&event->body;
+
+                            if (obj->body.otype != uridTimePos)
+                                continue;
+
+                            LV2_Atom* bar = nullptr;
+                            LV2_Atom* barBeat = nullptr;
+                            LV2_Atom* beatUnit = nullptr;
+                            LV2_Atom* beatsPerBar = nullptr;
+                            LV2_Atom* beatsPerMinute = nullptr;
+                            LV2_Atom* frame = nullptr;
+                            LV2_Atom* speed = nullptr;
+
+                            lv2_atom_object_get (obj,
+                                                 uridTimeBar, &bar,
+                                                 uridTimeBarBeat, &barBeat,
+                                                 uridTimeBeatUnit, &beatUnit,
+                                                 uridTimeBeatsPerBar, &beatsPerBar,
+                                                 uridTimeBeatsPerMinute, &beatsPerMinute,
+                                                 uridTimeFrame, &frame,
+                                                 uridTimeSpeed, &speed,
+                                                 nullptr);
+
+                            // need to handle this first as other values depend on it
+                            if (speed != nullptr)
+                            {
+                                /**/ if (speed->type == uridAtomDouble)
+                                    lastPositionData.speed = ((LV2_Atom_Double*)speed)->body;
+                                else if (speed->type == uridAtomFloat)
+                                    lastPositionData.speed = ((LV2_Atom_Float*)speed)->body;
+                                else if (speed->type == uridAtomInt)
+                                    lastPositionData.speed = ((LV2_Atom_Int*)speed)->body;
+                                else if (speed->type == uridAtomLong)
+                                    lastPositionData.speed = ((LV2_Atom_Long*)speed)->body;
+
+                                curPosInfo.isPlaying = lastPositionData.speed != 0.0;
+                            }
+
+                            if (bar != nullptr)
+                            {
+                                /**/ if (bar->type == uridAtomDouble)
+                                    lastPositionData.bar = ((LV2_Atom_Double*)bar)->body;
+                                else if (bar->type == uridAtomFloat)
+                                    lastPositionData.bar = ((LV2_Atom_Float*)bar)->body;
+                                else if (bar->type == uridAtomInt)
+                                    lastPositionData.bar = ((LV2_Atom_Int*)bar)->body;
+                                else if (bar->type == uridAtomLong)
+                                    lastPositionData.bar = ((LV2_Atom_Long*)bar)->body;
+                            }
+
+                            if (barBeat != nullptr)
+                            {
+                                /**/ if (barBeat->type == uridAtomDouble)
+                                    lastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body;
+                                else if (barBeat->type == uridAtomFloat)
+                                    lastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body;
+                                else if (barBeat->type == uridAtomInt)
+                                    lastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body;
+                                else if (barBeat->type == uridAtomLong)
+                                    lastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body;
+                            }
+
+                            if (beatUnit != nullptr)
+                            {
+                                /**/ if (beatUnit->type == uridAtomDouble)
+                                    lastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body;
+                                else if (beatUnit->type == uridAtomFloat)
+                                    lastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body;
+                                else if (beatUnit->type == uridAtomInt)
+                                    lastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body;
+                                else if (beatUnit->type == uridAtomLong)
+                                    lastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body;
+
+                                if (lastPositionData.beatUnit > 0)
+                                    curPosInfo.timeSigDenominator = lastPositionData.beatUnit;
+                            }
+
+                            if (beatsPerBar != nullptr)
+                            {
+                                /**/ if (beatsPerBar->type == uridAtomDouble)
+                                    lastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body;
+                                else if (beatsPerBar->type == uridAtomFloat)
+                                    lastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body;
+                                else if (beatsPerBar->type == uridAtomInt)
+                                    lastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body;
+                                else if (beatsPerBar->type == uridAtomLong)
+                                    lastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body;
+
+                                if (lastPositionData.beatsPerBar > 0.0f)
+                                    curPosInfo.timeSigNumerator = lastPositionData.beatsPerBar;
+                            }
+
+                            if (beatsPerMinute != nullptr)
+                            {
+                                /**/ if (beatsPerMinute->type == uridAtomDouble)
+                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body;
+                                else if (beatsPerMinute->type == uridAtomFloat)
+                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body;
+                                else if (beatsPerMinute->type == uridAtomInt)
+                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body;
+                                else if (beatsPerMinute->type == uridAtomLong)
+                                    lastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body;
+
+                                if (lastPositionData.beatsPerMinute > 0.0f)
+                                {
+                                    curPosInfo.bpm = lastPositionData.beatsPerMinute;
+
+                                    if (lastPositionData.speed != 0)
+                                        curPosInfo.bpm *= std::abs(lastPositionData.speed);
+                                }
+                            }
+
+                            if (frame != nullptr)
+                            {
+                                /**/ if (frame->type == uridAtomDouble)
+                                    lastPositionData.frame = ((LV2_Atom_Double*)frame)->body;
+                                else if (frame->type == uridAtomFloat)
+                                    lastPositionData.frame = ((LV2_Atom_Float*)frame)->body;
+                                else if (frame->type == uridAtomInt)
+                                    lastPositionData.frame = ((LV2_Atom_Int*)frame)->body;
+                                else if (frame->type == uridAtomLong)
+                                    lastPositionData.frame = ((LV2_Atom_Long*)frame)->body;
+
+                                if (lastPositionData.frame >= 0)
+                                {
+                                    curPosInfo.timeInSamples = lastPositionData.frame;
+                                    curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
+                                }
+                            }
+
+                            if (lastPositionData.bar >= 0 && lastPositionData.beatsPerBar > 0.0f)
+                            {
+                                curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
+
+                                if (lastPositionData.barBeat >= 0.0f)
+                                    curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
+                            }
+
+                            lastPositionData.extraValid = (lastPositionData.beatsPerMinute > 0.0 &&
+                                                           lastPositionData.beatUnit > 0 &&
+                                                           lastPositionData.beatsPerBar > 0.0f);
+                        }
+ #endif
+                    }
+                }
+#endif
+                {
+                    AudioSampleBuffer chans (channels, jmax (numInChans, numOutChans), sampleCount);
+                    filter->processBlock (chans, midiEvents);
+                }
+            }
+        }
+
+#if JucePlugin_WantsLV2TimePos
+        // update timePos for next callback
+        if (lastPositionData.speed != 0.0)
+        {
+            if (lastPositionData.speed > 0.0)
+            {
+                // playing forwards
+                lastPositionData.frame += sampleCount;
+            }
+            else
+            {
+                // playing backwards
+                lastPositionData.frame -= sampleCount;
+
+                if (lastPositionData.frame < 0)
+                    lastPositionData.frame = 0;
+            }
+
+            curPosInfo.timeInSamples = lastPositionData.frame;
+            curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate;
+
+            if (lastPositionData.extraValid)
+            {
+                const double beatsPerMinute = lastPositionData.beatsPerMinute * lastPositionData.speed;
+                const double framesPerBeat  = 60.0 * sampleRate / beatsPerMinute;
+                const double addedBarBeats  = double(sampleCount) / framesPerBeat;
+
+                if (lastPositionData.bar >= 0 && lastPositionData.barBeat >= 0.0f)
+                {
+                    lastPositionData.bar    += std::floor((lastPositionData.barBeat+addedBarBeats)/
+                                                           lastPositionData.beatsPerBar);
+                    lastPositionData.barBeat = std::fmod(lastPositionData.barBeat+addedBarBeats,
+                                                         lastPositionData.beatsPerBar);
+
+                    if (lastPositionData.bar < 0)
+                        lastPositionData.bar = 0;
+
+                    curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar;
+                    curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat;
+                }
+
+                curPosInfo.bpm = std::abs(beatsPerMinute);
+            }
+        }
+#endif
+
+#if JucePlugin_ProducesMidiOutput
+        if (portMidiOut != nullptr)
+        {
+            const uint32_t capacity = portMidiOut->atom.size;
+
+            portMidiOut->atom.size = sizeof(LV2_Atom_Sequence_Body);
+            portMidiOut->atom.type = uridAtomSequence;
+            portMidiOut->body.unit = 0;
+            portMidiOut->body.pad  = 0;
+
+            if (! midiEvents.isEmpty())
+            {
+                const uint8* midiEventData;
+                int midiEventSize, midiEventPosition;
+                MidiBuffer::Iterator i (midiEvents);
+
+                uint32_t size, offset = 0;
+                LV2_Atom_Event* aev;
+
+                while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
+                {
+                    jassert (midiEventPosition >= 0 && midiEventPosition < (int)sampleCount);
+
+                    if (sizeof(LV2_Atom_Event) + midiEventSize > capacity - offset)
+                        break;
+
+                    aev = (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, portMidiOut) + offset);
+                    aev->time.frames = midiEventPosition;
+                    aev->body.type   = uridMidiEvent;
+                    aev->body.size   = midiEventSize;
+                    memcpy(LV2_ATOM_BODY(&aev->body), midiEventData, midiEventSize);
+
+                    size    = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEventSize);
+                    offset += size;
+                    portMidiOut->atom.size += size;
+                }
+
+                midiEvents.clear();
+            }
+        } else
+#endif
+        if (! midiEvents.isEmpty())
+        {
+            midiEvents.clear();
+        }
+    }
+
+    //==============================================================================
+    // LV2 extended calls
+
+    uint32_t lv2GetOptions (LV2_Options_Option* options)
+    {
+        // currently unused
+        ignoreUnused(options);
+
+        return LV2_OPTIONS_SUCCESS;
+    }
+
+    uint32_t lv2SetOptions (const LV2_Options_Option* options)
+    {
+        for (int j=0; options[j].key != 0; ++j)
+        {
+            if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
+            {
+                if (options[j].type == uridAtomInt)
+                    bufferSize = *(int*)options[j].value;
+                else
+                    std::cerr << "Host changed nominalBlockLength but with wrong value type" << std::endl;
+            }
+            else if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! usingNominalBlockLength)
+            {
+                if (options[j].type == uridAtomInt)
+                    bufferSize = *(int*)options[j].value;
+                else
+                    std::cerr << "Host changed maxBlockLength but with wrong value type" << std::endl;
+            }
+            else if (options[j].key == uridMap->map(uridMap->handle, LV2_CORE__sampleRate))
+            {
+                if (options[j].type == uridAtomDouble)
+                    sampleRate = *(double*)options[j].value;
+                else
+                    std::cerr << "Host changed sampleRate but with wrong value type" << std::endl;
+            }
+        }
+
+        return LV2_OPTIONS_SUCCESS;
+    }
+
+    const LV2_Program_Descriptor* lv2GetProgram (uint32_t index)
+    {
+        jassert (filter != nullptr);
+
+        if (progDesc.name != nullptr)
+        {
+            free((void*)progDesc.name);
+            progDesc.name = nullptr;
+        }
+
+        if ((int)index < filter->getNumPrograms())
+        {
+            progDesc.bank    = index / 128;
+            progDesc.program = index % 128;
+            progDesc.name    = strdup(filter->getProgramName(index).toUTF8());
+            return &progDesc;
+        }
+
+        return nullptr;
+    }
+
+    void lv2SelectProgram (uint32_t bank, uint32_t program)
+    {
+        jassert (filter != nullptr);
+
+        int realProgram = bank * 128 + program;
+
+        if (realProgram < filter->getNumPrograms())
+        {
+            filter->setCurrentProgram(realProgram);
+
+            // update input control ports now
+            for (int i = 0; i < portControls.size(); ++i)
+            {
+                float value = filter->getParameter(i);
+
+                if (portControls[i] != nullptr)
+                    *portControls[i] = value;
+
+                lastControlValues.set(i, value);
+            }
+        }
+    }
+
+    LV2_State_Status lv2SaveState (LV2_State_Store_Function store, LV2_State_Handle stateHandle)
+    {
+        jassert (filter != nullptr);
+
+#if JucePlugin_WantsLV2StateString
+        String stateData(filter->getStateInformationString().replace("\r\n","\n"));
+        CharPointer_UTF8 charData(stateData.toUTF8());
+
+        store (stateHandle,
+               uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
+               charData.getAddress(),
+               charData.sizeInBytes(),
+               uridMap->map(uridMap->handle, LV2_ATOM__String),
+               LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
+#else
+        MemoryBlock chunkMemory;
+        filter->getCurrentProgramStateInformation (chunkMemory);
+
+        store (stateHandle,
+               uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
+               chunkMemory.getData(),
+               chunkMemory.getSize(),
+               uridMap->map(uridMap->handle, LV2_ATOM__Chunk),
+               LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
+#endif
+
+        return LV2_STATE_SUCCESS;
+    }
+
+    LV2_State_Status lv2RestoreState (LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle, uint32_t flags)
+    {
+        jassert (filter != nullptr);
+
+        size_t size = 0;
+        uint32 type = 0;
+        const void* data = retrieve (stateHandle,
+#if JucePlugin_WantsLV2StateString
+                                     uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI),
+#else
+                                     uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI),
+#endif
+                                     &size, &type, &flags);
+
+        if (data == nullptr || size == 0 || type == 0)
+            return LV2_STATE_ERR_UNKNOWN;
+
+#if JucePlugin_WantsLV2StateString
+        if (type == uridMap->map (uridMap->handle, LV2_ATOM__String))
+        {
+            String stateData (CharPointer_UTF8(static_cast<const char*>(data)));
+            filter->setStateInformationString (stateData);
+
+           #if ! JUCE_AUDIOPROCESSOR_NO_GUI
+            if (ui != nullptr)
+                ui->repaint();
+           #endif
+
+            return LV2_STATE_SUCCESS;
+        }
+#else
+        if (type == uridMap->map (uridMap->handle, LV2_ATOM__Chunk))
+        {
+            filter->setCurrentProgramStateInformation (data, size);
+
+           #if ! JUCE_AUDIOPROCESSOR_NO_GUI
+            if (ui != nullptr)
+                ui->repaint();
+           #endif
+
+            return LV2_STATE_SUCCESS;
+        }
+#endif
+
+        return LV2_STATE_ERR_BAD_TYPE;
+    }
+
+    //==============================================================================
+    // Juce calls
+
+    bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
+    {
+#if JucePlugin_WantsLV2TimePos
+        info = curPosInfo;
+        return true;
+#else
+        ignoreUnused(info);
+        return false;
+#endif
+    }
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+    //==============================================================================
+    JuceLv2UIWrapper* getUI (LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget,
+                             const LV2_Feature* const* features, bool isExternal)
+    {
+        const MessageManagerLock mmLock;
+
+        if (ui != nullptr)
+            ui->resetIfNeeded (writeFunction, controller, widget, features);
+        else
+            ui = new JuceLv2UIWrapper (filter, writeFunction, controller, widget, features, isExternal);
+
+        return ui;
+    }
+#endif
+
+private:
+#if JUCE_LINUX
+    SharedResourcePointer<SharedMessageThread> msgThread;
+#else
+    SharedResourcePointer<ScopedJuceInitialiser_GUI> sharedJuceGUI;
+#endif
+
+    ScopedPointer<AudioProcessor> filter;
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+    ScopedPointer<JuceLv2UIWrapper> ui;
+#endif
+    HeapBlock<float*> channels;
+    MidiBuffer midiEvents;
+    int numInChans, numOutChans;
+
+#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos)
+    LV2_Atom_Sequence* portEventsIn;
+#endif
+#if JucePlugin_ProducesMidiOutput
+    LV2_Atom_Sequence* portMidiOut;
+#endif
+    float* portFreewheel;
+#if JucePlugin_WantsLV2Latency
+    float* portLatency;
+#endif
+    float* portAudioIns[JucePlugin_MaxNumInputChannels];
+    float* portAudioOuts[JucePlugin_MaxNumOutputChannels];
+    Array<float*> portControls;
+
+    uint32 bufferSize;
+    double sampleRate;
+    Array<float> lastControlValues;
+    AudioPlayHead::CurrentPositionInfo curPosInfo;
+
+    struct Lv2PositionData {
+        int64_t  bar;
+        float    barBeat;
+        uint32_t beatUnit;
+        float    beatsPerBar;
+        float    beatsPerMinute;
+        int64_t  frame;
+        double   speed;
+        bool     extraValid;
+
+        Lv2PositionData()
+            : bar(-1),
+              barBeat(-1.0f),
+              beatUnit(0),
+              beatsPerBar(0.0f),
+              beatsPerMinute(0.0f),
+              frame(-1),
+              speed(0.0),
+              extraValid(false) {}
+    };
+    Lv2PositionData lastPositionData;
+
+    const LV2_URID_Map* uridMap;
+    LV2_URID uridAtomBlank;
+    LV2_URID uridAtomObject;
+    LV2_URID uridAtomDouble;
+    LV2_URID uridAtomFloat;
+    LV2_URID uridAtomInt;
+    LV2_URID uridAtomLong;
+    LV2_URID uridAtomSequence;
+    LV2_URID uridMidiEvent;
+    LV2_URID uridTimePos;
+    LV2_URID uridTimeBar;
+    LV2_URID uridTimeBarBeat;
+    LV2_URID uridTimeBeatsPerBar;    // timeSigNumerator
+    LV2_URID uridTimeBeatsPerMinute; // bpm
+    LV2_URID uridTimeBeatUnit;       // timeSigDenominator
+    LV2_URID uridTimeFrame;          // timeInSamples
+    LV2_URID uridTimeSpeed;
+
+    bool usingNominalBlockLength; // if false use maxBlockLength
+
+    LV2_Program_Descriptor progDesc;
+
+    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Wrapper)
+};
+
+//==============================================================================
+// LV2 descriptor functions
+
+static LV2_Handle juceLV2_Instantiate (const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features)
+{
+    return new JuceLv2Wrapper (sampleRate, features);
+}
+
+#define handlePtr ((JuceLv2Wrapper*)handle)
+
+static void juceLV2_ConnectPort (LV2_Handle handle, uint32 port, void* dataLocation)
+{
+    handlePtr->lv2ConnectPort (port, dataLocation);
+}
+
+static void juceLV2_Activate (LV2_Handle handle)
+{
+    handlePtr->lv2Activate();
+}
+
+static void juceLV2_Run( LV2_Handle handle, uint32 sampleCount)
+{
+    handlePtr->lv2Run (sampleCount);
+}
+
+static void juceLV2_Deactivate (LV2_Handle handle)
+{
+    handlePtr->lv2Deactivate();
+}
+
+static void juceLV2_Cleanup (LV2_Handle handle)
+{
+    delete handlePtr;
+}
+
+//==============================================================================
+// LV2 extended functions
+
+static uint32_t juceLV2_getOptions (LV2_Handle handle, LV2_Options_Option* options)
+{
+    return handlePtr->lv2GetOptions(options);
+}
+
+static uint32_t juceLV2_setOptions (LV2_Handle handle, const LV2_Options_Option* options)
+{
+    return handlePtr->lv2SetOptions(options);
+}
+
+static const LV2_Program_Descriptor* juceLV2_getProgram (LV2_Handle handle, uint32_t index)
+{
+    return handlePtr->lv2GetProgram(index);
+}
+
+static void juceLV2_selectProgram (LV2_Handle handle, uint32_t bank, uint32_t program)
+{
+    handlePtr->lv2SelectProgram(bank, program);
+}
+
+static LV2_State_Status juceLV2_SaveState (LV2_Handle handle, LV2_State_Store_Function store, LV2_State_Handle stateHandle,
+                                           uint32_t, const LV2_Feature* const*)
+{
+    return handlePtr->lv2SaveState(store, stateHandle);
+}
+
+static LV2_State_Status juceLV2_RestoreState (LV2_Handle handle, LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle,
+                                              uint32_t flags, const LV2_Feature* const*)
+{
+    return handlePtr->lv2RestoreState(retrieve, stateHandle, flags);
+}
+
+#undef handlePtr
+
+static const void* juceLV2_ExtensionData (const char* uri)
+{
+    static const LV2_Options_Interface options = { juceLV2_getOptions, juceLV2_setOptions };
+    static const LV2_Programs_Interface programs = { juceLV2_getProgram, juceLV2_selectProgram };
+    static const LV2_State_Interface state = { juceLV2_SaveState, juceLV2_RestoreState };
+
+    if (strcmp(uri, LV2_OPTIONS__interface) == 0)
+        return &options;
+    if (strcmp(uri, LV2_PROGRAMS__Interface) == 0)
+        return &programs;
+    if (strcmp(uri, LV2_STATE__interface) == 0)
+        return &state;
+
+    return nullptr;
+}
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+//==============================================================================
+// LV2 UI descriptor functions
+
+static LV2UI_Handle juceLV2UI_Instantiate (LV2UI_Write_Function writeFunction, LV2UI_Controller controller,
+                                           LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal)
+{
+    for (int i = 0; features[i] != nullptr; ++i)
+    {
+        if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr)
+        {
+            JuceLv2Wrapper* wrapper = (JuceLv2Wrapper*)features[i]->data;
+            return wrapper->getUI(writeFunction, controller, widget, features, isExternal);
+        }
+    }
+
+    std::cerr << "Host does not support instance-access, cannot use UI" << std::endl;
+    return nullptr;
+}
+
+static LV2UI_Handle juceLV2UI_InstantiateExternal (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
+                                                   LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
+{
+    return juceLV2UI_Instantiate(writeFunction, controller, widget, features, true);
+}
+
+static LV2UI_Handle juceLV2UI_InstantiateParent (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
+                                                 LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
+{
+    return juceLV2UI_Instantiate(writeFunction, controller, widget, features, false);
+}
+
+static void juceLV2UI_Cleanup (LV2UI_Handle handle)
+{
+    ((JuceLv2UIWrapper*)handle)->lv2Cleanup();
+}
+#endif
+
+//==============================================================================
+// static LV2 Descriptor objects
+
+static const LV2_Descriptor JuceLv2Plugin = {
+    strdup(getPluginURI().toRawUTF8()),
+    juceLV2_Instantiate,
+    juceLV2_ConnectPort,
+    juceLV2_Activate,
+    juceLV2_Run,
+    juceLV2_Deactivate,
+    juceLV2_Cleanup,
+    juceLV2_ExtensionData
+};
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+static const LV2UI_Descriptor JuceLv2UI_External = {
+    strdup(String(getPluginURI() + "#ExternalUI").toRawUTF8()),
+    juceLV2UI_InstantiateExternal,
+    juceLV2UI_Cleanup,
+    nullptr,
+    nullptr
+};
+
+static const LV2UI_Descriptor JuceLv2UI_Parent = {
+    strdup(String(getPluginURI() + "#ParentUI").toRawUTF8()),
+    juceLV2UI_InstantiateParent,
+    juceLV2UI_Cleanup,
+    nullptr,
+    nullptr
+};
+#endif
+
+static const struct DescriptorCleanup {
+    DescriptorCleanup() {}
+    ~DescriptorCleanup()
+    {
+        free((void*)JuceLv2Plugin.URI);
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+        free((void*)JuceLv2UI_External.URI);
+        free((void*)JuceLv2UI_Parent.URI);
+#endif
+    }
+} _descCleanup;
+
+#if JUCE_WINDOWS
+ #define JUCE_EXPORTED_FUNCTION extern "C" __declspec (dllexport)
+#else
+ #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility("default")))
+#endif
+
+//==============================================================================
+// startup code..
+
+JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename);
+JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename)
+{
+    createLv2Files (basename);
+}
+
+JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index);
+JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index)
+{
+    return (index == 0) ? &JuceLv2Plugin : nullptr;
+}
+
+#if ! JUCE_AUDIOPROCESSOR_NO_GUI
+JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index);
+JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index)
+{
+    switch (index)
+    {
+    case 0:
+        return &JuceLv2UI_External;
+    case 1:
+        return &JuceLv2UI_Parent;
+    default:
+        return nullptr;
+    }
+}
+#endif
+
+#endif
diff --git a/debian/extra/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp b/debian/extra/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp
new file mode 100644 (file)
index 0000000..dc507e1
--- /dev/null
@@ -0,0 +1,27 @@
+/*\r
+  ==============================================================================\r
+\r
+   This file is part of the JUCE library.\r
+   Copyright (c) 2017 - ROLI Ltd.\r
+\r
+   JUCE is an open source library subject to commercial or open-source\r
+   licensing.\r
+\r
+   By using JUCE, you agree to the terms of both the JUCE 5 End-User License\r
+   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the\r
+   27th April 2017).\r
+\r
+   End User License Agreement: www.juce.com/juce-5-licence\r
+   Privacy Policy: www.juce.com/juce-5-privacy-policy\r
+\r
+   Or: You may also use this code under the terms of the GPL v3 (see\r
+   www.gnu.org/licenses).\r
+\r
+   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER\r
+   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE\r
+   DISCLAIMED.\r
+\r
+  ==============================================================================\r
+*/\r
+\r
+#include "LV2/juce_LV2_Wrapper.cpp"\r
diff --git a/debian/patches/LV2-audioprocessor.patch b/debian/patches/LV2-audioprocessor.patch
new file mode 100644 (file)
index 0000000..c4511e9
--- /dev/null
@@ -0,0 +1,67 @@
+Description: LV2 fixes for autiodprocessor
+Author: Filipe Coelho <falkTX>
+Origin: https://github.com/DISTRHO/juce/tree/9f6cdc3659df13169285464ee1d13ef14357f833
+Reviewed-by: IOhannes m zmölnig
+Last-Update: 2018-02-10
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- juce.orig/modules/juce_audio_processors/processors/juce_AudioProcessor.h
++++ juce/modules/juce_audio_processors/processors/juce_AudioProcessor.h
+@@ -908,6 +908,7 @@
+     */\r
+     virtual void setNonRealtime (bool isNonRealtime) noexcept;\r
\r
++   #if ! JUCE_AUDIOPROCESSOR_NO_GUI\r
+     //==============================================================================\r
+     /** Creates the processor's GUI.\r
\r
+@@ -954,6 +955,7 @@
+         This may call createEditor() internally to create the component.\r
+     */\r
+     AudioProcessorEditor* createEditorIfNeeded();\r
++   #endif\r
\r
+     //==============================================================================\r
+     /** This must return the correct value immediately after the object has been\r
+@@ -1278,6 +1280,11 @@
+     virtual void processorLayoutsChanged();\r
\r
+     //==============================================================================\r
++    /** LV2 specific calls, saving/restore as string. */\r
++    virtual String getStateInformationString () { return String(); }\r
++    virtual void setStateInformationString (const String&) {}\r
++\r
++    //==============================================================================\r
+     /** Adds a listener that will be called when an aspect of this processor changes. */\r
+     virtual void addListener (AudioProcessorListener* newListener);\r
\r
+@@ -1319,9 +1326,11 @@
+                                                   const AudioChannelSet& mainOutputLayout,\r
+                                                   bool idForAudioSuite) const;\r
\r
++   #if ! JUCE_AUDIOPROCESSOR_NO_GUI\r
+     //==============================================================================\r
+     /** Not for public use - this is called before deleting an editor component. */\r
+     void editorBeingDeleted (AudioProcessorEditor*) noexcept;\r
++   #endif\r
\r
+     /** Flags to indicate the type of plugin context in which a processor is being used. */\r
+     enum WrapperType\r
+@@ -1333,6 +1342,7 @@
+         wrapperType_AudioUnitv3,\r
+         wrapperType_RTAS,\r
+         wrapperType_AAX,\r
++        wrapperType_LV2,\r
+         wrapperType_Standalone\r
+     };\r
\r
+@@ -1581,7 +1591,9 @@
\r
+     //==============================================================================\r
+     Array<AudioProcessorListener*> listeners;\r
++   #if ! JUCE_AUDIOPROCESSOR_NO_GUI\r
+     Component::SafePointer<AudioProcessorEditor> activeEditor;\r
++   #endif\r
+     double currentSampleRate = 0;\r
+     int blockSize = 0, latencySamples = 0;\r
+     bool suspended = false, nonRealtime = false;\r
index 31adb158c57634cd2638424482fde05276b70367..f147bd8d1a33016d2ad09c491b177822fa986897 100644 (file)
@@ -1,4 +1,5 @@
 reproducible-date.patch
+LV2-audioprocessor.patch
 debian_fixed-defines.patch
 debian_gpl_variant.patch
 debian_no-update-check.patch