X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fplugin_insert.cc;h=8a5919ab5a40d5c72b6530b1cd83c62d71b61f73;hb=2b3adfb18f0c2befb39b8b56d3e3f07833cc7b33;hp=6786f7a4f67aa377badd8b7c957c3796517e4822;hpb=2bd721d1ccb35a095d7e2b35f069abc14b79ed30;p=ardour.git diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 6786f7a4f6..8a5919ab5a 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -26,6 +26,7 @@ #include "pbd/failed_constructor.h" #include "pbd/xml++.h" +#include "pbd/convert.h" #include "ardour/audio_buffer.h" #include "ardour/automation_list.h" @@ -65,6 +66,7 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr plug) : Processor (s, (plug ? plug->name() : string ("toBeRenamed"))) , _signal_analysis_collected_nframes(0) , _signal_analysis_collect_nframes_max(0) + , _splitting (false) { /* the first is the master */ @@ -146,7 +148,19 @@ PluginInsert::input_streams() const { ChanCount in = _plugins[0]->get_info()->n_inputs; - if (in == ChanCount::INFINITE) { + if (_splitting) { + + /* we are splitting 1 processor input to multiple plugin inputs, + so we have a maximum of 1 stream of each type. + */ + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + if (in.get (*t) > 1) { + in.set (*t, 1); + } + } + return in; + + } else if (in == ChanCount::INFINITE) { return _plugins[0]->input_streams (); } else { in.set_audio (in.n_audio() * _plugins.size()); @@ -190,7 +204,7 @@ PluginInsert::set_automatable () Evoral::Parameter param(*i); _plugins.front()->get_parameter_descriptor(i->id(), desc); - + /* the Parameter belonging to the actual plugin doesn't have its range set but we want the Controllable related to this Parameter to have those limits. */ @@ -222,7 +236,7 @@ PluginInsert::parameter_changed (Evoral::Parameter which, float val) } int -PluginInsert::set_block_size (nframes_t nframes) +PluginInsert::set_block_size (pframes_t nframes) { int ret = 0; for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { @@ -262,11 +276,11 @@ PluginInsert::flush () } void -PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now) +PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now) { // Calculate if, and how many frames we need to collect for analysis - nframes_t collect_signal_nframes = (_signal_analysis_collect_nframes_max - - _signal_analysis_collected_nframes); + framecnt_t collect_signal_nframes = (_signal_analysis_collect_nframes_max - + _signal_analysis_collected_nframes); if (nframes < collect_signal_nframes) { // we might not get all frames now collect_signal_nframes = nframes; } @@ -274,6 +288,18 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off ChanMapping in_map(input_streams()); ChanMapping out_map(output_streams()); + if (_splitting) { + /* fix the input mapping so that we have maps for each of the plugin's inputs */ + in_map = ChanMapping (natural_input_streams ()); + + /* copy the first stream's buffer contents to the others */ + /* XXX: audio only */ + Sample const * mono = bufs.get_audio (in_map.get (DataType::AUDIO, 0)).data (offset); + for (uint32_t i = input_streams().n_audio(); i < natural_input_streams().n_audio(); ++i) { + memcpy (bufs.get_audio (in_map.get (DataType::AUDIO, i)).data() + offset, mono + offset, sizeof (Sample) * (nframes - offset)); + } + } + /* Note that we've already required that plugins be able to handle in-place processing. */ @@ -354,20 +380,27 @@ PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t off } void -PluginInsert::silence (nframes_t nframes) +PluginInsert::silence (framecnt_t nframes) { + if (!active ()) { + return; + } + ChanMapping in_map(input_streams()); ChanMapping out_map(output_streams()); - if (active()) { - for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { - (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_map, out_map, nframes, 0); - } + if (_splitting) { + /* fix the input mapping so that we have maps for each of the plugin's inputs */ + in_map = ChanMapping (natural_input_streams ()); + } + + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_map, out_map, nframes, 0); } } void -PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool) +PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool) { if (_pending_active) { /* run as normal if we are active or moving from inactive to active */ @@ -436,12 +469,12 @@ PluginInsert::get_parameter (Evoral::Parameter param) } void -PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes) +PluginInsert::automation_run (BufferSet& bufs, pframes_t nframes) { Evoral::ControlEvent next_event (0, 0.0f); - nframes_t now = _session.transport_frame (); - nframes_t end = now + nframes; - nframes_t offset = 0; + framepos_t now = _session.transport_frame (); + framepos_t end = now + nframes; + framecnt_t offset = 0; Glib::Mutex::Lock lm (control_lock(), Glib::TRY_LOCK); @@ -460,7 +493,7 @@ PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes) while (nframes) { - nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes); + framecnt_t cnt = min (((framecnt_t) ceil (next_event.when) - now), (framecnt_t) nframes); connect_and_run (bufs, cnt, offset, true, now); @@ -535,19 +568,22 @@ PluginInsert::plugin_factory (boost::shared_ptr other) bool PluginInsert::configure_io (ChanCount in, ChanCount out) { - if (set_count (count_for_configuration (in, out)) < 0) { + if (set_count (count_for_configuration (in, out)) == false) { + set_splitting (false); return false; } - /* if we're running replicated plugins, each plugin has - the same i/o configuration and we may need to announce how many - output streams there are. - - if we running a single plugin, we need to configure it. - */ - - if (_plugins.front()->configure_io (in, out) < 0) { - return false; + if (_plugins.front()->get_info()->n_inputs <= in) { + set_splitting (false); + if (_plugins.front()->configure_io (in, out) == false) { + return false; + } + } else { + /* we must be splitting a single processor input to + multiple plugin inputs + */ + set_splitting (true); + _plugins.front()->configure_io (_plugins.front()->get_info()->n_inputs, out); } // we don't know the analysis window size, so we must work with the @@ -583,35 +619,32 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out) } // See if replication is possible - // We can replicate if there exists a single factor f such that, for every type, - // the number of plugin inputs * f = the requested number of inputs + // We allow replication only for plugins with either zero or 1 inputs and outputs + // for every valid data type. uint32_t f = 0; bool can_replicate = true; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { // No inputs of this type if (inputs.get(*t) == 0 && in.get(*t) == 0) { continue; + } - // Plugin has more inputs than requested, can not replicate - } else if (inputs.get(*t) >= in.get(*t)) { - can_replicate = false; - break; + if (inputs.get(*t) != 1 || outputs.get (*t) != 1) { + can_replicate = false; + break; + } - // Plugin inputs is not a factor of requested inputs, can not replicate - } else if (inputs.get(*t) == 0 || in.get(*t) % inputs.get(*t) != 0) { - can_replicate = false; - break; + // Potential factor not set yet - // Potential factor not set yet - } else if (f == 0) { - f = in.get(*t) / inputs.get(*t);; - } + if (f == 0) { + f = in.get(*t) / inputs.get(*t);; + } - // Factor for this type does not match another type, can not replicate - if (f != (in.get(*t) / inputs.get(*t))) { - can_replicate = false; - break; - } + // Factor for this type does not match another type, can not replicate + if (f != (in.get(*t) / inputs.get(*t))) { + can_replicate = false; + break; + } } if (can_replicate) { @@ -619,9 +652,33 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out) out.set (*t, outputs.get(*t) * f); } return true; - } else { - return false; } + + /* If the processor has exactly one input of a given type, and + the plugin has more, we can feed the single processor input + to some or all of the plugin inputs. This is rather + special-case-y, but the 1-to-many case is by far the + simplest. How do I split thy 2 processor inputs to 3 + plugin inputs? Let me count the ways ... + */ + + bool can_split = true; + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + + bool const can_split_type = (in.get (*t) == 1 && inputs.get (*t) > 1); + bool const nothing_to_do_for_type = (in.get (*t) == 0 && inputs.get (*t) == 0); + + if (!can_split_type && !nothing_to_do_for_type) { + can_split = false; + } + } + + if (can_split) { + out = outputs; + return true; + } + + return false; } /* Number of plugin instances required to support a given channel configuration. @@ -658,6 +715,11 @@ PluginInsert::count_for_configuration (ChanCount in, ChanCount /*out*/) const return 1; } + if (inputs > in) { + /* more plugin inputs than processor inputs, so we are splitting */ + return 1; + } + // assumes in is valid, so we must be replicating if (inputs.n_total() < in.n_total() && (in.n_total() % inputs.n_total() == 0)) { @@ -695,6 +757,31 @@ PluginInsert::state (bool full) return node; } +void +PluginInsert::set_control_ids (const XMLNode& node, int version) +{ + const XMLNodeList& nlist = node.children(); + XMLNodeConstIterator iter; + set::const_iterator p; + + for (iter = nlist.begin(); iter != nlist.end(); ++iter) { + if ((*iter)->name() == Controllable::xml_node_name) { + const XMLProperty* prop; + + if ((prop = (*iter)->property (X_("parameter"))) != 0) { + uint32_t p = atoi (prop->value()); + boost::shared_ptr c = control (Evoral::Parameter (PluginAutomation, 0, p)); + if (!c) { + continue; + } + boost::shared_ptr ac = boost::dynamic_pointer_cast (c); + if (ac) { + ac->set_state (**iter, version); + } + } + } + } +} int PluginInsert::set_state(const XMLNode& node, int version) { @@ -779,6 +866,7 @@ PluginInsert::set_state(const XMLNode& node, int version) if (need_automatables) { set_automatable (); + set_control_ids (node, version); } /* Handle the node list for this Processor (or Insert if an A2 session) */ @@ -845,9 +933,9 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version) XMLNode *child; const char *port; uint32_t port_id; - + cnodes = (*niter)->children ("port"); - + for (iter = cnodes.begin(); iter != cnodes.end(); ++iter){ child = *iter; @@ -872,6 +960,30 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version) if (c) { if (!child->children().empty()) { c->alist()->set_state (*child->children().front(), version); + + /* In some cases 2.X saves lists with min_yval and max_yval + being FLT_MIN and FLT_MAX respectively. This causes problems + in A3 because these min/max values are used to compute + where GUI control points should be drawn. If we see such + values, `correct' them to the min/max of the appropriate + parameter. + */ + + float min_y = c->alist()->get_min_y (); + float max_y = c->alist()->get_max_y (); + + Plugin::ParameterDescriptor desc; + _plugins.front()->get_parameter_descriptor (port_id, desc); + + if (min_y == FLT_MIN) { + min_y = desc.lower; + } + + if (max_y == FLT_MAX) { + max_y = desc.upper; + } + + c->alist()->set_yrange (min_y, max_y); } } else { error << string_compose (_("PluginInsert: automatable control %1 not found - ignored"), port_id) << endmsg; @@ -888,13 +1000,14 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version) string PluginInsert::describe_parameter (Evoral::Parameter param) { - if (param.type() != PluginAutomation) + if (param.type() != PluginAutomation) { return Automatable::describe_parameter(param); + } return _plugins[0]->describe_parameter (param); } -ARDOUR::nframes_t +ARDOUR::framecnt_t PluginInsert::signal_latency() const { if (_user_latency) { @@ -962,6 +1075,18 @@ PluginInsert::PluginControl::set_value (double val) AutomationControl::set_value(val); } +XMLNode& +PluginInsert::PluginControl::get_state () +{ + stringstream ss; + + XMLNode& node (AutomationControl::get_state()); + ss << parameter().id(); + node.add_property (X_("parameter"), ss.str()); + + return node; +} + double PluginInsert::PluginControl::get_value (void) const { @@ -1000,7 +1125,7 @@ PluginInsert::get_impulse_analysis_plugin() } void -PluginInsert::collect_signal_for_analysis(nframes_t nframes) +PluginInsert::collect_signal_for_analysis (framecnt_t nframes) { // called from outside the audio thread, so this should be safe // only do audio as analysis is (currently) only for audio plugins @@ -1020,3 +1145,22 @@ PluginInsert::add_plugin_with_activation (boost::shared_ptr plugin) plugin->activate (); } } + +void +PluginInsert::realtime_handle_transport_stopped () +{ + for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) { + (*i)->realtime_handle_transport_stopped (); + } +} + +void +PluginInsert::set_splitting (bool s) +{ + if (_splitting == s) { + return; + } + + _splitting = s; + SplittingChanged (); /* EMIT SIGNAL */ +}