summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2008-01-15 18:49:34 +0000
committerDavid Robillard <d@drobilla.net>2008-01-15 18:49:34 +0000
commit123399c2a8141458b21b0079ac17aa9ede16e763 (patch)
treece1b77ca58ba24f858efe59714d9ca1c3addaeff
parent69b94c201d39c6a64fa6f1e9412578d482d0ce5b (diff)
LV2 support.
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2922 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--SConstruct32
-rw-r--r--gtk2_ardour/SConscript3
-rw-r--r--gtk2_ardour/plugin_selector.cc9
-rw-r--r--gtk2_ardour/plugin_selector.h1
-rw-r--r--libs/ardour/SConscript7
-rw-r--r--libs/ardour/ardour/audio_unit.h6
-rw-r--r--libs/ardour/ardour/ladspa_plugin.h16
-rw-r--r--libs/ardour/ardour/lv2_plugin.h138
-rw-r--r--libs/ardour/ardour/plugin_manager.h19
-rw-r--r--libs/ardour/ardour/types.h1
-rw-r--r--libs/ardour/insert.cc13
-rw-r--r--libs/ardour/lv2_plugin.cc539
-rw-r--r--libs/ardour/plugin.cc10
-rw-r--r--libs/ardour/plugin_manager.cc26
-rw-r--r--libs/ardour/route.cc1
15 files changed, 792 insertions, 29 deletions
diff --git a/SConstruct b/SConstruct
index 88b15db0f3..b0dfbb292c 100644
--- a/SConstruct
+++ b/SConstruct
@@ -46,6 +46,7 @@ opts.AddOptions(
BoolOption('UNIVERSAL', 'Compile as universal binary. Requires that external libraries are already universal.', 0),
BoolOption('VERSIONED', 'Add revision information to ardour/gtk executable name inside the build directory', 0),
BoolOption('VST', 'Compile with support for VST', 0),
+ BoolOption('LV2', 'Compile with support for LV2 (if slv2 is available)', 1),
BoolOption('GPROFILE', 'Compile with support for gprofile (Developers only)', 0),
BoolOption('TRANZPORT', 'Compile with support for Frontier Designs (if libusb is available)', 1)
)
@@ -403,7 +404,6 @@ else:
if os.path.isfile('.personal_use_only'):
os.remove('.personal_use_only')
-
####################
# push environment
####################
@@ -527,6 +527,17 @@ if env['FFT_ANALYSIS']:
print ('FFT Analysis cannot be compiled without the FFTW3 headers, which do not seem to be installed')
sys.exit (1)
conf.Finish()
+
+if env['LV2']:
+ conf = env.Configure(custom_tests = { 'CheckPKGExists' : CheckPKGExists })
+
+ if conf.CheckPKGExists ('\"slv2 >= 0.4.4\"'):
+ libraries['slv2'] = LibraryInfo()
+ libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2')
+ else:
+ print 'Building Ardour with LV2 support requires SLV2 >= 0.4.4'
+ env['LV2'] = 0
+ conf.Finish()
libraries['jack'] = LibraryInfo()
libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
@@ -929,7 +940,7 @@ if env['SYSLIBS']:
print '%s >= %s not found.' %(pkg, version)
DependenciesRequiredMessage()
Exit(1)
-
+
env = conf.Finish()
libraries['sigc2'] = LibraryInfo()
@@ -962,9 +973,9 @@ if env['SYSLIBS']:
# libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
libraries['soundtouch'] = LibraryInfo()
- libraries['soundtouch'].ParseConfig ('pkg-config --cflags --libs soundtouch-1.0')
+ #libraries['soundtouch'].ParseConfig ('pkg-config --cflags --libs soundtouch-1.0')
# Comment the previous line and uncomment this for Debian:
- #libraries['soundtouch'].ParseConfig ('pkg-config --cflags --libs libSoundTouch')
+ libraries['soundtouch'].ParseConfig ('pkg-config --cflags --libs libSoundTouch')
libraries['appleutility'] = LibraryInfo(LIBS='libappleutility',
LIBPATH='#libs/appleutility',
@@ -1057,13 +1068,12 @@ else:
]
gtk_subdirs = [
- 'libs/glibmm2',
- 'libs/gtkmm2/pango',
- 'libs/gtkmm2/atk',
- 'libs/gtkmm2/gdk',
- 'libs/gtkmm2/gtk',
- 'libs/libgnomecanvasmm',
-# 'libs/flowcanvas',
+ 'libs/glibmm2',
+ 'libs/gtkmm2/pango',
+ 'libs/gtkmm2/atk',
+ 'libs/gtkmm2/gdk',
+ 'libs/gtkmm2/gtk',
+ 'libs/libgnomecanvasmm',
'libs/gtkmm2ext',
'gtk2_ardour',
'libs/clearlooks'
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index cb2b1e9d4e..d2c0448e23 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -270,6 +270,9 @@ if env['VST']:
extra_sources += vst_files
gtkardour.Append (CCFLAGS="-DVST_SUPPORT", CPPPATH="#libs/fst")
+if env['LV2']:
+ gtkardour.Append (CCFLAGS="-DHAVE_SLV2")
+
if gtkardour['GTKOSX']:
extra_sources += gtkosx_files
gtkardour.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX")
diff --git a/gtk2_ardour/plugin_selector.cc b/gtk2_ardour/plugin_selector.cc
index cd597af8e4..7a5d76e8ad 100644
--- a/gtk2_ardour/plugin_selector.cc
+++ b/gtk2_ardour/plugin_selector.cc
@@ -227,6 +227,7 @@ PluginSelector::refill ()
setup_filter_string (filterstr);
ladspa_refiller (filterstr);
+ lv2_refiller (filterstr);
vst_refiller (filterstr);
au_refiller (filterstr);
}
@@ -281,6 +282,14 @@ PluginSelector::ladspa_refiller (const std::string& filterstr)
}
void
+PluginSelector::lv2_refiller (const std::string& filterstr)
+{
+#ifdef HAVE_SLV2
+ refiller (manager->lv2_plugin_info(), filterstr, "LV2");
+#endif
+}
+
+void
PluginSelector::vst_refiller (const std::string& filterstr)
{
#ifdef VST_SUPPORT
diff --git a/gtk2_ardour/plugin_selector.h b/gtk2_ardour/plugin_selector.h
index ac300638d7..ea258d3be0 100644
--- a/gtk2_ardour/plugin_selector.h
+++ b/gtk2_ardour/plugin_selector.h
@@ -94,6 +94,7 @@ class PluginSelector : public ArdourDialog
void refill ();
void refiller (const ARDOUR::PluginInfoList& plugs, const::std::string& filterstr, const char* type);
void ladspa_refiller (const std::string&);
+ void lv2_refiller (const std::string&);
void vst_refiller (const std::string&);
void au_refiller (const std::string&);
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 5f33eb01f8..d8d6885156 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -57,6 +57,7 @@ insert.cc
io.cc
jack_slave.cc
ladspa_plugin.cc
+lv2_plugin.cc
location.cc
mtc_slave.cc
named_selection.cc
@@ -111,6 +112,9 @@ if ardour['VST']:
extra_sources += vst_files
ardour.Append(CCFLAGS="-DVST_SUPPORT", CPPPATH="#libs/fst")
+if ardour['LV2']:
+ ardour.Append(CCFLAGS="-DHAVE_SLV2")
+
if ardour['LIBLO']:
extra_sources += osc_files
@@ -272,6 +276,9 @@ else:
ardour.Merge ([ libraries['soundtouch'] ])
timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ]
+if ardour['LV2']:
+ ardour.Merge ([ libraries['slv2'] ])
+
if ardour['LIBLO']:
ardour.Merge ([ libraries['lo'] ])
diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h
index 68498f9074..306269b291 100644
--- a/libs/ardour/ardour/audio_unit.h
+++ b/libs/ardour/ardour/audio_unit.h
@@ -143,9 +143,9 @@ class AUPluginInfo : public PluginInfo {
private:
boost::shared_ptr<CAComponentDescription> descriptor;
- static void discover_music (PluginInfoList&);
- static void discover_fx (PluginInfoList&);
- static void discover_by_description (PluginInfoList&, CAComponentDescription&);
+ static void discover_music (PluginInfoList&);
+ static void discover_fx (PluginInfoList&);
+ static void discover_by_description (PluginInfoList&, CAComponentDescription&);
};
typedef boost::shared_ptr<AUPluginInfo> AUPluginInfoPtr;
diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h
index 8c00675b69..545806e437 100644
--- a/libs/ardour/ardour/ladspa_plugin.h
+++ b/libs/ardour/ardour/ladspa_plugin.h
@@ -20,7 +20,6 @@
#ifndef __ardour_ladspa_plugin_h__
#define __ardour_ladspa_plugin_h__
-#include <list>
#include <set>
#include <vector>
#include <string>
@@ -33,11 +32,6 @@
#include <jack/types.h>
#include <ardour/ladspa.h>
#include <ardour/plugin.h>
-#include <ardour/ladspa_plugin.h>
-
-using std::string;
-using std::vector;
-using std::list;
namespace ARDOUR {
class AudioEngine;
@@ -90,10 +84,10 @@ class LadspaPlugin : public ARDOUR::Plugin
void set_block_size (nframes_t nframes) {}
- int connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset);
- string describe_parameter (uint32_t);
- string state_node_name() const { return "ladspa"; }
- void print_parameter (uint32_t, char*, uint32_t len) const;
+ int connect_and_run (std::vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset);
+ std::string describe_parameter (uint32_t);
+ std::string state_node_name() const { return "ladspa"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
bool parameter_is_audio(uint32_t) const;
bool parameter_is_control(uint32_t) const;
@@ -103,7 +97,7 @@ class LadspaPlugin : public ARDOUR::Plugin
XMLNode& get_state();
int set_state(const XMLNode& node);
- bool save_preset(string name);
+ bool save_preset(std::string name);
bool has_editor() const { return false; }
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
new file mode 100644
index 0000000000..eacf823a01
--- /dev/null
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -0,0 +1,138 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_lv2_plugin_h__
+#define __ardour_lv2_plugin_h__
+
+#include <set>
+#include <vector>
+#include <string>
+#include <dlfcn.h>
+
+#include <sigc++/signal.h>
+
+#include <pbd/stateful.h>
+
+#include <jack/types.h>
+#include <slv2/slv2.h>
+#include <ardour/plugin.h>
+
+namespace ARDOUR {
+class AudioEngine;
+class Session;
+
+class LV2Plugin : public ARDOUR::Plugin
+{
+ public:
+ LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, SLV2Plugin plugin, nframes_t sample_rate);
+ LV2Plugin (const LV2Plugin &);
+ ~LV2Plugin ();
+
+ /* Plugin interface */
+
+ std::string unique_id() const;
+ const char* label() const { return slv2_plugin_get_name(_plugin); }
+ const char* name() const { return slv2_plugin_get_name(_plugin); }
+ const char* maker() const { return slv2_plugin_get_author_name(_plugin); }
+ uint32_t parameter_count() const { return slv2_plugin_get_num_ports(_plugin); }
+ float default_value (uint32_t port);
+ nframes_t latency() const;
+ void set_parameter (uint32_t port, float val);
+ float get_parameter (uint32_t port) const;
+ int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
+ uint32_t nth_parameter (uint32_t port, bool& ok) const;
+
+ std::set<uint32_t> automatable() const;
+
+ void activate () {
+ if (!_was_activated) {
+ slv2_instance_activate(_instance);
+ _was_activated = true;
+ }
+ }
+
+ void deactivate () {
+ if (_was_activated) {
+ slv2_instance_deactivate(_instance);
+ _was_activated = false;
+ }
+ }
+
+ void cleanup () {
+ activate();
+ deactivate();
+ slv2_instance_free(_instance);
+ _instance = NULL;
+ }
+
+ void set_block_size (nframes_t nframes) {}
+
+ int connect_and_run (std::vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset);
+ std::string describe_parameter (uint32_t);
+ std::string state_node_name() const { return "lv2"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
+
+ bool parameter_is_audio(uint32_t) const;
+ bool parameter_is_control(uint32_t) const;
+ bool parameter_is_input(uint32_t) const;
+ bool parameter_is_output(uint32_t) const;
+ bool parameter_is_toggled(uint32_t) const;
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+ bool save_preset(std::string name);
+
+ bool has_editor() const { return false; }
+
+ int require_output_streams (uint32_t);
+
+ private:
+ void* _module;
+ SLV2Plugin _plugin;
+ SLV2Template _template;
+ SLV2Instance _instance;
+ nframes_t _sample_rate;
+ float* _control_data;
+ float* _shadow_data;
+ float* _latency_control_port;
+ bool _was_activated;
+ vector<bool> _port_is_input;
+
+ void init (SLV2Plugin plugin, nframes_t rate);
+ void run (nframes_t nsamples);
+ void latency_compute_run ();
+};
+
+class LV2PluginInfo : public PluginInfo {
+public:
+ LV2PluginInfo (void* slv2_plugin);;
+ ~LV2PluginInfo ();;
+ static PluginInfoList discover (void* slv2_world);
+
+ PluginPtr load (Session& session);
+
+ void* _slv2_plugin;
+};
+
+typedef boost::shared_ptr<LV2PluginInfo> LV2PluginInfoPtr;
+
+} // namespace ARDOUR
+
+#endif /* __ardour_lv2_plugin_h__ */
diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h
index 64b871104b..dd50a079e3 100644
--- a/libs/ardour/ardour/plugin_manager.h
+++ b/libs/ardour/ardour/plugin_manager.h
@@ -27,20 +27,23 @@
#include <ardour/types.h>
#include <ardour/plugin.h>
+#ifdef HAVE_SLV2
+#include <slv2/slv2.h>
+#endif
+
namespace ARDOUR {
class Plugin;
-class Session;
-class AudioEngine;
class PluginManager {
public:
PluginManager ();
~PluginManager ();
- ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; }
+ ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; }
ARDOUR::PluginInfoList &ladspa_plugin_info () { return _ladspa_plugin_info; }
- ARDOUR::PluginInfoList &au_plugin_info () { return _au_plugin_info; }
+ ARDOUR::PluginInfoList &lv2_plugin_info () { return _lv2_plugin_info; }
+ ARDOUR::PluginInfoList &au_plugin_info () { return _au_plugin_info; }
void refresh ();
@@ -52,7 +55,12 @@ class PluginManager {
private:
ARDOUR::PluginInfoList _vst_plugin_info;
ARDOUR::PluginInfoList _ladspa_plugin_info;
+ ARDOUR::PluginInfoList _lv2_plugin_info;
ARDOUR::PluginInfoList _au_plugin_info;
+
+#ifdef HAVE_SLV2
+ SLV2World _lv2_world;
+#endif
std::map<uint32_t, std::string> rdf_type;
@@ -69,6 +77,9 @@ class PluginManager {
int au_discover ();
void au_refresh ();
+
+ int lv2_discover ();
+ void lv2_refresh ();
int vst_discover_from_path (std::string path);
int vst_discover (std::string path);
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index b179bce47c..933ca44e73 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -340,6 +340,7 @@ namespace ARDOUR {
enum PluginType {
AudioUnit,
LADSPA,
+ LV2,
VST
};
diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc
index c2408ec86c..eb0576b46e 100644
--- a/libs/ardour/insert.cc
+++ b/libs/ardour/insert.cc
@@ -30,6 +30,10 @@
#include <ardour/route.h>
#include <ardour/ladspa_plugin.h>
+#ifdef HAVE_SLV2
+#include <ardour/lv2_plugin.h>
+#endif
+
#ifdef VST_SUPPORT
#include <ardour/vst_plugin.h>
#endif
@@ -515,6 +519,9 @@ boost::shared_ptr<Plugin>
PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
{
boost::shared_ptr<LadspaPlugin> lp;
+#ifdef HAVE_SLV2
+ boost::shared_ptr<LV2Plugin> lv2p;
+#endif
#ifdef VST_SUPPORT
boost::shared_ptr<VSTPlugin> vp;
#endif
@@ -524,6 +531,10 @@ PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
if ((lp = boost::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
return boost::shared_ptr<Plugin> (new LadspaPlugin (*lp));
+#ifdef HAVE_SLV2
+ } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
+ return boost::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
+#endif
#ifdef VST_SUPPORT
} else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) {
return boost::shared_ptr<Plugin> (new VSTPlugin (*vp));
@@ -673,6 +684,8 @@ PluginInsert::set_state(const XMLNode& node)
if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */
type = ARDOUR::LADSPA;
+ } else if (prop->value() == X_("lv2")) {
+ type = ARDOUR::LV2;
} else if (prop->value() == X_("vst")) {
type = ARDOUR::VST;
} else if (prop->value() == X_("audiounit")) {
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
new file mode 100644
index 0000000000..fbaa7f6f3b
--- /dev/null
+++ b/libs/ardour/lv2_plugin.cc
@@ -0,0 +1,539 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+#include <string>
+
+#include <cstdlib>
+#include <cmath>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+#include <pbd/xml++.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/lv2_plugin.h>
+
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, SLV2Plugin plugin, nframes_t rate)
+ : Plugin (e, session)
+{
+ init (plugin, rate);
+}
+
+LV2Plugin::LV2Plugin (const LV2Plugin &other)
+ : Plugin (other)
+{
+ init (other._plugin, other._sample_rate);
+
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ _control_data[i] = other._shadow_data[i];
+ _shadow_data[i] = other._shadow_data[i];
+ }
+}
+
+void
+LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
+{
+ _plugin = plugin;
+ _template = slv2_plugin_get_template(plugin);
+ _control_data = 0;
+ _shadow_data = 0;
+ _latency_control_port = 0;
+ _was_activated = false;
+
+ _instance = slv2_plugin_instantiate(plugin, rate, NULL);
+
+ if (_instance == 0) {
+ error << _("LV2: Failed to instantiate plugin ") << slv2_plugin_get_uri(plugin) << endl;
+ throw failed_constructor();
+ }
+
+ if (slv2_plugin_has_feature(plugin, "http://lv2plug.in/ns/lv2core#inPlaceBroken")) {
+ error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), slv2_plugin_get_name(plugin)) << endmsg;
+ throw failed_constructor();
+ }
+
+ _sample_rate = rate;
+
+ const uint32_t num_ports = slv2_plugin_get_num_ports(plugin);
+
+ _control_data = new float[num_ports];
+ _shadow_data = new float[num_ports];
+
+ const bool latent = slv2_plugin_has_latency(plugin);
+ uint32_t latency_port = (latent ? slv2_plugin_get_latency_port(plugin) : 0);
+
+ for (uint32_t i = 0; i < num_ports; ++i) {
+ if (parameter_is_control(i)) {
+ slv2_instance_connect_port (_instance, i, &_control_data[i]);
+
+ if (latent && i == latency_port) {
+ _latency_control_port = &_control_data[i];
+ *_latency_control_port = 0;
+ }
+
+ if (parameter_is_input(i)) {
+ _shadow_data[i] = default_value (i);
+ }
+ }
+ }
+
+ Plugin::setup_controls ();
+
+ latency_compute_run ();
+}
+
+LV2Plugin::~LV2Plugin ()
+{
+ deactivate ();
+ cleanup ();
+
+ GoingAway (); /* EMIT SIGNAL */
+
+ slv2_instance_free(_instance);
+
+ if (_control_data) {
+ delete [] _control_data;
+ }
+
+ if (_shadow_data) {
+ delete [] _shadow_data;
+ }
+}
+
+string
+LV2Plugin::unique_id() const
+{
+ return slv2_plugin_get_uri(_plugin);
+}
+
+
+float
+LV2Plugin::default_value (uint32_t port)
+{
+ return slv2_port_get_default_value(_plugin,
+ slv2_plugin_get_port_by_index(_plugin, port));
+}
+
+void
+LV2Plugin::set_parameter (uint32_t which, float val)
+{
+ if (which < slv2_plugin_get_num_ports(_plugin)) {
+ _shadow_data[which] = val;
+ ParameterChanged (which, val); /* EMIT SIGNAL */
+
+ if (which < parameter_count() && controls[which]) {
+ controls[which]->Changed ();
+ }
+
+ } else {
+ warning << string_compose (_("Illegal parameter number used with plugin \"%1\"."
+ "This is a bug in either Ardour or the LV2 plugin (%2)"),
+ name(), unique_id()) << endmsg;
+ }
+}
+
+float
+LV2Plugin::get_parameter (uint32_t which) const
+{
+ if (parameter_is_input(which)) {
+ return (float) _shadow_data[which];
+ } else {
+ return (float) _control_data[which];
+ }
+ return 0.0f;
+}
+
+uint32_t
+LV2Plugin::nth_parameter (uint32_t n, bool& ok) const
+{
+ uint32_t x, c;
+
+ ok = false;
+
+ for (c = 0, x = 0; x < slv2_plugin_get_num_ports(_plugin); ++x) {
+ if (parameter_is_control (x)) {
+ if (c++ == n) {
+ ok = true;
+ return x;
+ }
+ }
+ }
+
+ return 0;
+}
+
+XMLNode&
+LV2Plugin::get_state()
+{
+ XMLNode *root = new XMLNode(state_node_name());
+ XMLNode *child;
+ char buf[16];
+ LocaleGuard lg (X_("POSIX"));
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+
+ if (parameter_is_input(i) && parameter_is_control(i)) {
+ child = new XMLNode("port");
+ snprintf(buf, sizeof(buf), "%u", i);
+ child->add_property("number", string(buf));
+ snprintf(buf, sizeof(buf), "%+f", _shadow_data[i]);
+ child->add_property("value", string(buf));
+ root->add_child_nocopy (*child);
+
+ if (i < controls.size() && controls[i]) {
+ root->add_child_nocopy (controls[i]->get_state());
+ }
+ }
+ }
+
+ return *root;
+}
+
+bool
+LV2Plugin::save_preset (string name)
+{
+ return Plugin::save_preset (name, "lv2");
+}
+
+int
+LV2Plugin::set_state(const XMLNode& node)
+{
+ XMLNodeList nodes;
+ XMLProperty *prop;
+ XMLNodeConstIterator iter;
+ XMLNode *child;
+ const char *port;
+ const char *data;
+ uint32_t port_id;
+ LocaleGuard lg (X_("POSIX"));
+
+ if (node.name() != state_node_name()) {
+ error << _("Bad node sent to LV2Plugin::set_state") << endmsg;
+ return -1;
+ }
+
+ nodes = node.children ("port");
+
+ for(iter = nodes.begin(); iter != nodes.end(); ++iter){
+
+ child = *iter;
+
+ if ((prop = child->property("number")) != 0) {
+ port = prop->value().c_str();
+ } else {
+ warning << _("LV2: no lv2 port number") << endmsg;
+ continue;
+ }
+
+ if ((prop = child->property("value")) != 0) {
+ data = prop->value().c_str();
+ } else {
+ warning << _("LV2: no lv2 port data") << endmsg;
+ continue;
+ }
+
+ sscanf (port, "%" PRIu32, &port_id);
+ set_parameter (port_id, atof(data));
+ }
+
+ latency_compute_run ();
+
+ return 0;
+}
+
+int
+LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const
+{
+ SLV2Port port = slv2_plugin_get_port_by_index(_plugin, which);
+
+ #define LV2_URI "http://lv2plug.in/ns/lv2core#"
+
+ desc.integer_step = slv2_port_has_property(_plugin, port, LV2_URI "integer");
+ desc.toggled = slv2_port_has_property(_plugin, port, LV2_URI "toggled");
+ desc.logarithmic = false; // TODO (LV2 extension)
+ desc.sr_dependent = slv2_port_has_property(_plugin, port, LV2_URI "sampleRate");
+ desc.label = slv2_port_get_name(_plugin, port);
+ desc.lower = slv2_port_get_minimum_value(_plugin, port);
+ desc.upper = slv2_port_get_maximum_value(_plugin, port);
+ desc.min_unbound = false; // TODO (LV2 extension)
+ desc.max_unbound = false; // TODO (LV2 extension)
+
+ if (desc.integer_step) {
+ desc.step = 1.0;
+ desc.smallstep = 0.1;
+ desc.largestep = 10.0;
+ } else {
+ const float delta = desc.upper - desc.lower;
+ desc.step = delta / 1000.0f;
+ desc.smallstep = delta / 10000.0f;
+ desc.largestep = delta/10.0f;
+ }
+
+ return 0;
+}
+
+
+string
+LV2Plugin::describe_parameter (uint32_t which)
+{
+ if (which < parameter_count()) {
+ return slv2_port_get_name(_plugin,
+ slv2_plugin_get_port_by_index(_plugin, which));
+ } else {
+ return "??";
+ }
+}
+
+nframes_t
+LV2Plugin::latency () const
+{
+ if (_latency_control_port) {
+ return (nframes_t) floor (*_latency_control_port);
+ } else {
+ return 0;
+ }
+}
+
+set<uint32_t>
+LV2Plugin::automatable () const
+{
+ set<uint32_t> ret;
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+ if (parameter_is_input(i) && parameter_is_control(i)) {
+ ret.insert (ret.end(), i);
+ }
+ }
+
+ return ret;
+}
+
+int
+LV2Plugin::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, int32_t& in_index, int32_t& out_index, nframes_t nframes, nframes_t offset)
+{
+ uint32_t port_index;
+ cycles_t then, now;
+
+ port_index = 0;
+
+ then = get_cycles ();
+
+ while (port_index < parameter_count()) {
+ if (parameter_is_audio(port_index)) {
+ if (parameter_is_input(port_index)) {
+ slv2_instance_connect_port(_instance, port_index,
+ bufs[min((uint32_t)in_index, nbufs - 1)] + offset);
+ in_index++;
+ } else if (parameter_is_output(port_index)) {
+ slv2_instance_connect_port(_instance, port_index,
+ bufs[min((uint32_t)out_index, nbufs - 1)] + offset);
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run (nframes);
+ now = get_cycles ();
+ set_cycles ((uint32_t) (now - then));
+
+ return 0;
+}
+
+bool
+LV2Plugin::parameter_is_control (uint32_t param) const
+{
+ SLV2PortSignature sig = slv2_template_get_port(_template, param);
+ return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL);
+}
+
+bool
+LV2Plugin::parameter_is_audio (uint32_t param) const
+{
+ SLV2PortSignature sig = slv2_template_get_port(_template, param);
+ return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_AUDIO);
+}
+
+bool
+LV2Plugin::parameter_is_output (uint32_t param) const
+{
+ SLV2PortSignature sig = slv2_template_get_port(_template, param);
+ return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_OUTPUT);
+}
+
+bool
+LV2Plugin::parameter_is_input (uint32_t param) const
+{
+ SLV2PortSignature sig = slv2_template_get_port(_template, param);
+ return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT);
+}
+
+void
+LV2Plugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
+{
+ if (buf && len) {
+ if (param < parameter_count()) {
+ snprintf (buf, len, "%.3f", get_parameter (param));
+ } else {
+ strcat (buf, "0");
+ }
+ }
+}
+
+void
+LV2Plugin::run (nframes_t nframes)
+{
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ SLV2PortSignature sig = slv2_template_get_port(_template, i);
+ if (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL
+ && slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT) {
+ _control_data[i] = _shadow_data[i];
+ }
+ }
+
+ slv2_instance_run(_instance, nframes);
+}
+
+void
+LV2Plugin::latency_compute_run ()
+{
+ if (!_latency_control_port) {
+ return;
+ }
+
+ /* we need to run the plugin so that it can set its latency
+ parameter.
+ */
+
+ activate ();
+
+ uint32_t port_index = 0;
+ uint32_t in_index = 0;
+ uint32_t out_index = 0;
+ const nframes_t bufsize = 1024;
+ float buffer[bufsize];
+
+ memset(buffer,0,sizeof(float)*bufsize);
+
+ /* Note that we've already required that plugins
+ be able to handle in-place processing.
+ */
+
+ port_index = 0;
+
+ while (port_index < parameter_count()) {
+ if (parameter_is_audio (port_index)) {
+ if (parameter_is_input (port_index)) {
+ slv2_instance_connect_port (_instance, port_index, buffer);
+ in_index++;
+ } else if (parameter_is_output (port_index)) {
+ slv2_instance_connect_port (_instance, port_index, buffer);
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run (bufsize);
+ deactivate ();
+}
+
+LV2PluginInfo::LV2PluginInfo (void* slv2_plugin)
+ : _slv2_plugin(slv2_plugin)
+{
+}
+
+LV2PluginInfo::~LV2PluginInfo()
+{
+}
+
+PluginPtr
+LV2PluginInfo::load (Session& session)
+{
+ SLV2Plugin p = (SLV2Plugin)_slv2_plugin;
+
+ try {
+ PluginPtr plugin;
+
+ plugin.reset (new LV2Plugin (session.engine(), session, p, session.frame_rate()));
+
+ plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this)));
+ return plugin;
+ }
+
+ catch (failed_constructor &err) {
+ return PluginPtr ((Plugin*) 0);
+ }
+
+ return PluginPtr();
+}
+
+PluginInfoList
+LV2PluginInfo::discover (void* slv2_world)
+{
+ PluginInfoList plugs;
+
+ SLV2Plugins plugins = slv2_world_get_all_plugins((SLV2World)slv2_world);
+
+ for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) {
+ SLV2Plugin p = slv2_plugins_get_at(plugins, i);
+ LV2PluginInfoPtr info (new LV2PluginInfo(p));
+
+ info->name = slv2_plugin_get_name(p);
+
+ SLV2PluginClass pclass = slv2_plugin_get_class(p);
+ info->category = slv2_plugin_class_get_label(pclass);
+
+ char* author_name = slv2_plugin_get_author_name(p);
+ info->creator = author_name ? string(author_name) : "Unknown";
+ free(author_name);
+
+ info->path = "/NOPATH"; // Meaningless for LV2
+
+ SLV2Template io = slv2_plugin_get_template(p);
+
+ info->n_inputs = slv2_template_get_num_ports_of_type(io,
+ SLV2_PORT_DIRECTION_INPUT, SLV2_PORT_DATA_TYPE_AUDIO);
+
+ info->n_outputs = slv2_template_get_num_ports_of_type(io,
+ SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_AUDIO);
+
+ info->unique_id = slv2_plugin_get_uri(p);
+ info->index = 0; // Meaningless for LV2
+
+ plugs.push_back (info);
+ }
+
+ return plugs;
+}
+
diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc
index 4f14a7f65d..07afcbba51 100644
--- a/libs/ardour/plugin.cc
+++ b/libs/ardour/plugin.cc
@@ -46,6 +46,10 @@
#include <ardour/audio_unit.h>
#endif
+#ifdef HAVE_SLV2
+#include <ardour/lv2_plugin.h>
+#endif
+
#include <pbd/stl_delete.h>
#include "i18n.h"
@@ -314,6 +318,12 @@ ARDOUR::find_plugin(Session& session, string identifier, PluginType type)
case ARDOUR::LADSPA:
plugs = mgr->ladspa_plugin_info();
break;
+
+#ifdef HAVE_SLV2
+ case ARDOUR::LV2:
+ plugs = mgr->lv2_plugin_info();
+ break;
+#endif
#ifdef VST_SUPPORT
case ARDOUR::VST:
diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc
index 2b6c6e77dc..183948f373 100644
--- a/libs/ardour/plugin_manager.cc
+++ b/libs/ardour/plugin_manager.cc
@@ -36,6 +36,11 @@
#include <ardour/plugin.h>
#include <ardour/ladspa_plugin.h>
+#ifdef HAVE_SLV2
+#include <slv2/slv2.h>
+#include <ardour/lv2_plugin.h>
+#endif
+
#ifdef VST_SUPPORT
#include <ardour/vst_plugin.h>
#endif
@@ -101,6 +106,9 @@ PluginManager::PluginManager ()
ladspa_plugin_whitelist.push_back (2150); // tap pitch shifter
}
+ _lv2_world = slv2_world_new();
+ slv2_world_load_all(_lv2_world);
+
refresh ();
}
@@ -108,6 +116,9 @@ void
PluginManager::refresh ()
{
ladspa_refresh ();
+#ifdef HAVE_SLV2
+ lv2_refresh ();
+#endif
#ifdef VST_SUPPORT
if (Config->get_use_vst()) {
vst_refresh ();
@@ -342,6 +353,21 @@ PluginManager::get_ladspa_category (uint32_t plugin_id)
return label;
}
+#ifdef HAVE_SLV2
+void
+PluginManager::lv2_refresh ()
+{
+ lv2_discover();
+}
+
+int
+PluginManager::lv2_discover ()
+{
+ _lv2_plugin_info = LV2PluginInfo::discover(_lv2_world);
+ return 0;
+}
+#endif
+
#ifdef HAVE_AUDIOUNITS
void
PluginManager::au_refresh ()
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index 34602bf2da..f1953af386 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -1567,6 +1567,7 @@ Route::add_redirect_from_xml (const XMLNode& node)
bool have_insert = false;
if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
prop->value() == "vst" ||
prop->value() == "audiounit") {