Save I/O connections per device (#9344)
This commit is contained in:
parent
c11f8532ca
commit
aaf3013211
@ -134,11 +134,11 @@ public:
|
||||
uint32_t externally_connected () const { return _externally_connected; }
|
||||
uint32_t internally_connected () const { return _internally_connected; }
|
||||
|
||||
void increment_external_connections() { _externally_connected++; }
|
||||
void decrement_external_connections() { if (_externally_connected) _externally_connected--; }
|
||||
void increment_external_connections ();
|
||||
void decrement_external_connections ();
|
||||
|
||||
void increment_internal_connections() { _internally_connected++; }
|
||||
void decrement_internal_connections() { if (_internally_connected) _internally_connected--; }
|
||||
void increment_internal_connections ();
|
||||
void decrement_internal_connections ();
|
||||
|
||||
|
||||
PBD::Signal1<void,bool> MonitorInputChanged;
|
||||
@ -201,15 +201,23 @@ private:
|
||||
uint32_t _externally_connected;
|
||||
uint32_t _internally_connected;
|
||||
|
||||
/** ports that we are connected to, kept so that we can
|
||||
reconnect to the backend when required
|
||||
*/
|
||||
std::set<std::string> _connections;
|
||||
typedef std::set<std::string> ConnectionSet;
|
||||
/* ports that we are connected to, kept so that we can
|
||||
* reconnect to the backend when required
|
||||
*/
|
||||
mutable Glib::Threads::RWLock _connections_lock;
|
||||
ConnectionSet _int_connections;
|
||||
std::map<std::string, ConnectionSet> _ext_connections;
|
||||
|
||||
static uint32_t _resampler_quality; // 8 <= q <= 96
|
||||
static uint32_t _resampler_latency; // = _resampler_quality - 1
|
||||
|
||||
void port_connected_or_disconnected (std::weak_ptr<Port>, std::weak_ptr<Port>, bool);
|
||||
void port_connected_or_disconnected (std::weak_ptr<Port>, std::string, std::weak_ptr<Port>, std::string, bool);
|
||||
|
||||
int connect_internal (std::string const &);
|
||||
void insert_connection (std::string const&);
|
||||
void erase_connection (std::string const&);
|
||||
|
||||
void signal_drop ();
|
||||
void session_global_drop ();
|
||||
void drop ();
|
||||
|
@ -548,34 +548,7 @@ IO::state () const
|
||||
}
|
||||
|
||||
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
|
||||
|
||||
vector<string> connections;
|
||||
|
||||
XMLNode* pnode = new XMLNode (X_("Port"));
|
||||
pnode->set_property (X_("type"), i->type());
|
||||
pnode->set_property (X_("name"), i->name());
|
||||
|
||||
if (i->get_connections (connections)) {
|
||||
vector<string>::const_iterator ci;
|
||||
std::sort (connections.begin(), connections.end());
|
||||
|
||||
for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
|
||||
|
||||
/* if its a connection to our own port,
|
||||
return only the port name, not the
|
||||
whole thing. this allows connections
|
||||
to be re-established even when our
|
||||
client name is different.
|
||||
*/
|
||||
|
||||
XMLNode* cnode = new XMLNode (X_("Connection"));
|
||||
|
||||
cnode->set_property (X_("other"), _session.engine().make_port_name_relative (*ci));
|
||||
pnode->add_child_nocopy (*cnode);
|
||||
}
|
||||
}
|
||||
|
||||
node->add_child_nocopy (*pnode);
|
||||
node->add_child_nocopy (i->get_state ());
|
||||
}
|
||||
|
||||
return *node;
|
||||
|
@ -91,7 +91,7 @@ Port::Port (std::string const & n, DataType t, PortFlags f)
|
||||
|
||||
PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::session_global_drop, this));
|
||||
PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
|
||||
port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
|
||||
port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _2, _3, _4, _5));
|
||||
}
|
||||
|
||||
/** Port destructor */
|
||||
@ -160,7 +160,7 @@ Port::drop ()
|
||||
}
|
||||
|
||||
void
|
||||
Port::port_connected_or_disconnected (std::weak_ptr<Port> w0, std::weak_ptr<Port> w1, bool con)
|
||||
Port::port_connected_or_disconnected (std::weak_ptr<Port> w0, std::string n1, std::weak_ptr<Port> w1, std::string n2, bool con)
|
||||
{
|
||||
std::shared_ptr<Port> p0 = w0.lock ();
|
||||
std::shared_ptr<Port> p1 = w1.lock ();
|
||||
@ -168,13 +168,88 @@ Port::port_connected_or_disconnected (std::weak_ptr<Port> w0, std::weak_ptr<Port
|
||||
std::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
|
||||
|
||||
if (p0 == pself) {
|
||||
if (con) {
|
||||
insert_connection (n2);
|
||||
} else {
|
||||
erase_connection (n2);
|
||||
}
|
||||
ConnectedOrDisconnected (p0, p1, con); // emit signal
|
||||
}
|
||||
if (p1 == pself) {
|
||||
if (con) {
|
||||
insert_connection (n1);
|
||||
} else {
|
||||
erase_connection (n1);
|
||||
}
|
||||
ConnectedOrDisconnected (p1, p0, con); // emit signal
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Port::insert_connection (std::string const& pn)
|
||||
{
|
||||
#if 1 // include external JACK clients
|
||||
if (!AudioEngine::instance()->port_is_mine (pn))
|
||||
#else
|
||||
if (port_manager->port_is_physical (pn))
|
||||
#endif
|
||||
{
|
||||
std::string const bid (AudioEngine::instance()->backend_id (receives_input ()));
|
||||
Glib::Threads::RWLock::WriterLock lm (_connections_lock);
|
||||
_ext_connections[bid].insert (pn);
|
||||
_int_connections.erase (pn); // XXX
|
||||
} else {
|
||||
Glib::Threads::RWLock::WriterLock lm (_connections_lock);
|
||||
_int_connections.insert (pn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Port::erase_connection (std::string const& pn)
|
||||
{
|
||||
#if 1 // include external JACK clients
|
||||
if (!AudioEngine::instance()->port_is_mine (pn))
|
||||
#else
|
||||
if (port_manager->port_is_physical (pn))
|
||||
#endif
|
||||
{
|
||||
std::string const bid (AudioEngine::instance()->backend_id (receives_input ()));
|
||||
Glib::Threads::RWLock::WriterLock lm (_connections_lock);
|
||||
_ext_connections[bid].erase (pn);
|
||||
} else {
|
||||
Glib::Threads::RWLock::WriterLock lm (_connections_lock);
|
||||
_int_connections.erase (pn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Port::increment_external_connections ()
|
||||
{
|
||||
_externally_connected++;
|
||||
}
|
||||
|
||||
void
|
||||
Port::decrement_external_connections ()
|
||||
{
|
||||
if (_externally_connected) {
|
||||
_externally_connected--;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Port::increment_internal_connections ()
|
||||
{
|
||||
_internally_connected++;
|
||||
}
|
||||
|
||||
void
|
||||
Port::decrement_internal_connections ()
|
||||
{
|
||||
if (_internally_connected) {
|
||||
_internally_connected--;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return true if this port is connected to anything */
|
||||
bool
|
||||
Port::connected () const
|
||||
@ -194,7 +269,12 @@ Port::disconnect_all ()
|
||||
get_connections (connections);
|
||||
|
||||
port_engine.disconnect_all (_port_handle);
|
||||
_connections.clear ();
|
||||
{
|
||||
std::string const bid (AudioEngine::instance()->backend_id (receives_input ()));
|
||||
Glib::Threads::RWLock::WriterLock lm (_connections_lock);
|
||||
_int_connections.clear ();
|
||||
_ext_connections[bid].clear ();
|
||||
}
|
||||
|
||||
/* a cheaper, less hacky way to do boost::shared_from_this() ...
|
||||
*/
|
||||
@ -202,7 +282,7 @@ Port::disconnect_all ()
|
||||
for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
|
||||
std::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
|
||||
if (pother) {
|
||||
pother->_connections.erase (_name);
|
||||
pother->erase_connection (_name);
|
||||
ConnectedOrDisconnected (pself, pother, false); // emit signal
|
||||
}
|
||||
}
|
||||
@ -229,11 +309,17 @@ Port::connected_to (std::string const & o) const
|
||||
}
|
||||
|
||||
int
|
||||
Port::get_connections (std::vector<std::string> & c) const
|
||||
Port::get_connections (std::vector<std::string>& c) const
|
||||
{
|
||||
if (!port_manager->running()) {
|
||||
c.insert (c.end(), _connections.begin(), _connections.end());
|
||||
return c.size();
|
||||
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 {
|
||||
c.insert (c.end(), _ext_connections.at(bid).begin(), _ext_connections.at(bid).end());
|
||||
} catch (std::out_of_range&) {
|
||||
}
|
||||
return c.size ();
|
||||
}
|
||||
|
||||
if (_port_handle) {
|
||||
@ -244,7 +330,7 @@ Port::get_connections (std::vector<std::string> & c) const
|
||||
}
|
||||
|
||||
int
|
||||
Port::connect (std::string const & other)
|
||||
Port::connect_internal (std::string const & other)
|
||||
{
|
||||
std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
|
||||
std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
|
||||
@ -262,6 +348,13 @@ Port::connect (std::string const & other)
|
||||
DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
|
||||
r = port_engine.connect (other_name, our_name);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
Port::connect (std::string const& other)
|
||||
{
|
||||
int r = connect_internal (other);
|
||||
|
||||
if (r == 0) {
|
||||
/* Connections can be saved on either or both sides. The code above works regardless
|
||||
@ -274,11 +367,11 @@ Port::connect (std::string const & other)
|
||||
*
|
||||
* This is also nicer when reading the session file's <Port><Connection>.
|
||||
*/
|
||||
_connections.insert (other);
|
||||
insert_connection (other);
|
||||
|
||||
std::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
|
||||
if (pother) {
|
||||
pother->_connections.insert (_name);
|
||||
pother->insert_connection (_name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,7 +393,7 @@ Port::disconnect (std::string const & other)
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
_connections.erase (other);
|
||||
erase_connection (other);
|
||||
}
|
||||
|
||||
/* a cheaper, less hacky way to do boost::shared_from_this() ... */
|
||||
@ -308,7 +401,7 @@ Port::disconnect (std::string const & other)
|
||||
std::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
|
||||
|
||||
if (r == 0 && pother) {
|
||||
pother->_connections.erase (_name);
|
||||
pother->erase_connection (_name);
|
||||
}
|
||||
|
||||
if (pself && pother) {
|
||||
@ -601,7 +694,7 @@ Port::reestablish ()
|
||||
|
||||
reset ();
|
||||
|
||||
port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
|
||||
port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _2, _3, _4, _5));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -611,22 +704,36 @@ Port::reconnect ()
|
||||
{
|
||||
/* caller must hold process lock; intended to be used only after reestablish() */
|
||||
|
||||
if (_connections.empty ()) {
|
||||
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 */
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), _connections.size()));
|
||||
DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reconnect() Connect %1 to %2 destinations\n",name(), _int_connections.size() + _ext_connections[bid].size()));
|
||||
|
||||
int count = 0;
|
||||
std::set<string>::iterator i = _connections.begin();
|
||||
|
||||
while (i != _connections.end()) {
|
||||
std::set<string>::iterator current = i++;
|
||||
if (connect (*current)) {
|
||||
ConnectionSet::iterator i = _int_connections.begin();
|
||||
while (i != _int_connections.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)));
|
||||
_connections.erase (current);
|
||||
_int_connections.erase (current);
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -669,23 +776,30 @@ Port::get_state () const
|
||||
XMLNode* root = new XMLNode (state_node_name);
|
||||
|
||||
root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
|
||||
root->set_property (X_("type"), type ());
|
||||
|
||||
if (receives_input()) {
|
||||
root->set_property (X_("direction"), X_("input"));
|
||||
root->set_property (X_("direction"), X_("Input"));
|
||||
} else {
|
||||
root->set_property (X_("direction"), X_("output"));
|
||||
root->set_property (X_("direction"), X_("Output"));
|
||||
}
|
||||
|
||||
vector<string> c;
|
||||
|
||||
get_connections (c);
|
||||
|
||||
for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
|
||||
Glib::Threads::RWLock::ReaderLock lm (_connections_lock);
|
||||
for (auto const& c : _int_connections) {
|
||||
XMLNode* child = new XMLNode (X_("Connection"));
|
||||
child->set_property (X_("other"), *i);
|
||||
child->set_property (X_("other"), AudioEngine::instance()->make_port_name_relative (c));
|
||||
root->add_child_nocopy (*child);
|
||||
}
|
||||
|
||||
for (auto const& hwc : _ext_connections) {
|
||||
for (auto const& c : hwc.second) {
|
||||
XMLNode* child = new XMLNode (X_("ExtConnection"));
|
||||
child->set_property (X_("for"), hwc.first);
|
||||
child->set_property (X_("other"), c);
|
||||
root->add_child_nocopy (*child);
|
||||
}
|
||||
}
|
||||
|
||||
return *root;
|
||||
}
|
||||
|
||||
@ -703,19 +817,20 @@ Port::set_state (const XMLNode& node, int)
|
||||
|
||||
const XMLNodeList& children (node.children());
|
||||
|
||||
_connections.clear ();
|
||||
_int_connections.clear ();
|
||||
_ext_connections.clear ();
|
||||
|
||||
for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
|
||||
|
||||
if ((*c)->name() != X_("Connection")) {
|
||||
if ((*c)->name() == X_("Connection") && (*c)->get_property (X_("other"), str)) {
|
||||
_int_connections.insert (AudioEngine::instance()->make_port_name_non_relative (str));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(*c)->get_property (X_("other"), str)) {
|
||||
continue;
|
||||
std::string hw;
|
||||
if ((*c)->name() == X_("ExtConnection") && (*c)->get_property (X_("for"), hw) && (*c)->get_property (X_("other"), str)) {
|
||||
_ext_connections[hw].insert (str);
|
||||
}
|
||||
|
||||
_connections.insert (str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user