X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fio.cc;h=5d1f39f968c3ef87009d686c996be83f7a1a0d56;hb=6bc95465d6137da74907bb528861681deec7b2df;hp=3bbe34f825b97a31861ae90c5e0e05a9f8e54553;hpb=cb09b0b34e6382bcd403526e8501b62d609fed39;p=ardour.git diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 3bbe34f825..5d1f39f968 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -31,6 +31,8 @@ #include "pbd/replace_all.h" #include "pbd/unknown_type.h" #include "pbd/enumwriter.h" +#include "pbd/locale_guard.h" +#include "pbd/types_convert.h" #include "ardour/audioengine.h" #include "ardour/buffer.h" @@ -41,9 +43,10 @@ #include "ardour/profile.h" #include "ardour/route.h" #include "ardour/session.h" +#include "ardour/types_convert.h" #include "ardour/user_bundle.h" -#include "i18n.h" +#include "pbd/i18n.h" #define BLOCK_PROCESS_CALLBACK() Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock()) @@ -89,16 +92,22 @@ IO::~IO () { Glib::Threads::Mutex::Lock lm (io_lock); + DEBUG_TRACE (DEBUG::Ports, string_compose ("IO %1 unregisters %2 ports\n", name(), _ports.num_ports())); + BLOCK_PROCESS_CALLBACK (); for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { _session.engine().unregister_port (*i); } + delete pending_state_node; pending_state_node = 0; } void IO::disconnect_check (boost::shared_ptr a, boost::shared_ptr b) { + if (_session.state_of_the_state () & Session::Deletion) { + return; + } /* this could be called from within our own ::disconnect() method(s) or from somewhere that operates directly on a port. so, we don't know for sure if we can take this lock or not. if we fail, @@ -126,11 +135,11 @@ IO::increment_port_buffer_offset (pframes_t offset) { /* io_lock, not taken: function must be called from Session::process() calltree */ - if (_direction == Output) { - for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - i->increment_port_buffer_offset (offset); - } - } + if (_direction == Output) { + for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { + i->increment_port_buffer_offset (offset); + } + } } void @@ -139,54 +148,12 @@ IO::silence (framecnt_t nframes) /* io_lock, not taken: function must be called from Session::process() calltree */ for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - i->get_buffer(nframes).silence (nframes); - } -} - -/** Set _bundles_connected to those bundles that are connected such that every - * port on every bundle channel x is connected to port x in _ports. - */ -void -IO::check_bundles_connected () -{ - std::vector new_list; - - for (std::vector::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) { - - uint32_t const N = (*i)->bundle->nchannels().n_total(); - - if (_ports.num_ports() < N) { - continue; - } - - bool ok = true; - - for (uint32_t j = 0; j < N; ++j) { - /* Every port on bundle channel j must be connected to our input j */ - Bundle::PortList const pl = (*i)->bundle->channel_ports (j); - for (uint32_t k = 0; k < pl.size(); ++k) { - if (_ports.port(j)->connected_to (pl[k]) == false) { - ok = false; - break; - } - } - - if (ok == false) { - break; - } - } - - if (ok) { - new_list.push_back (*i); - } else { - delete *i; + if (i->port_handle ()) { + i->get_buffer(nframes).silence (nframes); } } - - _bundles_connected = new_list; } - int IO::disconnect (boost::shared_ptr our_port, string other_port, void* src) { @@ -194,26 +161,24 @@ IO::disconnect (boost::shared_ptr our_port, string other_port, void* src) return 0; } - { - Glib::Threads::Mutex::Lock lm (io_lock); - - /* check that our_port is really one of ours */ + { + Glib::Threads::Mutex::Lock lm (io_lock); - if ( ! _ports.contains(our_port)) { - return -1; - } + /* check that our_port is really one of ours */ - /* disconnect it from the source */ + if ( ! _ports.contains(our_port)) { + return -1; + } - if (our_port->disconnect (other_port)) { - error << string_compose(_("IO: cannot disconnect port %1 from %2"), our_port->name(), other_port) << endmsg; - return -1; - } + /* disconnect it from the source */ - check_bundles_connected (); - } + if (our_port->disconnect (other_port)) { + error << string_compose(_("IO: cannot disconnect port %1 from %2"), our_port->name(), other_port) << endmsg; + return -1; + } + } - changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ _session.set_dirty (); @@ -277,7 +242,6 @@ IO::remove_port (boost::shared_ptr port, void* src) } _session.engine().unregister_port (port); - check_bundles_connected (); } } @@ -384,8 +348,6 @@ IO::disconnect (void* src) for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { i->disconnect_all (); } - - check_bundles_connected (); } changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ @@ -402,6 +364,7 @@ IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed) #endif boost::shared_ptr port; + vector > deleted_ports; changed = false; @@ -415,11 +378,30 @@ IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed) assert(port); _ports.remove(port); + + /* hold a reference to the port so that we can ensure + * that this thread, and not a JACK notification thread, + * holds the final reference. + */ + + deleted_ports.push_back (port); _session.engine().unregister_port (port); changed = true; } + /* this will drop the final reference to the deleted ports, + * which will in turn call their destructors, which will in + * turn call the backend to unregister them. + * + * There will no connect/disconnect or register/unregister + * callbacks from the backend until we get here, because + * they are driven by the Port destructor. The destructor + * will not execute until we drop the final reference, + * which all happens right .... here. + */ + deleted_ports.clear (); + /* create any necessary new ports */ while (n_ports().get(*t) < n) { @@ -451,7 +433,6 @@ IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed) } if (changed) { - check_bundles_connected (); PortCountChanged (n_ports()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -524,26 +505,16 @@ XMLNode& IO::state (bool /*full_state*/) { XMLNode* node = new XMLNode (state_node_name); - char buf[64]; - string str; int n; - LocaleGuard lg (X_("C")); Glib::Threads::Mutex::Lock lm (io_lock); - node->add_property("name", _name); - id().print (buf, sizeof (buf)); - node->add_property("id", buf); - node->add_property ("direction", enum_2_string (_direction)); - node->add_property ("default-type", _default_type.to_string()); + node->set_property ("name", name()); + node->set_property ("id", id ()); + node->set_property ("direction", _direction); + node->set_property ("default-type", _default_type); if (!_pretty_name_prefix.empty ()) { - node->add_property("pretty-name", _pretty_name_prefix); - } - - for (std::vector::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) { - XMLNode* n = new XMLNode ("Bundle"); - n->add_property ("name", (*i)->bundle->name ()); - node->add_child_nocopy (*n); + node->set_property("pretty-name", _pretty_name_prefix); } for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { @@ -551,8 +522,8 @@ IO::state (bool /*full_state*/) vector connections; XMLNode* pnode = new XMLNode (X_("Port")); - pnode->add_property (X_("type"), i->type().to_string()); - pnode->add_property (X_("name"), i->name()); + pnode->set_property (X_("type"), i->type()); + pnode->set_property (X_("name"), i->name()); if (i->get_connections (connections)) { vector::const_iterator ci; @@ -569,7 +540,7 @@ IO::state (bool /*full_state*/) XMLNode* cnode = new XMLNode (X_("Connection")); - cnode->add_property (X_("other"), _session.engine().make_port_name_relative (*ci)); + cnode->set_property (X_("other"), _session.engine().make_port_name_relative (*ci)); pnode->add_child_nocopy (*cnode); } } @@ -577,8 +548,7 @@ IO::state (bool /*full_state*/) node->add_child_nocopy (*pnode); } - snprintf (buf, sizeof (buf), "%" PRId64, _user_latency); - node->add_property (X_("user-latency"), buf); + node->set_property (X_("user-latency"), _user_latency); return *node; } @@ -592,10 +562,6 @@ IO::set_state (const XMLNode& node, int version) */ assert (version >= 3000); - const XMLProperty* prop; - XMLNodeConstIterator iter; - LocaleGuard lg (X_("C")); - /* force use of non-localized representation of decimal point, since we use it a lot in XML files and so forth. */ @@ -605,28 +571,27 @@ IO::set_state (const XMLNode& node, int version) return -1; } - if ((prop = node.property ("name")) != 0) { - set_name (prop->value()); + bool ignore_name = node.property ("ignore-name"); + std::string name; + if (node.get_property ("name", name) && !ignore_name) { + set_name (name); } - if ((prop = node.property (X_("default-type"))) != 0) { - _default_type = DataType(prop->value()); + if (node.get_property (X_("default-type"), _default_type)) { assert(_default_type != DataType::NIL); } set_id (node); - if ((prop = node.property ("direction")) != 0) { - _direction = (Direction) string_2_enum (prop->value(), _direction); - } + node.get_property ("direction", _direction); if (create_ports (node, version)) { return -1; } // after create_ports, updates names - if ((prop = node.property ("pretty-name")) != 0) { - set_pretty_name (prop->value()); + if (node.get_property ("pretty-name", name)) { + set_pretty_name (name); } if (connecting_legal) { @@ -637,15 +602,14 @@ IO::set_state (const XMLNode& node, int version) } else { + delete pending_state_node; pending_state_node = new XMLNode (node); pending_state_node_version = version; pending_state_node_in = false; ConnectingLegal.connect_same_thread (connection_legal_c, boost::bind (&IO::connecting_became_legal, this)); } - if ((prop = node.property ("user-latency")) != 0) { - _user_latency = atoi (prop->value ()); - } + node.get_property ("user-latency", _user_latency); return 0; } @@ -653,9 +617,9 @@ IO::set_state (const XMLNode& node, int version) int IO::set_state_2X (const XMLNode& node, int version, bool in) { - const XMLProperty* prop; + XMLProperty const * prop; XMLNodeConstIterator iter; - LocaleGuard lg (X_("C")); + LocaleGuard lg; /* force use of non-localized representation of decimal point, since we use it a lot in XML files and so forth. @@ -691,6 +655,7 @@ IO::set_state_2X (const XMLNode& node, int version, bool in) } else { + delete pending_state_node; pending_state_node = new XMLNode (node); pending_state_node_version = version; pending_state_node_in = in; @@ -709,10 +674,10 @@ IO::connecting_became_legal () connection_legal_c.disconnect (); - // it's not required for TracksLive, as long as TracksLive's session does all the connections when it's being loaded - if (!Profile->get_trx() ) { - ret = make_connections (*pending_state_node, pending_state_node_version, pending_state_node_in); - } + // it's not required for TracksLive, as long as TracksLive's session does all the connections when it's being loaded + if (!Profile->get_trx() ) { + ret = make_connections (*pending_state_node, pending_state_node_version, pending_state_node_in); + } delete pending_state_node; pending_state_node = 0; @@ -734,6 +699,7 @@ IO::find_possible_bundle (const string &desired_name) string possible_name; bool stereo = false; string::size_type last_non_digit_pos; + std::string bundle_number_str; error << string_compose(_("Unknown bundle \"%1\" listed for %2 of %3"), desired_name, bundle_type_name, _name) << endmsg; @@ -744,9 +710,8 @@ IO::find_possible_bundle (const string &desired_name) last_non_digit_pos = desired_name.find_last_not_of(digits); if (last_non_digit_pos != string::npos) { - stringstream s; - s << desired_name.substr(last_non_digit_pos); - s >> bundle_number; + bundle_number_str = desired_name.substr(last_non_digit_pos); + bundle_number = string_to(bundle_number_str); } // see if it's a stereo connection e.g. "in 3+4" @@ -758,9 +723,8 @@ IO::find_possible_bundle (const string &desired_name) if (left_last_non_digit_pos != string::npos) { int left_bundle_number = 0; - stringstream s; - s << desired_name.substr(left_last_non_digit_pos, last_non_digit_pos-1); - s >> left_bundle_number; + bundle_number_str = desired_name.substr(left_last_non_digit_pos, last_non_digit_pos-1); + left_bundle_number = string_to(bundle_number_str); if (left_bundle_number > 0 && left_bundle_number + 1 == bundle_number) { bundle_number--; @@ -785,15 +749,12 @@ IO::find_possible_bundle (const string &desired_name) if (bundle_number & mask) { bundle_number &= ~mask; - stringstream s; - s << default_name << " " << bundle_number + 1; + std::string possible_name = default_name + " " + to_string(bundle_number + 1); if (stereo) { - s << "+" << bundle_number + 2; + possible_name += "+" + to_string(bundle_number + 2); } - possible_name = s.str(); - if ((c = _session.bundle_by_name (possible_name)) != 0) { break; } @@ -925,7 +886,7 @@ IO::make_connections (const XMLNode& node, int version, bool in) return make_connections_2X (node, version, in); } - const XMLProperty* prop; + XMLProperty const * prop; for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) { @@ -965,7 +926,7 @@ IO::make_connections (const XMLNode& node, int version, bool in) } if (prop) { - connect (p, prop->value(), this); + connect (p, prop->value(), this); } } } @@ -979,14 +940,14 @@ void IO::prepare_for_reset (XMLNode& node, const std::string& name) { /* reset name */ - node.add_property ("name", name); + node.set_property ("name", name); /* now find connections and reset the name of the port in one so that when we re-use it it will match the name of the thing we're applying it to. */ - XMLProperty* prop; + XMLProperty * prop; XMLNodeList children = node.children(); for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { @@ -1017,7 +978,7 @@ IO::prepare_for_reset (XMLNode& node, const std::string& name) int IO::make_connections_2X (const XMLNode& node, int /*version*/, bool in) { - const XMLProperty* prop; + XMLProperty const * prop; /* XXX: bundles ("connections" as was) */ @@ -1267,22 +1228,28 @@ IO::latency () const for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { if ((latency = i->private_latency_range (_direction == Output).max) > max_latency) { - DEBUG_TRACE (DEBUG::Latency, string_compose ("port %1 has %2 latency of %3 - use\n", - name(), - ((_direction == Output) ? "PLAYBACK" : "CAPTURE"), - latency)); + DEBUG_TRACE (DEBUG::Latency, string_compose ("port %1 has %2 latency of %3 - use\n", + name(), + ((_direction == Output) ? "PLAYBACK" : "CAPTURE"), + latency)); max_latency = latency; } } - DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: max %4 latency from %2 ports = %3\n", - name(), _ports.num_ports(), max_latency, - ((_direction == Output) ? "PLAYBACK" : "CAPTURE"))); + DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: max %4 latency from %2 ports = %3\n", + name(), _ports.num_ports(), max_latency, + ((_direction == Output) ? "PLAYBACK" : "CAPTURE"))); return max_latency; } int -IO::connect_ports_to_bundle (boost::shared_ptr c, bool exclusive, void* src) +IO::connect_ports_to_bundle (boost::shared_ptr c, bool exclusive, void* src) { + return connect_ports_to_bundle(c, exclusive, false, src); +} + +int +IO::connect_ports_to_bundle (boost::shared_ptr c, bool exclusive, + bool allow_partial, void* src) { BLOCK_PROCESS_CALLBACK (); @@ -1295,24 +1262,8 @@ IO::connect_ports_to_bundle (boost::shared_ptr c, bool exclusive, void* } } - c->connect (_bundle, _session.engine()); - - /* If this is a UserBundle, make a note of what we've done */ + c->connect (_bundle, _session.engine(), allow_partial); - boost::shared_ptr ub = boost::dynamic_pointer_cast (c); - if (ub) { - - /* See if we already know about this one */ - std::vector::iterator i = _bundles_connected.begin(); - while (i != _bundles_connected.end() && (*i)->bundle != ub) { - ++i; - } - - if (i == _bundles_connected.end()) { - /* We don't, so make a note */ - _bundles_connected.push_back (new UserBundleInfo (this, ub)); - } - } } changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ @@ -1331,19 +1282,6 @@ IO::disconnect_ports_from_bundle (boost::shared_ptr c, void* src) /* If this is a UserBundle, make a note of what we've done */ - boost::shared_ptr ub = boost::dynamic_pointer_cast (c); - if (ub) { - - std::vector::iterator i = _bundles_connected.begin(); - while (i != _bundles_connected.end() && (*i)->bundle != ub) { - ++i; - } - - if (i != _bundles_connected.end()) { - delete *i; - _bundles_connected.erase (i); - } - } } changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ @@ -1480,11 +1418,10 @@ IO::midi(uint32_t n) const /** * Setup a bundle that describe our inputs or outputs. Also creates the bundle if necessary. */ - void IO::setup_bundle () { - char buf[32]; + char buf[32]; if (!_bundle) { _bundle.reset (new Bundle (_direction == Input)); @@ -1499,7 +1436,7 @@ IO::setup_bundle () } else { snprintf(buf, sizeof (buf), _("%s out"), _name.val().c_str()); } - _bundle->set_name (buf); + _bundle->set_name (buf); int c = 0; for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) { @@ -1522,11 +1459,6 @@ IO::bundles_connected () { BundleList bundles; - /* User bundles */ - for (std::vector::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) { - bundles.push_back ((*i)->bundle); - } - /* Session bundles */ boost::shared_ptr b = _session.bundles (); for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) { @@ -1576,13 +1508,13 @@ IO::bundle_channel_name (uint32_t c, uint32_t n, DataType t) const case 2: return c == 0 ? _("L") : _("R"); default: - snprintf (buf, sizeof(buf), _("%d"), (c + 1)); + snprintf (buf, sizeof(buf), "%d", (c + 1)); return buf; } } else { - snprintf (buf, sizeof(buf), _("%d"), (c + 1)); + snprintf (buf, sizeof(buf), "%d", (c + 1)); return buf; } @@ -1593,7 +1525,7 @@ IO::bundle_channel_name (uint32_t c, uint32_t n, DataType t) const string IO::name_from_state (const XMLNode& node) { - const XMLProperty* prop; + XMLProperty const * prop; if ((prop = node.property ("name")) != 0) { return prop->value(); @@ -1605,13 +1537,13 @@ IO::name_from_state (const XMLNode& node) void IO::set_name_in_state (XMLNode& node, const string& new_name) { - node.add_property (X_("name"), new_name); + node.set_property (X_("name"), new_name); XMLNodeList children = node.children (); for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { if ((*i)->name() == X_("Port")) { string const old_name = (*i)->property(X_("name"))->value(); string const old_name_second_part = old_name.substr (old_name.find_first_of ("/") + 1); - (*i)->add_property (X_("name"), string_compose ("%1/%2", new_name, old_name_second_part)); + (*i)->set_property (X_("name"), string_compose ("%1/%2", new_name, old_name_second_part)); } } } @@ -1619,22 +1551,22 @@ IO::set_name_in_state (XMLNode& node, const string& new_name) bool IO::connected () const { - /* do we have any connections at all? */ + /* do we have any connections at all? */ - for (PortSet::const_iterator p = _ports.begin(); p != _ports.end(); ++p) { - if (p->connected()) { - return true; - } - } + for (PortSet::const_iterator p = _ports.begin(); p != _ports.end(); ++p) { + if (p->connected()) { + return true; + } + } - return false; + return false; } bool IO::connected_to (boost::shared_ptr other) const { if (!other) { - return connected (); + return connected (); } assert (_direction != other->direction()); @@ -1672,7 +1604,7 @@ IO::connected_to (const string& str) const * Caller must hold process lock. */ void -IO::process_input (boost::shared_ptr proc, framepos_t start_frame, framepos_t end_frame, pframes_t nframes) +IO::process_input (boost::shared_ptr proc, framepos_t start_frame, framepos_t end_frame, double speed, pframes_t nframes) { /* don't read the data into new buffers - just use the port buffers directly */ @@ -1683,7 +1615,7 @@ IO::process_input (boost::shared_ptr proc, framepos_t start_frame, fr _buffers.get_backend_port_addresses (_ports, nframes); if (proc) { - proc->run (_buffers, start_frame, end_frame, nframes, true); + proc->run (_buffers, start_frame, end_frame, speed, nframes, true); } } @@ -1718,12 +1650,14 @@ IO::collect_input (BufferSet& bufs, pframes_t nframes, ChanCount offset) void IO::copy_to_outputs (BufferSet& bufs, DataType type, pframes_t nframes, framecnt_t offset) { - // Copy any buffers 1:1 to outputs - PortSet::iterator o = _ports.begin(type); BufferSet::iterator i = bufs.begin(type); BufferSet::iterator prev = i; + assert(i != bufs.end(type)); // or second loop will crash + + // Copy any buffers 1:1 to outputs + while (i != bufs.end(type) && o != _ports.end (type)) { Buffer& port_buffer (o->get_buffer (nframes)); port_buffer.read_from (*i, nframes, offset); @@ -1760,12 +1694,12 @@ bool IO::physically_connected () const { for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { - if (i->physically_connected()) { - return true; - } - } + if (i->physically_connected()) { + return true; + } + } - return false; + return false; } bool