X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fladspa_plugin.cc;h=be3fefdaaca7a2feefb40035c7e36e98df750bfd;hb=7b818e9a7f3d999eb6bcc90c961d2b42531c3917;hp=ea8b4314813d644a6e4ca2d7f6b939b0cf02ea4a;hpb=bed58e9f372a6c2671e9f072c19a4c77d06c4292;p=ardour.git diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index ea8b431481..be3fefdaac 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,20 +31,18 @@ #include -#include -#include -#include +#include "pbd/compose.h" +#include "pbd/error.h" +#include "pbd/xml++.h" -#include +#include "midi++/manager.h" -#include -#include -#include -#include -#include -#include +#include "ardour/session.h" +#include "ardour/ladspa_plugin.h" +#include "ardour/buffer_set.h" +#include "ardour/audio_buffer.h" -#include +#include "pbd/stl_delete.h" #include "i18n.h" #include @@ -54,7 +51,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); @@ -72,7 +69,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; @@ -102,7 +99,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) { @@ -121,7 +118,7 @@ LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate) 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]; @@ -131,7 +128,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); } } @@ -144,8 +141,6 @@ LadspaPlugin::~LadspaPlugin () deactivate (); cleanup (); - GoingAway (); /* EMIT SIGNAL */ - /* XXX who should close a plugin? */ // dlclose (module); @@ -172,40 +167,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; @@ -228,11 +216,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 { @@ -242,11 +230,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 { @@ -256,11 +244,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) { @@ -268,12 +256,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) { @@ -285,29 +273,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, 0, 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" "invalid"), name()) << endmsg; } + + Plugin::set_parameter (which, val); } +/** @return `plugin' value */ float LadspaPlugin::get_parameter (uint32_t which) const { @@ -336,20 +331,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]); @@ -357,18 +351,59 @@ 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); + } + + 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 LadspaPlugin::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 << _("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)); + } + + 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 */) { XMLNodeList nodes; XMLProperty *prop; @@ -418,7 +453,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; @@ -431,7 +466,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; @@ -444,7 +479,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; @@ -455,7 +490,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); @@ -476,7 +511,7 @@ LadspaPlugin::describe_parameter (Evoral::Parameter which) } } -ARDOUR::nframes_t +ARDOUR::framecnt_t LadspaPlugin::signal_latency () const { if (_user_latency) { @@ -484,7 +519,7 @@ 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; } @@ -496,9 +531,9 @@ LadspaPlugin::automatable () const 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(), Evoral::Parameter(PluginAutomation, 0, i)); } } @@ -507,36 +542,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_silent_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)); @@ -580,14 +616,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); } @@ -601,23 +662,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))) { @@ -630,7 +691,7 @@ LadspaPlugin::latency_compute_run () } port_index++; } - + run_in_place (bufsize); deactivate (); } @@ -645,6 +706,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())); } @@ -655,5 +717,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; +} + + +