diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 84217c4f0e..5d4d916832 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -118,6 +118,7 @@ public: bool connected_to (const std::string&) const; bool connected () const; bool physically_connected () const; + bool has_ext_connection () const; samplecnt_t latency () const; samplecnt_t connected_latency (bool for_playback) const; diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index 9fea39bf20..b2fdc27c62 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -128,6 +128,7 @@ public: virtual void realtime_locate (bool for_loop_end) {} virtual void set_buffer_size (pframes_t) {} + bool has_ext_connection () const; bool physically_connected () const; bool in_cycle () const { return _in_cycle; } diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 2b3a76aace..fe48b7afe1 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -161,6 +161,7 @@ class PluginInsert; class PluginInfo; class Port; class PortInsert; +class PortManager; class ProcessThread; class Processor; class Region; @@ -2192,7 +2193,9 @@ private: std::shared_ptr _master_out; std::shared_ptr _monitor_out; + friend class PortManager; void auto_connect_master_bus (); + void auto_connect_monitor_bus (); void setup_route_monitor_sends (bool enable, bool need_process_lock); diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index c02f6eff49..ecf73012ec 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -1717,6 +1717,18 @@ IO::physically_connected () const return false; } +bool +IO::has_ext_connection () const +{ + for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { + if (i->has_ext_connection()) { + return true; + } + } + + return false; +} + bool IO::has_port (std::shared_ptr p) const { diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 0af0323f01..2fd3bdd957 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -215,7 +215,9 @@ Port::erase_connection (std::string const& pn) { std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); Glib::Threads::RWLock::WriterLock lm (_connections_lock); - _ext_connections[bid].erase (pn); + if (_ext_connections.find (bid) != _ext_connections.end ()) { + _ext_connections[bid].erase (pn); + } } else { Glib::Threads::RWLock::WriterLock lm (_connections_lock); _int_connections.erase (pn); @@ -273,7 +275,9 @@ Port::disconnect_all () std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); Glib::Threads::RWLock::WriterLock lm (_connections_lock); _int_connections.clear (); - _ext_connections[bid].clear (); + if (_ext_connections.find (bid) != _ext_connections.end ()) { + _ext_connections[bid].clear (); + } } /* a cheaper, less hacky way to do boost::shared_from_this() ... @@ -315,9 +319,8 @@ Port::get_connections (std::vector& c) const std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); Glib::Threads::RWLock::ReaderLock lm (_connections_lock); c.insert (c.end(), _int_connections.begin(), _int_connections.end()); - try { + if (_ext_connections.find (bid) != _ext_connections.end ()) { c.insert (c.end(), _ext_connections.at(bid).begin(), _ext_connections.at(bid).end()); - } catch (std::out_of_range&) { } return c.size (); } @@ -698,23 +701,42 @@ Port::reestablish () return 0; } +bool +Port::has_ext_connection () const +{ + std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); + + Glib::Threads::RWLock::ReaderLock lm (_connections_lock); + + return _ext_connections.find (bid) != _ext_connections.end (); +} int Port::reconnect () { /* caller must hold process lock; intended to be used only after reestablish() */ + int count = 0; + std::string const bid (AudioEngine::instance()->backend_id (receives_input ())); Glib::Threads::RWLock::WriterLock lm (_connections_lock); - if (_int_connections.empty () && _ext_connections[bid].empty ()) { - return 0; /* OK */ + if (_ext_connections.find (bid) != _ext_connections.end ()) { + if (_int_connections.empty () && _ext_connections[bid].empty ()) { + return 0; /* OK */ + } + count = _int_connections.size() + _ext_connections[bid].size(); + } else { + if (_int_connections.empty ()) { + return 0; /* OK */ + } + count = _int_connections.size(); } - DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), _int_connections.size() + _ext_connections[bid].size())); + DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), count)); - int count = 0; + count = 0; ConnectionSet::iterator i = _int_connections.begin(); while (i != _int_connections.end()) { @@ -727,14 +749,16 @@ Port::reconnect () } } - i = _ext_connections[bid].begin(); - while (i != _ext_connections[bid].end()) { - ConnectionSet::iterator current = i++; - if (connect_internal (*current)) { - DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (*current))); - _ext_connections[bid].erase (current); - } else { - ++count; + if (_ext_connections.find (bid) != _ext_connections.end ()) { + i = _ext_connections[bid].begin(); + while (i != _ext_connections[bid].end()) { + ConnectionSet::iterator current = i++; + if (connect_internal (*current)) { + DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() failed to connect %1 to %2\n", name(), (*current))); + _ext_connections[bid].erase (current); + } else { + ++count; + } } } @@ -792,6 +816,9 @@ Port::get_state () const } for (auto const& hwc : _ext_connections) { + XMLNode* child = new XMLNode (X_("ExtConnection")); + child->set_property (X_("for"), hwc.first); + root->add_child_nocopy (*child); for (auto const& c : hwc.second) { XMLNode* child = new XMLNode (X_("ExtConnection")); child->set_property (X_("for"), hwc.first); @@ -828,8 +855,12 @@ Port::set_state (const XMLNode& node, int) } std::string hw; - if ((*c)->name() == X_("ExtConnection") && (*c)->get_property (X_("for"), hw) && (*c)->get_property (X_("other"), str)) { + if ((*c)->name() == X_("ExtConnection") && (*c)->get_property (X_("for"), hw)) { + if ((*c)->get_property (X_("other"), str)) { _ext_connections[hw].insert (str); + } else { + _ext_connections[hw]; // create + } } } diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc index 8055f3ec4a..83a80c64e5 100644 --- a/libs/ardour/port_manager.cc +++ b/libs/ardour/port_manager.cc @@ -918,6 +918,14 @@ PortManager::reconnect_ports () DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size ())); + Session* s = AudioEngine::instance ()->session (); + if (s && s->master_out() && !s->master_out ()->output()->has_ext_connection()) { + s->auto_connect_master_bus (); + } + if (s && s->monitor_out() && !s->monitor_out ()->output()->has_ext_connection()) { + s->auto_connect_monitor_bus (); + } + for (auto const& i : *p) { if (i.second->reconnect ()) { PortConnectedOrDisconnected (i.second, i.first, std::weak_ptr (), "", false); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index f79f1bc93c..653e3487c5 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -1149,58 +1149,7 @@ Session::add_monitor_section () } } - /* if monitor section is not connected, connect it to physical outs */ - - if ((Config->get_auto_connect_standard_busses () || Profile->get_mixbus ()) && !_monitor_out->output()->connected ()) { - - if (!Config->get_monitor_bus_preferred_bundle().empty()) { - - std::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); - - if (b) { - _monitor_out->output()->connect_ports_to_bundle (b, true, this); - } else { - warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), - Config->get_monitor_bus_preferred_bundle()) - << endmsg; - } - - } else { - - /* Monitor bus is audio only */ - - vector outputs[DataType::num_types]; - - for (uint32_t i = 0; i < DataType::num_types; ++i) { - _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); - } - - uint32_t mod = outputs[DataType::AUDIO].size(); - uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); - - if (mod != 0) { - - for (uint32_t n = 0; n < limit; ++n) { - - std::shared_ptr p = _monitor_out->output()->ports().port(DataType::AUDIO, n); - string connect_to; - if (outputs[DataType::AUDIO].size() > (n % mod)) { - connect_to = outputs[DataType::AUDIO][n % mod]; - } - - if (!connect_to.empty()) { - if (_monitor_out->output()->connect (p, connect_to, this)) { - error << string_compose ( - _("cannot connect control output %1 to %2"), - n, connect_to) - << endmsg; - break; - } - } - } - } - } - } + auto_connect_monitor_bus (); /* Hold process lock while doing this so that we don't hear bits and * pieces of audio as we work on each route. @@ -1211,6 +1160,68 @@ Session::add_monitor_section () MonitorBusAddedOrRemoved (); /* EMIT SIGNAL */ } +void +Session::auto_connect_monitor_bus () +{ + if (!_master_out || !_monitor_out) { + return; + } + + if ((!Config->get_auto_connect_standard_busses () && !Profile->get_mixbus ()) || _monitor_out->output()->connected ()) { + return; + } + + /* if monitor section is not connected, connect it to physical outs */ + + if (!Config->get_monitor_bus_preferred_bundle().empty()) { + + std::shared_ptr b = bundle_by_name (Config->get_monitor_bus_preferred_bundle()); + + if (b) { + _monitor_out->output()->connect_ports_to_bundle (b, true, this); + } else { + warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"), + Config->get_monitor_bus_preferred_bundle()) + << endmsg; + } + + } else { + + /* Monitor bus is audio only */ + + vector outputs[DataType::num_types]; + + for (uint32_t i = 0; i < DataType::num_types; ++i) { + _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]); + } + + uint32_t mod = outputs[DataType::AUDIO].size(); + uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO); + + if (mod != 0) { + + for (uint32_t n = 0; n < limit; ++n) { + + std::shared_ptr p = _monitor_out->output()->ports().port(DataType::AUDIO, n); + string connect_to; + if (outputs[DataType::AUDIO].size() > (n % mod)) { + connect_to = outputs[DataType::AUDIO][n % mod]; + } + + if (!connect_to.empty()) { + if (_monitor_out->output()->connect (p, connect_to, this)) { + error << string_compose ( + _("cannot connect control output %1 to %2"), + n, connect_to) + << endmsg; + break; + } + } + } + } + } +} + void Session::setup_route_monitor_sends (bool enable, bool need_process_lock) {