revert bd5da84928 - separate i/o-map & config reset.
[ardour.git] / libs / ardour / route.cc
index 5eb962da5aa0ff081eff697aa6087e334d224fc6..6519980b684c6e53b1a11dde447cd9bbea2f1c19 100644 (file)
@@ -115,6 +115,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
+       , _strict_io (false)
        , _custom_meter_position_noted (false)
 {
        processor_max_streams.reset();
@@ -663,7 +664,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        break;
                }
 
-               /* if we're not exporting, stop processing if we come across a routing processor. */
+               /* if we're *not* exporting, stop processing if we come across a routing processor. */
                if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
                        break;
                }
@@ -671,8 +672,20 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        break;
                }
 
-               /* don't run any processors that does routing.
-                * oh, and don't bother with the peak meter either.
+               /* special case the panner (export outputs)
+                * Ideally we'd only run the panner, not the delivery itself...
+                * but panners need separate input/output buffers and some context
+                * (panshell, panner type, etc). AFAICT there is no ill side effect
+                * of re-using the main delivery when freewheeling/exporting a region.
+                */
+               if ((*i) == _main_outs) {
+                       assert ((*i)->does_routing());
+                       (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+                       buffers.set_count ((*i)->output_streams());
+               }
+
+               /* don't run any processors that do routing.
+                * Also don't bother with metering.
                 */
                if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
                        (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
@@ -795,7 +808,7 @@ Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override
        }
 
        if (use_group (group_override, &RouteGroup::is_solo)) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::NoGroup));
+               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -880,6 +893,9 @@ Route::clear_all_solo_state ()
 void
 Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
 {
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
+                                                 name(), yn, enum_2_string(group_override), self_soloed()));
+
        if (_solo_safe) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
                return;
@@ -891,13 +907,10 @@ Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
        }
 
        if (use_group (group_override, &RouteGroup::is_solo)) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::NoGroup));
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
                return;
        }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
-                                                 name(), yn, enum_2_string(group_override), self_soloed()));
-
        if (self_soloed() != yn) {
                set_self_solo (yn);
                solo_changed (true, group_override); /* EMIT SIGNAL */
@@ -1042,7 +1055,7 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o
        }
 
        if (use_group (group_override, &RouteGroup::is_solo)) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::NoGroup));
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -1112,7 +1125,7 @@ void
 Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (use_group (group_override, &RouteGroup::is_mute)) {
-               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::NoGroup));
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -1245,6 +1258,13 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
                return 1;
        }
 
+       if (_strict_io) {
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+                       pi->set_strict_io (true);
+               }
+       }
+
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
@@ -1443,7 +1463,8 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1);
+                               pi->set_count (1); // why? configure_processors_unlocked() will re-do this
+                               pi->set_strict_io (_strict_io);
                        }
 
                        _processors.insert (loc, *i);
@@ -1801,6 +1822,104 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        return 0;
 }
 
+int
+Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Processor> sub, ProcessorStreams* err)
+{
+       /* these can never be removed */
+       if (old == _amp || old == _meter || old == _main_outs || old == _delayline || old == _trim) {
+               return 1;
+       }
+       /* and can't be used as substitute, either */
+       if (sub == _amp || sub == _meter || sub == _main_outs || sub == _delayline || sub == _trim) {
+               return 1;
+       }
+
+       /* I/Os are out, too */
+       if (boost::dynamic_pointer_cast<IOProcessor> (old) || boost::dynamic_pointer_cast<IOProcessor> (sub)) {
+               return 1;
+       }
+
+       /* this function cannot be used to swap/reorder processors */
+       if (find (_processors.begin(), _processors.end(), sub) != _processors.end ()) {
+               return 1;
+       }
+
+       if (!AudioEngine::instance()->connected() || !old || !sub) {
+               return 1;
+       }
+
+       /* ensure that sub is not owned by another route */
+       if (sub->owner ()) {
+               return 1;
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
+               assert (find (_processors.begin(), _processors.end(), sub) == _processors.end ());
+
+               ProcessorList::iterator i;
+               bool replaced = false;
+               bool enable = old->active ();
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+                       if (*i == old) {
+                               i = _processors.erase (i);
+                               _processors.insert (i, sub);
+                               sub->set_owner (this);
+                               replaced = true;
+                               break;
+                       } else {
+                               ++i;
+                       }
+               }
+
+               if (!replaced) {
+                       return 1;
+               }
+
+               if (_strict_io) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(sub)) != 0) {
+                               pi->set_strict_io (true);
+                       }
+               }
+
+               if (configure_processors_unlocked (err)) {
+                       pstate.restore ();
+                       configure_processors_unlocked (0);
+                       return -1;
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->has_no_inputs ()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (enable) {
+                       sub->activate ();
+               }
+
+               sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+               _output->set_user_latency (0);
+       }
+
+       reset_instrument_info ();
+       old->drop_references ();
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
+       return 0;
+}
+
 int
 Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
 {
@@ -2036,16 +2155,22 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
                if (!(*p)->configure_io(c->first, c->second)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
+                       _in_configure_processors = false;
+                       return -1;
                }
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
 
                boost::shared_ptr<PluginInsert> pi;
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
-                       /* plugins connected via Split Match may have more channels.
-                        * route/scratch buffers are needed for all of them*/
+                       /* plugins connected via Split or Hide Match may have more channels.
+                        * route/scratch buffers are needed for all of them
+                        * The configuration may only be a subset (both input and output)
+                        */
                        processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->output_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams() * pi->get_count());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_output_streams() * pi->get_count());
                }
                out = c->second;
 
