Support for LV2 scale points (control port enumerations), ala LADSPA+LRDF.

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2977 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-01-30 01:29:07 +00:00
parent f52dee5a8b
commit 931b33567b
7 changed files with 205 additions and 83 deletions

View file

@ -531,12 +531,14 @@ if env['FFT_ANALYSIS']:
if env['LV2']:
conf = env.Configure(custom_tests = { 'CheckPKGExists' : CheckPKGExists })
if conf.CheckPKGExists ('\"slv2 >= 0.4.4\"'):
if conf.CheckPKGExists ('\"slv2 >= 0.6.0\"'):
libraries['slv2'] = LibraryInfo()
libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2')
else:
print 'Building Ardour with LV2 support requires SLV2 >= 0.4.4'
print 'Building Ardour with LV2 support requires SLV2 >= 0.6.0'
print 'WARNING: SLV2 not found, or too old. Ardour will be built without LV2 support.'
print 'Until the 2.3 release, Ardour requires SLV2 out of SVN.'
print 'Testing would be very much appreciated! svn co http://svn.drobilla.net/lad/slv2'
env['LV2'] = 0
conf.Finish()
else:

View file

@ -38,6 +38,7 @@
#include <ardour/plugin.h>
#include <ardour/insert.h>
#include <ardour/ladspa_plugin.h>
#include <ardour/lv2_plugin.h>
#include <lrdf.h>
@ -384,6 +385,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, PBD::Controllable* mcontr
if (plugin->parameter_is_input (port_index)) {
boost::shared_ptr<LadspaPlugin> lp;
boost::shared_ptr<LV2Plugin> lv2p;
if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
@ -407,6 +409,26 @@ GenericPluginUI::build_control_ui (guint32 port_index, PBD::Controllable* mcontr
lrdf_free_setting_values(defaults);
return control_ui;
}
} else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
SLV2Port port = lv2p->slv2_port(port_index);
SLV2ScalePoints points = slv2_port_get_scale_points(lv2p->slv2_plugin(), port);
if (points) {
control_ui->combo = new Gtk::ComboBoxText;
//control_ui->combo->set_value_in_list(true, false);
set_popdown_strings (*control_ui->combo, setup_scale_values(port_index, control_ui));
control_ui->combo->signal_changed().connect (bind (mem_fun(*this, &GenericPluginUI::control_combo_changed), control_ui));
plugin->ParameterChanged.connect (bind (mem_fun (*this, &GenericPluginUI::parameter_changed), control_ui));
control_ui->pack_start(control_ui->label, true, true);
control_ui->pack_start(*control_ui->combo, false, true);
update_control_display(control_ui);
slv2_scale_points_free(points);
return control_ui;
}
}
if (desc.toggled) {
@ -743,28 +765,49 @@ vector<string>
GenericPluginUI::setup_scale_values(guint32 port_index, ControlUI* cui)
{
vector<string> enums;
boost::shared_ptr<LadspaPlugin> lp = boost::dynamic_pointer_cast<LadspaPlugin> (plugin);
boost::shared_ptr<LadspaPlugin> lp;
boost::shared_ptr<LV2Plugin> lv2p;
if (!lp) {
return enums;
}
// all LADPSA plugins have a numeric unique ID
uint32_t id = atol (lp->unique_id().c_str());
if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
// all LADPSA plugins have a numeric unique ID
uint32_t id = atol (lp->unique_id().c_str());
cui->combo_map = new std::map<string, float>;
lrdf_defaults* defaults = lrdf_get_scale_values(id, port_index);
if (defaults) {
for (uint32_t i = 0; i < defaults->count; ++i) {
enums.push_back(defaults->items[i].label);
pair<string, float> newpair;
newpair.first = defaults->items[i].label;
newpair.second = defaults->items[i].value;
cui->combo_map->insert(newpair);
cui->combo_map = new std::map<string, float>;
lrdf_defaults* defaults = lrdf_get_scale_values(id, port_index);
if (defaults) {
for (uint32_t i = 0; i < defaults->count; ++i) {
enums.push_back(defaults->items[i].label);
pair<string, float> newpair;
newpair.first = defaults->items[i].label;
newpair.second = defaults->items[i].value;
cui->combo_map->insert(newpair);
}
lrdf_free_setting_values(defaults);
}
lrdf_free_setting_values(defaults);
} else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
SLV2Port port = lv2p->slv2_port(port_index);
SLV2ScalePoints points = slv2_port_get_scale_points(lv2p->slv2_plugin(), port);
cui->combo_map = new std::map<string, float>;
for (unsigned i=0; i < slv2_scale_points_size(points); ++i) {
SLV2ScalePoint p = slv2_scale_points_get_at(points, i);
SLV2Value label = slv2_scale_point_get_label(p);
SLV2Value value = slv2_scale_point_get_value(p);
if (label && (slv2_value_is_float(value) || slv2_value_is_int(value))) {
enums.push_back(slv2_value_as_string(label));
pair<string, float> newpair;
newpair.first = slv2_value_as_string(label);
newpair.second = slv2_value_as_float(value);
cui->combo_map->insert(newpair);
}
}
slv2_scale_points_free(points);
}
return enums;
}

View file

@ -37,20 +37,21 @@
namespace ARDOUR {
class AudioEngine;
class Session;
struct LV2World;
class LV2Plugin : public ARDOUR::Plugin
{
public:
LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, SLV2Plugin plugin, nframes_t sample_rate);
LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, ARDOUR::LV2World&, 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); }
const char* label() const { return slv2_value_as_string(_name); }
const char* name() const { return slv2_value_as_string(_name); }
const char* maker() const { return _author ? slv2_value_as_string(_author) : "Unknown"; }
uint32_t parameter_count() const { return slv2_plugin_get_num_ports(_plugin); }
float default_value (uint32_t port);
nframes_t latency() const;
@ -58,6 +59,9 @@ class LV2Plugin : public ARDOUR::Plugin
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;
SLV2Plugin slv2_plugin() { return _plugin; }
SLV2Port slv2_port(uint32_t i) { return slv2_plugin_get_port_by_index(_plugin, i); }
std::set<uint32_t> automatable() const;
@ -105,29 +109,55 @@ class LV2Plugin : public ARDOUR::Plugin
private:
void* _module;
LV2World& _world;
SLV2Plugin _plugin;
SLV2Template _template;
SLV2Value _name;
SLV2Value _author;
SLV2Instance _instance;
nframes_t _sample_rate;
float* _control_data;
float* _shadow_data;
float* _defaults;
float* _latency_control_port;
bool _was_activated;
vector<bool> _port_is_input;
void init (SLV2Plugin plugin, nframes_t rate);
void init (LV2World& world, SLV2Plugin plugin, nframes_t rate);
void run (nframes_t nsamples);
void latency_compute_run ();
};
/** The SLV2World, and various cached (as symbols, fast) URIs.
*
* This object represents everything ardour 'knows' about LV2
* (ie understood extensions/features/etc)
*/
struct LV2World {
LV2World();
~LV2World();
SLV2World world;
SLV2Value input_class;
SLV2Value output_class;
SLV2Value audio_class;
SLV2Value control_class;
SLV2Value in_place_broken;
SLV2Value integer;
SLV2Value toggled;
SLV2Value srate;
};
class LV2PluginInfo : public PluginInfo {
public:
LV2PluginInfo (void* slv2_plugin);;
LV2PluginInfo (void* slv2_world, void* slv2_plugin);;
~LV2PluginInfo ();;
static PluginInfoList discover (void* slv2_world);
PluginPtr load (Session& session);
void* _lv2_world;
void* _slv2_plugin;
};

