X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fio.cc;h=01a4a64a389554e35da4f721b2f3c4497eb94a27;hb=e43d91949be2fedf09eaf61cb5b9e4778a505dc0;hp=ebed41fd645fe570060d02b6f6c03dfe7302f8a7;hpb=979a61eb3f1729affe1b0ab4201749b5cde11f68;p=ardour.git diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index ebed41fd64..01a4a64a38 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -34,16 +34,12 @@ #include "ardour/audioengine.h" #include "ardour/buffer.h" +#include "ardour/buffer_set.h" +#include "ardour/debug.h" #include "ardour/io.h" -#include "ardour/route.h" #include "ardour/port.h" -#include "ardour/audio_port.h" -#include "ardour/midi_port.h" +#include "ardour/route.h" #include "ardour/session.h" -#include "ardour/cycle_timer.h" -#include "ardour/buffer_set.h" -#include "ardour/meter.h" -#include "ardour/amp.h" #include "ardour/user_bundle.h" #include "i18n.h" @@ -62,23 +58,27 @@ PBD::Signal1 IO::PortCountChanged; /** @param default_type The type of port that will be created by ensure_io * and friends if no type is explicitly requested (to avoid breakage). */ -IO::IO (Session& s, const string& name, Direction dir, DataType default_type) +IO::IO (Session& s, const string& name, Direction dir, DataType default_type, bool sendish) : SessionObject (s, name) , _direction (dir) , _default_type (default_type) + , _sendish (sendish) { _active = true; + Port::PostDisconnect.connect_same_thread (*this, boost::bind (&IO::disconnect_check, this, _1, _2)); pending_state_node = 0; setup_bundle (); } -IO::IO (Session& s, const XMLNode& node, DataType dt) +IO::IO (Session& s, const XMLNode& node, DataType dt, bool sendish) : SessionObject(s, "unnamed io") , _direction (Input) , _default_type (dt) + , _sendish (sendish) { _active = true; pending_state_node = 0; + Port::PostDisconnect.connect_same_thread (*this, boost::bind (&IO::disconnect_check, this, _1, _2)); set_state (node, Stateful::loading_state_version); setup_bundle (); @@ -96,31 +96,61 @@ IO::~IO () } void -IO::silence (nframes_t nframes) +IO::disconnect_check (boost::shared_ptr a, boost::shared_ptr b) { - /* io_lock, not taken: function must be called from Session::process() calltree */ + /* 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, + we assume that its safely locked by our own ::disconnect(). + */ - for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - i->get_buffer(nframes).silence (nframes); + Glib::Mutex::Lock tm (io_lock, Glib::TRY_LOCK); + + if (tm.locked()) { + /* we took the lock, so we cannot be here from inside + * ::disconnect() + */ + if (_ports.contains (a) || _ports.contains (b)) { + changed (IOChange (IOChange::ConnectionsChanged), this); /* EMIT SIGNAL */ + } + } else { + /* we didn't get the lock, so assume that we're inside + * ::disconnect(), and it will call changed() appropriately. + */ } } void -IO::check_bundles_connected () +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); + } + } +} + +void +IO::silence (framecnt_t nframes) { - check_bundles (_bundles_connected, ports()); + /* 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); + } } -/** Check the bundles in list to see which are connected to a given PortSet, - * and update list with those that are connected such that every port on every - * bundle channel x is connected to port x in ports. +/** 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 (std::vector& list, const PortSet& ports) +IO::check_bundles_connected () { std::vector new_list; - for (std::vector::iterator i = list.begin(); i != list.end(); ++i) { + for (std::vector::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) { uint32_t const N = (*i)->bundle->nchannels().n_total(); @@ -134,7 +164,7 @@ IO::check_bundles (std::vector& list, const PortSet& ports) /* 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) { + if (_ports.port(j)->connected_to (pl[k]) == false) { ok = false; break; } @@ -152,36 +182,36 @@ IO::check_bundles (std::vector& list, const PortSet& ports) } } - list = new_list; + _bundles_connected = new_list; } int -IO::disconnect (Port* our_port, string other_port, void* src) +IO::disconnect (boost::shared_ptr our_port, string other_port, void* src) { if (other_port.length() == 0 || our_port == 0) { return 0; } - + { Glib::Mutex::Lock lm (io_lock); - + /* check that our_port is really one of ours */ - + if ( ! _ports.contains(our_port)) { return -1; } - + /* disconnect it from the source */ - + if (our_port->disconnect (other_port)) { error << string_compose(_("IO: cannot disconnect port %1 from %2"), our_port->name(), other_port) << endmsg; return -1; } - + check_bundles_connected (); } - + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ _session.set_dirty (); @@ -189,9 +219,8 @@ IO::disconnect (Port* our_port, string other_port, void* src) return 0; } -/** Caller must hold process lock */ int -IO::connect (Port* our_port, string other_port, void* src) +IO::connect (boost::shared_ptr our_port, string other_port, void* src) { if (other_port.length() == 0 || our_port == 0) { return 0; @@ -199,34 +228,33 @@ IO::connect (Port* our_port, string other_port, void* src) { Glib::Mutex::Lock lm (io_lock); - + /* check that our_port is really one of ours */ - + if ( ! _ports.contains(our_port) ) { return -1; } - + /* connect it to the source */ - + if (our_port->connect (other_port)) { return -1; } } - changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ _session.set_dirty (); return 0; } int -IO::remove_port (Port* port, void* src) +IO::remove_port (boost::shared_ptr port, void* src) { ChanCount before = _ports.count (); ChanCount after = before; after.set (port->type(), after.get (port->type()) - 1); - bool const r = PortCountChanging (after); /* EMIT SIGNAL */ - if (r) { + boost::optional const r = PortCountChanging (after); /* EMIT SIGNAL */ + if (r.get_value_or (false)) { return -1; } @@ -247,7 +275,7 @@ IO::remove_port (Port* port, void* src) change.type = IOChange::Type (change.type | IOChange::ConnectionsChanged); } - _session.engine().unregister_port (*port); + _session.engine().unregister_port (port); check_bundles_connected (); } } @@ -256,7 +284,7 @@ IO::remove_port (Port* port, void* src) if (change.type != IOChange::NoChange) { changed (change, src); - _session.set_dirty (); + _buffers.attach_buffers (_ports); } } @@ -268,6 +296,8 @@ IO::remove_port (Port* port, void* src) return -1; } + _session.set_dirty (); + return 0; } @@ -280,12 +310,21 @@ IO::remove_port (Port* port, void* src) int IO::add_port (string destination, void* src, DataType type) { - Port* our_port; + boost::shared_ptr our_port; if (type == DataType::NIL) { type = _default_type; } + ChanCount before = _ports.count (); + ChanCount after = before; + after.set (type, after.get (type) + 1); + + bool const r = PortCountChanging (after); /* EMIT SIGNAL */ + if (r) { + return -1; + } + IOChange change; { @@ -298,7 +337,7 @@ IO::add_port (string destination, void* src, DataType type) /* Create a new port */ string portname = build_legal_port_name (type); - + if (_direction == Input) { if ((our_port = _session.engine().register_input_port (type, portname)) == 0) { error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg; @@ -314,16 +353,15 @@ IO::add_port (string destination, void* src, DataType type) change.before = _ports.count (); _ports.add (our_port); } - - PortCountChanged (n_ports()); /* EMIT SIGNAL */ - // pan_changed (src); /* EMIT SIGNAL */ + PortCountChanged (n_ports()); /* EMIT SIGNAL */ change.type = IOChange::ConfigurationChanged; change.after = _ports.count (); changed (change, src); /* EMIT SIGNAL */ + _buffers.attach_buffers (_ports); } - if (destination.length()) { + if (!destination.empty()) { if (our_port->connect (destination)) { return -1; } @@ -340,27 +378,28 @@ IO::disconnect (void* src) { { Glib::Mutex::Lock lm (io_lock); - + for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { i->disconnect_all (); } - + check_bundles_connected (); } - + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ return 0; } /** Caller must hold process lock */ -bool -IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/) +int +IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed) { assert (!AudioEngine::instance()->process_lock().trylock()); - - Port* port = 0; - bool changed = false; + + boost::shared_ptr port; + + changed = false; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { @@ -372,7 +411,7 @@ IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/) assert(port); _ports.remove(port); - _session.engine().unregister_port (*port); + _session.engine().unregister_port (port); changed = true; } @@ -420,7 +459,7 @@ IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/) } } - return changed; + return 0; } /** Caller must hold process lock */ @@ -428,7 +467,7 @@ int IO::ensure_ports (ChanCount count, bool clear, void* src) { assert (!AudioEngine::instance()->process_lock().trylock()); - + bool changed = false; if (count == n_ports() && !clear) { @@ -438,16 +477,19 @@ IO::ensure_ports (ChanCount count, bool clear, void* src) IOChange change; change.before = _ports.count (); - + { Glib::Mutex::Lock im (io_lock); - changed = ensure_ports_locked (count, clear, src); + if (ensure_ports_locked (count, clear, changed)) { + return -1; + } } if (changed) { change.after = _ports.count (); change.type = IOChange::ConfigurationChanged; this->changed (change, src); /* EMIT SIGNAL */ + _buffers.attach_buffers (_ports); setup_bundle (); _session.set_dirty (); } @@ -460,12 +502,12 @@ int IO::ensure_io (ChanCount count, bool clear, void* src) { assert (!AudioEngine::instance()->process_lock().trylock()); - + return ensure_ports (count, clear, src); } XMLNode& -IO::get_state (void) +IO::get_state () { return state (true); } @@ -522,6 +564,9 @@ IO::state (bool /*full_state*/) node->add_child_nocopy (*pnode); } + snprintf (buf, sizeof (buf), "%" PRId64, _user_latency); + node->add_property (X_("user-latency"), buf); + return *node; } @@ -556,9 +601,7 @@ IO::set_state (const XMLNode& node, int version) assert(_default_type != DataType::NIL); } - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); - } + set_id (node); if ((prop = node.property ("direction")) != 0) { _direction = (Direction) string_2_enum (prop->value(), _direction); @@ -582,6 +625,9 @@ IO::set_state (const XMLNode& node, int version) 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 ()); + } return 0; } @@ -611,9 +657,7 @@ IO::set_state_2X (const XMLNode& node, int version, bool in) assert(_default_type != DataType::NIL); } - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); - } + set_id (node); _direction = in ? Input : Output; @@ -840,7 +884,7 @@ IO::create_ports (const XMLNode& node, int version) { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - + if (ensure_ports (n, true, this)) { error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg; return -1; @@ -883,7 +927,7 @@ IO::make_connections (const XMLNode& node, int version, bool in) continue; } - Port* p = port_by_name (prop->value()); + boost::shared_ptr p = port_by_name (prop->value()); if (p) { for (XMLNodeConstIterator c = (*i)->children().begin(); c != (*i)->children().end(); ++c) { @@ -897,18 +941,56 @@ IO::make_connections (const XMLNode& node, int version, bool in) if ((prop = cnode->property (X_("other"))) == 0) { continue; } - + if (prop) { - p->connect (prop->value()); + connect (p, prop->value(), this); } } - } + } } } return 0; } +void +IO::prepare_for_reset (XMLNode& node, const std::string& name) +{ + /* reset name */ + node.add_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; + XMLNodeList children = node.children(); + + for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == "Port") { + + prop = (*i)->property (X_("name")); + + if (prop) { + string new_name; + string old = prop->value(); + string::size_type slash = old.find ('/'); + + if (slash != string::npos) { + /* port name is of form: / */ + + new_name = name; + new_name += old.substr (old.find ('/')); + + prop->set_value (new_name); + } + } + } + } +} + int IO::make_connections_2X (const XMLNode& node, int /*version*/, bool in) @@ -1018,7 +1100,7 @@ IO::set_ports (const string& str) { Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - + // FIXME: audio-only if (ensure_ports (ChanCount(DataType::AUDIO, nports), true, this)) { return -1; @@ -1117,7 +1199,7 @@ IO::set_name (const string& requested_name) /* replace all colons in the name. i wish we didn't have to do this */ - replace_all (name, ":", "-"); + replace_all (name, ":", "-"); for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { string current_name = i->name(); @@ -1132,45 +1214,32 @@ IO::set_name (const string& requested_name) return r; } -void -IO::set_port_latency (nframes_t nframes) -{ - Glib::Mutex::Lock lm (io_lock); - - for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - i->set_latency (nframes); - } -} - -nframes_t +framecnt_t IO::latency () const { - nframes_t max_latency; - nframes_t latency; + framecnt_t max_latency; + framecnt_t latency; max_latency = 0; /* io lock not taken - must be protected by other means */ for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { - if ((latency = i->total_latency ()) > max_latency) { + 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)); 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"))); return max_latency; } -void -IO::update_port_total_latencies () -{ - /* io_lock, not taken: function must be called from Session::process() calltree */ - - for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - _session.engine().update_total_latency (*i); - } -} - int IO::connect_ports_to_bundle (boost::shared_ptr c, void* src) { @@ -1245,6 +1314,7 @@ IO::disable_connecting () int IO::enable_connecting () { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock()); connecting_legal = true; boost::optional r = ConnectingLegal (); return r.get_value_or (0); @@ -1253,7 +1323,7 @@ IO::enable_connecting () void IO::bundle_changed (Bundle::Change /*c*/) { - //XXX + /* XXX */ // connect_input_ports_to_bundle (_input_bundle, this); } @@ -1279,10 +1349,18 @@ IO::build_legal_port_name (DataType type) use the (new) translated name. */ - if (_direction == Input) { - suffix += X_("_in"); + if (_sendish) { + if (_direction == Input) { + suffix += X_("_return"); + } else { + suffix += X_("_send"); + } } else { - suffix += X_("_out"); + if (_direction == Input) { + suffix += X_("_in"); + } else { + suffix += X_("_out"); + } } // allow up to 4 digits for the output port number, plus the slash, suffix and extra space @@ -1292,7 +1370,12 @@ IO::build_legal_port_name (DataType type) char buf1[name_size+1]; char buf2[name_size+1]; - snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.val().c_str(), suffix.c_str()); + /* colons are illegal in port names, so fix that */ + + string nom = _name.val(); + replace_all (nom, ":", ";"); + + snprintf (buf1, name_size+1, ("%.*s/%s"), limit, nom.c_str(), suffix.c_str()); int port_number = find_port_hole (buf1); snprintf (buf2, name_size+1, "%s %d", buf1, port_number); @@ -1334,14 +1417,14 @@ IO::find_port_hole (const char* base) } -AudioPort* +boost::shared_ptr IO::audio(uint32_t n) const { return _ports.nth_audio_port (n); } -MidiPort* +boost::shared_ptr IO::midi(uint32_t n) const { return _ports.nth_midi_port (n); @@ -1475,10 +1558,14 @@ IO::name_from_state (const XMLNode& node) void IO::set_name_in_state (XMLNode& node, const string& new_name) { - const XMLProperty* prop; - - if ((prop = node.property ("name")) != 0) { - node.add_property ("name", new_name); + node.add_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)); + } } } @@ -1486,13 +1573,13 @@ bool IO::connected () const { /* do we have any connections at all? */ - + for (PortSet::const_iterator p = _ports.begin(); p != _ports.end(); ++p) { if (p->connected()) { return true; } } - + return false; } @@ -1520,19 +1607,37 @@ IO::connected_to (boost::shared_ptr other) const return false; } -void -IO::process_input (boost::shared_ptr proc, framepos_t start_frame, framepos_t end_frame, nframes_t nframes) +bool +IO::connected_to (const string& str) const { - BufferSet bufs; + for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { + if (i->connected_to (str)) { + return true; + } + } + + return false; +} +/** Call a processor's ::run() method, giving it our buffers + * Caller must hold process lock. + */ +void +IO::process_input (boost::shared_ptr proc, framepos_t start_frame, framepos_t end_frame, pframes_t nframes) +{ /* don't read the data into new buffers - just use the port buffers directly */ - bufs.attach_buffers (_ports, nframes, 0); - proc->run (bufs, start_frame, end_frame, nframes, true); + if (n_ports().n_total() == 0) { + /* We have no ports, so nothing to process */ + return; + } + + _buffers.get_jack_port_addresses (_ports, nframes); + proc->run (_buffers, start_frame, end_frame, nframes, true); } void -IO::collect_input (BufferSet& bufs, nframes_t nframes, ChanCount offset) +IO::collect_input (BufferSet& bufs, pframes_t nframes, ChanCount offset) { assert(bufs.available() >= _ports.count()); @@ -1560,7 +1665,7 @@ IO::collect_input (BufferSet& bufs, nframes_t nframes, ChanCount offset) } void -IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes, nframes_t offset) +IO::copy_to_outputs (BufferSet& bufs, DataType type, pframes_t nframes, framecnt_t offset) { // Copy any buffers 1:1 to outputs @@ -1585,21 +1690,19 @@ IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes, nframes_ } } -Port* +boost::shared_ptr IO::port_by_name (const std::string& str) const { /* to be called only from ::set_state() - no locking */ for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { - const Port& p(*i); - - if (p.name() == str) { - return const_cast(&p); + if (i->name() == str) { + return boost::const_pointer_cast (*i); } } - return 0; + return boost::shared_ptr (); } bool @@ -1613,3 +1716,10 @@ IO::physically_connected () const return false; } + +bool +IO::has_port (boost::shared_ptr p) const +{ + Glib::Mutex::Lock lm (io_lock); + return _ports.contains (p); +}