X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fladspa_plugin.cc;h=bc3a83799bd45356424107c5b1d13509b530c7dc;hb=16a8762dd494adb68756b8d549f349ed7bdccd9b;hp=29f2d16767909392d1b02a75423dbd31ab8158ea;hpb=9f63ab9931e6478472853bdda58da47ea29ac125;p=ardour.git diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 29f2d16767..bc3a83799b 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2006 Paul Davis + Copyright (C) 2000-2006 Paul Davis 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 @@ -17,7 +17,6 @@ */ -#define __STDC_FORMAT_MACROS 1 #include #include @@ -32,19 +31,17 @@ #include -#include -#include -#include +#include "pbd/compose.h" +#include "pbd/error.h" +#include "pbd/xml++.h" +#include "pbd/stacktrace.h" -#include +#include "ardour/session.h" +#include "ardour/ladspa_plugin.h" +#include "ardour/buffer_set.h" +#include "ardour/audio_buffer.h" -#include -#include -#include -#include -#include - -#include +#include "pbd/stl_delete.h" #include "i18n.h" #include @@ -53,7 +50,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -LadspaPlugin::LadspaPlugin (void *mod, AudioEngine& e, Session& session, uint32_t index, nframes_t rate) +LadspaPlugin::LadspaPlugin (void *mod, AudioEngine& e, Session& session, uint32_t index, framecnt_t rate) : Plugin (e, session) { init (mod, index, rate); @@ -71,7 +68,7 @@ LadspaPlugin::LadspaPlugin (const LadspaPlugin &other) } void -LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate) +LadspaPlugin::init (void *mod, uint32_t index, framecnt_t rate) { LADSPA_Descriptor_Function dfunc; uint32_t i, port_cnt; @@ -101,7 +98,7 @@ LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate) error << string_compose(_("LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"), _descriptor->Name) << endmsg; throw failed_constructor(); } - + _sample_rate = rate; if (_descriptor->instantiate == 0) { @@ -115,12 +112,14 @@ LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate) port_cnt = parameter_count(); _control_data = new LADSPA_Data[port_cnt]; + memset (_control_data, 0, sizeof (LADSPA_Data) * port_cnt); _shadow_data = new LADSPA_Data[port_cnt]; + memset (_shadow_data, 0, sizeof (LADSPA_Data) * port_cnt); for (i = 0; i < port_cnt; ++i) { if (LADSPA_IS_PORT_CONTROL(port_descriptor (i))) { connect_port (i, &_control_data[i]); - + if (LADSPA_IS_PORT_OUTPUT(port_descriptor (i)) && strcmp (port_names()[i], X_("latency")) == 0) { _latency_control_port = &_control_data[i]; @@ -130,7 +129,7 @@ LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate) if (!LADSPA_IS_PORT_INPUT(port_descriptor (i))) { continue; } - + _shadow_data[i] = default_value (i); } } @@ -143,19 +142,12 @@ LadspaPlugin::~LadspaPlugin () deactivate (); cleanup (); - GoingAway (); /* EMIT SIGNAL */ - /* XXX who should close a plugin? */ // dlclose (module); - if (_control_data) { - delete [] _control_data; - } - - if (_shadow_data) { - delete [] _shadow_data; - } + delete [] _control_data; + delete [] _shadow_data; } string @@ -176,40 +168,33 @@ LadspaPlugin::default_value (uint32_t port) bool earlier_hint = false; /* defaults - case 1 */ - + if (LADSPA_IS_HINT_HAS_DEFAULT(prh[port].HintDescriptor)) { if (LADSPA_IS_HINT_DEFAULT_MINIMUM(prh[port].HintDescriptor)) { ret = prh[port].LowerBound; bounds_given = true; sr_scaling = true; - earlier_hint = true; } - - /* FIXME: add support for logarithmic defaults */ - + else if (LADSPA_IS_HINT_DEFAULT_LOW(prh[port].HintDescriptor)) { ret = prh[port].LowerBound * 0.75f + prh[port].UpperBound * 0.25f; bounds_given = true; sr_scaling = true; - earlier_hint = true; } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(prh[port].HintDescriptor)) { - ret = prh[port].LowerBound * 0.50f + prh[port].UpperBound * 0.50f; + ret = prh[port].LowerBound * 0.5f + prh[port].UpperBound * 0.5f; bounds_given = true; sr_scaling = true; - earlier_hint = true; } else if (LADSPA_IS_HINT_DEFAULT_HIGH(prh[port].HintDescriptor)) { ret = prh[port].LowerBound * 0.25f + prh[port].UpperBound * 0.75f; bounds_given = true; sr_scaling = true; - earlier_hint = true; } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(prh[port].HintDescriptor)) { ret = prh[port].UpperBound; bounds_given = true; sr_scaling = true; - earlier_hint = true; } else if (LADSPA_IS_HINT_DEFAULT_0(prh[port].HintDescriptor)) { ret = 0.0f; @@ -232,11 +217,11 @@ LadspaPlugin::default_value (uint32_t port) ret = 0.0f; } } - + /* defaults - case 2 */ else if (LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) && !LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) { - + if (prh[port].LowerBound < 0) { ret = 0.0f; } else { @@ -246,11 +231,11 @@ LadspaPlugin::default_value (uint32_t port) bounds_given = true; sr_scaling = true; } - + /* defaults - case 3 */ else if (!LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) && LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) { - + if (prh[port].UpperBound > 0) { ret = 0.0f; } else { @@ -260,11 +245,11 @@ LadspaPlugin::default_value (uint32_t port) bounds_given = true; sr_scaling = true; } - + /* defaults - case 4 */ else if (LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) && LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) { - + if (prh[port].LowerBound < 0 && prh[port].UpperBound > 0) { ret = 0.0f; } else if (prh[port].LowerBound < 0 && prh[port].UpperBound < 0) { @@ -272,12 +257,12 @@ LadspaPlugin::default_value (uint32_t port) } else { ret = prh[port].LowerBound; } - bounds_given = true; + bounds_given = true; sr_scaling = true; } - + /* defaults - case 5 */ - + if (LADSPA_IS_HINT_SAMPLE_RATE(prh[port].HintDescriptor) && !earlier_hint) { if (bounds_given) { if (sr_scaling) { @@ -289,29 +274,36 @@ LadspaPlugin::default_value (uint32_t port) } return ret; -} +} void LadspaPlugin::set_parameter (uint32_t which, float val) { if (which < _descriptor->PortCount) { + + if (get_parameter (which) == val) { + return; + } + _shadow_data[which] = (LADSPA_Data) val; -#if 0 - ParameterChanged (Parameter(PluginAutomation, which), val); /* EMIT SIGNAL */ +#if 0 if (which < parameter_count() && controls[which]) { controls[which]->Changed (); } #endif - + } else { - warning << string_compose (_("illegal parameter number used with plugin \"%1\". This may" - "indicate a change in the plugin design, and presets may be" + warning << string_compose (_("illegal parameter number used with plugin \"%1\". This may " + "indicate a change in the plugin design, and presets may be " "invalid"), name()) << endmsg; } + + Plugin::set_parameter (which, val); } +/** @return `plugin' value */ float LadspaPlugin::get_parameter (uint32_t which) const { @@ -340,20 +332,19 @@ LadspaPlugin::nth_parameter (uint32_t n, bool& ok) const return 0; } -XMLNode& -LadspaPlugin::get_state() +void +LadspaPlugin::add_state (XMLNode* root) const { - 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 (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && + if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))){ - child = new XMLNode("port"); + child = new XMLNode("Port"); snprintf(buf, sizeof(buf), "%u", i); child->add_property("number", string(buf)); snprintf(buf, sizeof(buf), "%+f", _shadow_data[i]); @@ -361,19 +352,66 @@ LadspaPlugin::get_state() root->add_child_nocopy (*child); } } - - return *root; } -bool -LadspaPlugin::save_preset (string name) +int +LadspaPlugin::set_state (const XMLNode& node, int version) { - return Plugin::save_preset (name, "ladspa"); + if (version < 3000) { + return set_state_2X (node, version); + } + +#ifndef NO_PLUGIN_STATE + XMLNodeList nodes; + XMLProperty *prop; + XMLNodeConstIterator iter; + XMLNode *child; + const char *port; + const char *data; + uint32_t port_id; +#endif + LocaleGuard lg (X_("POSIX")); + + if (node.name() != state_node_name()) { + error << _("Bad node sent to LadspaPlugin::set_state") << endmsg; + return -1; + } + +#ifndef NO_PLUGIN_STATE + + 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 << _("LADSPA: no ladspa port number") << endmsg; + continue; + } + if ((prop = child->property("value")) != 0) { + data = prop->value().c_str(); + } else { + warning << _("LADSPA: no ladspa port data") << endmsg; + continue; + } + + sscanf (port, "%" PRIu32, &port_id); + set_parameter (port_id, atof(data)); + } +#endif + + latency_compute_run (); + + return Plugin::set_state (node, version); } int -LadspaPlugin::set_state(const XMLNode& node) +LadspaPlugin::set_state_2X (const XMLNode& node, int /* version */) { +#ifndef NO_PLUGIN_STATE XMLNodeList nodes; XMLProperty *prop; XMLNodeConstIterator iter; @@ -381,6 +419,7 @@ LadspaPlugin::set_state(const XMLNode& node) const char *port; const char *data; uint32_t port_id; +#endif LocaleGuard lg (X_("POSIX")); if (node.name() != state_node_name()) { @@ -388,6 +427,7 @@ LadspaPlugin::set_state(const XMLNode& node) return -1; } +#ifndef NO_PLUGIN_STATE nodes = node.children ("port"); for(iter = nodes.begin(); iter != nodes.end(); ++iter){ @@ -412,6 +452,7 @@ LadspaPlugin::set_state(const XMLNode& node) } latency_compute_run (); +#endif return 0; } @@ -422,7 +463,7 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des LADSPA_PortRangeHint prh; prh = port_range_hints()[which]; - + if (LADSPA_IS_HINT_BOUNDED_BELOW(prh.HintDescriptor)) { desc.min_unbound = false; @@ -435,7 +476,7 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des desc.min_unbound = true; desc.lower = 0; } - + if (LADSPA_IS_HINT_BOUNDED_ABOVE(prh.HintDescriptor)) { desc.max_unbound = false; @@ -448,7 +489,7 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des desc.max_unbound = true; desc.upper = 4; /* completely arbitrary */ } - + if (LADSPA_IS_HINT_INTEGER (prh.HintDescriptor)) { desc.step = 1.0; desc.smallstep = 0.1; @@ -459,7 +500,7 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des desc.smallstep = delta / 10000.0f; desc.largestep = delta/10.0f; } - + desc.toggled = LADSPA_IS_HINT_TOGGLED (prh.HintDescriptor); desc.logarithmic = LADSPA_IS_HINT_LOGARITHMIC (prh.HintDescriptor); desc.sr_dependent = LADSPA_IS_HINT_SAMPLE_RATE (prh.HintDescriptor); @@ -471,7 +512,7 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des } string -LadspaPlugin::describe_parameter (Parameter which) +LadspaPlugin::describe_parameter (Evoral::Parameter which) { if (which.type() == PluginAutomation && which.id() < parameter_count()) { return port_names()[which.id()]; @@ -480,7 +521,7 @@ LadspaPlugin::describe_parameter (Parameter which) } } -ARDOUR::nframes_t +ARDOUR::framecnt_t LadspaPlugin::signal_latency () const { if (_user_latency) { @@ -488,22 +529,22 @@ LadspaPlugin::signal_latency () const } if (_latency_control_port) { - return (nframes_t) floor (*_latency_control_port); + return (framecnt_t) floor (*_latency_control_port); } else { return 0; } } -set +set LadspaPlugin::automatable () const { - set ret; + set ret; for (uint32_t i = 0; i < parameter_count(); ++i){ - if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && + if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))){ - - ret.insert (ret.end(), Parameter(PluginAutomation, i)); + + ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); } } @@ -511,36 +552,37 @@ LadspaPlugin::automatable () const } int -LadspaPlugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset) +LadspaPlugin::connect_and_run (BufferSet& bufs, + ChanMapping in_map, ChanMapping out_map, + pframes_t nframes, framecnt_t offset) { - uint32_t port_index = 0; - cycles_t then, now; - - then = get_cycles (); - - const uint32_t nbufs = bufs.count().n_audio(); - - while (port_index < parameter_count()) { - if (LADSPA_IS_PORT_AUDIO (port_descriptor(port_index))) { - if (LADSPA_IS_PORT_INPUT (port_descriptor(port_index))) { - const size_t index = min(in_index, nbufs - 1); - connect_port (port_index, bufs.get_audio(index).data(nframes, offset)); - //cerr << this << ' ' << name() << " @ " << offset << " inport " << in_index << " = buf " - // << min((uint32_t)in_index,nbufs) << " = " << &bufs[min((uint32_t)in_index,nbufs)][offset] << endl; - in_index++; - - - } else if (LADSPA_IS_PORT_OUTPUT (port_descriptor (port_index))) { - const size_t index = min(out_index,nbufs - 1); - connect_port (port_index, bufs.get_audio(index).data(nframes, offset)); - // cerr << this << ' ' << name() << " @ " << offset << " outport " << out_index << " = buf " - // << min((uint32_t)out_index,nbufs) << " = " << &bufs[min((uint32_t)out_index,nbufs)][offset] << endl; - out_index++; + Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset); + + cycles_t now; + cycles_t then = get_cycles (); + + BufferSet& silent_bufs = _session.get_silent_buffers(ChanCount(DataType::AUDIO, 1)); + BufferSet& scratch_bufs = _session.get_scratch_buffers(ChanCount(DataType::AUDIO, 1)); + + uint32_t audio_in_index = 0; + uint32_t audio_out_index = 0; + bool valid; + for (uint32_t port_index = 0; port_index < parameter_count(); ++port_index) { + if (LADSPA_IS_PORT_AUDIO(port_descriptor(port_index))) { + if (LADSPA_IS_PORT_INPUT(port_descriptor(port_index))) { + const uint32_t buf_index = in_map.get(DataType::AUDIO, audio_in_index++, &valid); + connect_port(port_index, + valid ? bufs.get_audio(buf_index).data(offset) + : silent_bufs.get_audio(0).data(offset)); + } else if (LADSPA_IS_PORT_OUTPUT(port_descriptor(port_index))) { + const uint32_t buf_index = out_map.get(DataType::AUDIO, audio_out_index++, &valid); + connect_port(port_index, + valid ? bufs.get_audio(buf_index).data(offset) + : scratch_bufs.get_audio(0).data(offset)); } } - port_index++; } - + run_in_place (nframes); now = get_cycles (); set_cycles ((uint32_t) (now - then)); @@ -584,14 +626,39 @@ LadspaPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const } } +boost::shared_ptr +LadspaPlugin::get_scale_points(uint32_t port_index) const +{ + const uint32_t id = atol(unique_id().c_str()); + lrdf_defaults* points = lrdf_get_scale_values(id, port_index); + + boost::shared_ptr ret; + if (!points) { + return ret; + } + + ret = boost::shared_ptr(new ScalePoints()); + + for (uint32_t i = 0; i < points->count; ++i) { + ret->insert(make_pair(points->items[i].label, + points->items[i].value)); + } + + lrdf_free_setting_values(points); + return ret; +} + void -LadspaPlugin::run_in_place (nframes_t nframes) +LadspaPlugin::run_in_place (pframes_t nframes) { for (uint32_t i = 0; i < parameter_count(); ++i) { if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))) { _control_data[i] = _shadow_data[i]; } } + + assert (_was_activated); + _descriptor->run (_handle, nframes); } @@ -605,23 +672,23 @@ LadspaPlugin::latency_compute_run () /* 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; + const framecnt_t bufsize = 1024; LADSPA_Data buffer[bufsize]; memset(buffer,0,sizeof(LADSPA_Data)*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 (LADSPA_IS_PORT_AUDIO (port_descriptor (port_index))) { if (LADSPA_IS_PORT_INPUT (port_descriptor (port_index))) { @@ -634,7 +701,7 @@ LadspaPlugin::latency_compute_run () } port_index++; } - + run_in_place (bufsize); deactivate (); } @@ -649,6 +716,7 @@ LadspaPluginInfo::load (Session& session) if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) { error << string_compose(_("LADSPA: cannot load module from \"%1\""), path) << endmsg; error << dlerror() << endmsg; + return PluginPtr ((Plugin*) 0); } else { plugin.reset (new LadspaPlugin (module, session.engine(), session, index, session.frame_rate())); } @@ -659,5 +727,218 @@ LadspaPluginInfo::load (Session& session) catch (failed_constructor &err) { return PluginPtr ((Plugin*) 0); - } + } +} + +LadspaPluginInfo::LadspaPluginInfo() +{ + type = ARDOUR::LADSPA; +} + + +void +LadspaPlugin::find_presets () +{ + uint32_t id; + std::string unique (unique_id()); + + if (!isdigit (unique[0])) { + return; + } + + id = atol (unique.c_str()); + + lrdf_uris* set_uris = lrdf_get_setting_uris(id); + + if (set_uris) { + for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) { + if (char* label = lrdf_get_label(set_uris->items[i])) { + PresetRecord rec (set_uris->items[i], label); + _presets.insert (make_pair (set_uris->items[i], rec)); + } + } + lrdf_free_uris(set_uris); + } +} + + +bool +LadspaPlugin::load_preset (PresetRecord r) +{ + lrdf_defaults* defs = lrdf_get_setting_values (r.uri.c_str()); + + if (defs) { + for (uint32_t i = 0; i < (uint32_t) defs->count; ++i) { + if (parameter_is_input (defs->items[i].pid)) { + set_parameter(defs->items[i].pid, defs->items[i].value); + } + } + lrdf_free_setting_values(defs); + } + + Plugin::load_preset (r); + return true; +} + +/* XXX: should be in liblrdf */ +static void +lrdf_remove_preset (const char* /*source*/, const char *setting_uri) +{ + lrdf_statement p; + lrdf_statement *q; + lrdf_statement *i; + char setting_uri_copy[64]; + char buf[64]; + + strncpy(setting_uri_copy, setting_uri, sizeof(setting_uri_copy)); + + p.subject = setting_uri_copy; + strncpy(buf, LADSPA_BASE "hasPortValue", sizeof(buf)); + p.predicate = buf; + p.object = NULL; + q = lrdf_matches(&p); + + p.predicate = NULL; + p.object = NULL; + for (i = q; i; i = i->next) { + p.subject = i->object; + lrdf_remove_matches(&p); + } + + lrdf_free_statements(q); + + p.subject = NULL; + strncpy(buf, LADSPA_BASE "hasSetting", sizeof(buf)); + p.predicate = buf; + p.object = setting_uri_copy; + lrdf_remove_matches(&p); + + p.subject = setting_uri_copy; + p.predicate = NULL; + p.object = NULL; + lrdf_remove_matches (&p); +} + +void +LadspaPlugin::do_remove_preset (string name) +{ + string const envvar = preset_envvar (); + if (envvar.empty()) { + warning << _("Could not locate HOME. Preset not removed.") << endmsg; + return; + } + + Plugin::PresetRecord const * p = preset_by_label (name); + if (!p) { + return; + } + + string const source = preset_source (envvar); + lrdf_remove_preset (source.c_str(), p->uri.c_str ()); + + write_preset_file (envvar); +} + +string +LadspaPlugin::preset_envvar () const +{ + char* envvar; + if ((envvar = getenv ("HOME")) == 0) { + return ""; + } + + return envvar; +} + +string +LadspaPlugin::preset_source (string envvar) const +{ + return string_compose ("file:%1/.ladspa/rdf/ardour-presets.n3", envvar); +} + +bool +LadspaPlugin::write_preset_file (string envvar) +{ + string path = string_compose("%1/.ladspa", envvar); + if (g_mkdir_with_parents (path.c_str(), 0775)) { + warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; + return false; + } + + path += "/rdf"; + if (g_mkdir_with_parents (path.c_str(), 0775)) { + warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg; + return false; + } + + string const source = preset_source (envvar); + + if (lrdf_export_by_source (source.c_str(), source.substr(5).c_str())) { + warning << string_compose(_("Error saving presets file %1."), source) << endmsg; + return false; + } + + return true; +} + +string +LadspaPlugin::do_save_preset (string name) +{ + /* make a vector of pids that are input parameters */ + vector input_parameter_pids; + for (uint32_t i = 0; i < parameter_count(); ++i) { + if (parameter_is_input (i)) { + input_parameter_pids.push_back (i); + } + } + + std::string unique (unique_id()); + + if (!isdigit (unique[0])) { + return ""; + } + + uint32_t const id = atol (unique.c_str()); + + lrdf_defaults defaults; + defaults.count = input_parameter_pids.size (); + lrdf_portvalue portvalues[input_parameter_pids.size()]; + defaults.items = portvalues; + + for (vector::size_type i = 0; i < input_parameter_pids.size(); ++i) { + portvalues[i].pid = input_parameter_pids[i]; + portvalues[i].value = get_parameter (input_parameter_pids[i]); + } + + string const envvar = preset_envvar (); + if (envvar.empty()) { + warning << _("Could not locate HOME. Preset not saved.") << endmsg; + return ""; + } + + string const source = preset_source (envvar); + + char* uri_char = lrdf_add_preset (source.c_str(), name.c_str(), id, &defaults); + string uri (uri_char); + free (uri_char); + + if (!write_preset_file (envvar)) { + return ""; + } + + return uri; +} + +LADSPA_PortDescriptor +LadspaPlugin::port_descriptor (uint32_t i) const +{ + if (i < _descriptor->PortCount) { + return _descriptor->PortDescriptors[i]; + } + + warning << "LADSPA plugin port index " << i << " out of range." << endmsg; + return 0; } + + +