@@ -2278,6 +2403,108 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
+bool
+Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
+{
+       ChanCount unused;
+       return customize_plugin_insert (proc, 0, unused);
+}
+
+bool
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               bool found = false;
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                       if (*p == proc) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
+               bool      old_cust = pi->custom_cfg ();
+               uint32_t  old_cnt  = pi->get_count ();
+               ChanCount old_chan = pi->output_streams ();
+
+               if (count == 0) {
+                       pi->set_custom_cfg (false);
+               } else {
+                       pi->set_custom_cfg (true);
+                       pi->set_count (count);
+                       pi->set_outputs (outs);
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+
+                       pi->set_count (old_cnt);
+                       pi->set_outputs (old_chan);
+                       pi->set_custom_cfg (old_cust);
+
+                       return false;
+               }
+               configure_processors_unlocked (0);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::set_strict_io (const bool enable)
+{
+       if (_strict_io != enable) {
+               _strict_io = enable;
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                               pi->set_strict_io (_strict_io);
+                       }
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+
+               if (c.empty()) {
+                       // not possible
+                       _strict_io = !enable; // restore old value
+                       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                               boost::shared_ptr<PluginInsert> pi;
+                               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                                       pi->set_strict_io (_strict_io);
+                               }
+                       }
+                       return false;
+               }
+               lm.release ();
+
+               {
+                       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+                       configure_processors (0);
+               }
+               processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
+       return true;
+}
+
 XMLNode&
 Route::get_state()
 {
@@ -2306,6 +2533,7 @@ Route::state(bool full_state)
        node->add_property("id", buf);
        node->add_property ("name", _name);
        node->add_property("default-type", _default_type.to_string());
+       node->add_property ("strict-io", _strict_io);
 
        if (_flags) {
                node->add_property("flags", enum_2_string (_flags));
@@ -2429,6 +2657,10 @@ Route::set_state (const XMLNode& node, int version)
                _flags = Flag (0);
        }
 
+       if ((prop = node.property (X_("strict-io"))) != 0) {
+               _strict_io = string_is_affirmative (prop->value());
+       }
+
        if (is_master() || is_monitor() || is_auditioner()) {
                _mute_master->set_solo_ignore (true);
        }
@@ -2972,13 +3204,19 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
                                           prop->value() == "windows-vst" ||
-                                          prop->value() == "lxvst" ||
+                                          prop->value() == "lxvst" ||
+                                          prop->value() == "luaproc" ||
                                           prop->value() == "audiounit") {
 
                                        if (_session.get_disable_all_loaded_plugins ()) {
                                                processor.reset (new UnknownProcessor (_session, **niter));
                                        } else {
                                                processor.reset (new PluginInsert (_session));
+                                               if (_strict_io) {
+                                                       boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+                                                       pi->set_strict_io (true);
+                                               }
+
                                        }
                                } else if (prop->value() == "port") {