View file

@ -28,7 +28,7 @@
#include <ardour/plugin.h>
#ifdef HAVE_SLV2
#include <slv2/slv2.h>
#include <ardour/lv2_plugin.h>
#endif
namespace ARDOUR {
@ -61,7 +61,7 @@ class PluginManager {
ARDOUR::PluginInfoList _au_plugin_info;
#ifdef HAVE_SLV2
SLV2World _lv2_world;
LV2World* _lv2_world;
#endif
std::map<uint32_t, std::string> rdf_type;

View file

@ -470,7 +470,6 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des
desc.label = port_names()[which];
return 0;
}

View file

@ -43,16 +43,18 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, SLV2Plugin plugin, nframes_t rate)
LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, LV2World& world, SLV2Plugin plugin, nframes_t rate)
: Plugin (e, session)
, _world(world)
{
init (plugin, rate);
init (world, plugin, rate);
}
LV2Plugin::LV2Plugin (const LV2Plugin &other)
: Plugin (other)
, _world(other._world)
{
init (other._plugin, other._sample_rate);
init (other._world, other._plugin, other._sample_rate);
for (uint32_t i = 0; i < parameter_count(); ++i) {
_control_data[i] = other._shadow_data[i];
@ -61,24 +63,30 @@ LV2Plugin::LV2Plugin (const LV2Plugin &other)
}
void
LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate)
{
_world = world;
_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);
_name = slv2_plugin_get_name(plugin);
assert(_name);
_author = slv2_plugin_get_author_name(plugin);
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;
if (slv2_plugin_has_feature(plugin, world.in_place_broken)) {
error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"),
slv2_value_as_string(_name));
slv2_value_free(_name);
slv2_value_free(_author);
throw failed_constructor();
}
@ -88,14 +96,21 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
_control_data = new float[num_ports];
_shadow_data = new float[num_ports];
_defaults = new float[num_ports];
const bool latent = slv2_plugin_has_latency(plugin);
uint32_t latency_port = (latent ? slv2_plugin_get_latency_port(plugin) : 0);
uint32_t latency_port = (latent ? slv2_plugin_get_latency_port_index(plugin) : 0);
for (uint32_t i = 0; i < num_ports; ++i) {
if (parameter_is_control(i)) {
SLV2Port port = slv2_plugin_get_port_by_index(plugin, i);
SLV2Value def;
slv2_port_get_range(plugin, port, &def, NULL, NULL);
_defaults[i] = def ? slv2_value_as_float(def) : 0.0f;
slv2_value_free(def);
slv2_instance_connect_port (_instance, i, &_control_data[i]);
if (latent && i == latency_port) {
_latency_control_port = &_control_data[i];
*_latency_control_port = 0;
@ -104,6 +119,8 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
if (parameter_is_input(i)) {
_shadow_data[i] = default_value (i);
}
} else {
_defaults[i] = 0.0f;
}
}
@ -120,6 +137,8 @@ LV2Plugin::~LV2Plugin ()
GoingAway (); /* EMIT SIGNAL */
slv2_instance_free(_instance);
slv2_value_free(_name);
slv2_value_free(_author);
if (_control_data) {
delete [] _control_data;
@ -133,15 +152,14 @@ LV2Plugin::~LV2Plugin ()
string
LV2Plugin::unique_id() const
{
return slv2_plugin_get_uri(_plugin);
return slv2_value_as_uri(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));
return _defaults[port];
}
void
@ -276,15 +294,16 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
{
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, which);
#define LV2_URI "http://lv2plug.in/ns/lv2core#"
SLV2Value def, min, max;
slv2_port_get_range(_plugin, port, &def, &min, &max);
desc.integer_step = slv2_port_has_property(_plugin, port, LV2_URI "integer");
desc.toggled = slv2_port_has_property(_plugin, port, LV2_URI "toggled");
desc.integer_step = slv2_port_has_property(_plugin, port, _world.integer);
desc.toggled = slv2_port_has_property(_plugin, port, _world.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.sr_dependent = slv2_port_has_property(_plugin, port, _world.srate);
desc.label = slv2_value_as_string(slv2_port_get_name(_plugin, port));
desc.lower = min ? slv2_value_as_float(min) : 0.0f;
desc.upper = max ? slv2_value_as_float(max) : 1.0f;
desc.min_unbound = false; // TODO (LV2 extension)
desc.max_unbound = false; // TODO (LV2 extension)
@ -299,6 +318,10 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
desc.largestep = delta/10.0f;
}
slv2_value_free(def);
slv2_value_free(min);
slv2_value_free(max);
return 0;
}
@ -307,8 +330,11 @@ 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));
SLV2Value name = slv2_port_get_name(_plugin,
slv2_plugin_get_port_by_index(_plugin, which));
string ret(slv2_value_as_string(name));
slv2_value_free(name);
return ret;
} else {
return "??";
}
@ -373,29 +399,29 @@ LV2Plugin::connect_and_run (vector<Sample*>& bufs, uint32_t nbufs, int32_t& in_i
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);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.control_class);
}
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);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.audio_class);
}
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);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.output_class);
}
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);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.input_class);
}
void
@ -414,9 +440,7 @@ 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) {
if (parameter_is_control(i) && parameter_is_input(i)) {
_control_data[i] = _shadow_data[i];
}
}
@ -468,8 +492,32 @@ LV2Plugin::latency_compute_run ()
deactivate ();
}
LV2PluginInfo::LV2PluginInfo (void* slv2_plugin)
: _slv2_plugin(slv2_plugin)
LV2World::LV2World()
: world(slv2_world_new())
{
slv2_world_load_all(world);
input_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_INPUT);
output_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_OUTPUT);
control_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_CONTROL);
audio_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_AUDIO);
in_place_broken = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "inPlaceBroken");
integer = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "integer");
toggled = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "toggled");
srate = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "sampleRate");
}
LV2World::~LV2World()
{
slv2_value_free(input_class);
slv2_value_free(output_class);
slv2_value_free(control_class);
slv2_value_free(audio_class);
slv2_value_free(in_place_broken);
}
LV2PluginInfo::LV2PluginInfo (void* lv2_world, void* slv2_plugin)
: _lv2_world(lv2_world)
, _slv2_plugin(slv2_plugin)
{
}
@ -480,12 +528,11 @@ 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.reset (new LV2Plugin (session.engine(), session,
*(LV2World*)_lv2_world, (SLV2Plugin)_slv2_plugin, session.frame_rate()));
plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this)));
return plugin;
@ -499,36 +546,38 @@ LV2PluginInfo::load (Session& session)
}
PluginInfoList
LV2PluginInfo::discover (void* slv2_world)
LV2PluginInfo::discover (void* lv2_world)
{
PluginInfoList plugs;
SLV2Plugins plugins = slv2_world_get_all_plugins((SLV2World)slv2_world);
LV2World* world = (LV2World*)lv2_world;
SLV2Plugins plugins = slv2_world_get_all_plugins(world->world);
for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) {
SLV2Plugin p = slv2_plugins_get_at(plugins, i);
LV2PluginInfoPtr info (new LV2PluginInfo(p));
LV2PluginInfoPtr info (new LV2PluginInfo(lv2_world, p));
info->name = slv2_plugin_get_name(p);
SLV2Value name = slv2_plugin_get_name(p);
info->name = string(slv2_value_as_string(name));
slv2_value_free(name);
SLV2PluginClass pclass = slv2_plugin_get_class(p);
info->category = slv2_plugin_class_get_label(pclass);
SLV2Value label = slv2_plugin_class_get_label(pclass);
info->category = slv2_value_as_string(label);
char* author_name = slv2_plugin_get_author_name(p);
info->creator = author_name ? string(author_name) : "Unknown";
free(author_name);
SLV2Value author_name = slv2_plugin_get_author_name(p);
info->creator = author_name ? string(slv2_value_as_string(author_name)) : "Unknown";
slv2_value_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_inputs = slv2_plugin_get_num_ports_of_class(p,
world->input_class, world->audio_class, NULL);
info->n_outputs = slv2_template_get_num_ports_of_type(io,
SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_AUDIO);
info->n_outputs = slv2_plugin_get_num_ports_of_class(p,
world->output_class, world->audio_class, NULL);
info->unique_id = slv2_plugin_get_uri(p);
info->unique_id = slv2_value_as_uri(slv2_plugin_get_uri(p));
info->index = 0; // Meaningless for LV2
plugs.push_back (info);

View file

@ -107,8 +107,7 @@ PluginManager::PluginManager ()
}
#ifdef HAVE_SLV2
_lv2_world = slv2_world_new();
slv2_world_load_all(_lv2_world);
_lv2_world = new LV2World();
#endif
refresh ();