X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fio.cc;h=4f1b03dfdaf8d4684766b525878339db5cb9e5d8;hb=ae94dfda5f42a6a0cbd042bce7a8007d90628d51;hp=f40cbafb2dca228e6a0e5c11d229e20e107db174;hpb=45b5b19da287cb3a5c21d37a13c54bc541c5f282;p=ardour.git diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index f40cbafb2d..4f1b03dfda 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -34,6 +34,7 @@ #include "ardour/audioengine.h" #include "ardour/buffer.h" +#include "ardour/debug.h" #include "ardour/io.h" #include "ardour/route.h" #include "ardour/port.h" @@ -96,31 +97,40 @@ IO::~IO () } void -IO::silence (nframes_t nframes) +IO::increment_port_buffer_offset (pframes_t offset) { /* 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); - } + if (_direction == Output) { + for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { + i->increment_port_buffer_offset (offset); + } + } } void -IO::check_bundles_connected () +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); + } } +/** 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 (); + uint32_t const N = (*i)->bundle->nchannels().n_total(); - if (_ports.num_ports (default_type()) < N) { + if (_ports.num_ports() < N) { continue; } @@ -130,7 +140,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; } @@ -148,133 +158,142 @@ 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; } - { - BLOCK_PROCESS_CALLBACK (); + { + Glib::Mutex::Lock lm (io_lock); - { - Glib::Mutex::Lock lm (io_lock); + /* check that our_port is really one of ours */ - /* check that our_port is really one of ours */ + if ( ! _ports.contains(our_port)) { + return -1; + } - if ( ! _ports.contains(our_port)) { - return -1; - } + /* disconnect it from the source */ - /* 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; + } - 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 (); + } - check_bundles_connected (); - } - } + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ - changed (ConnectionsChanged, src); /* EMIT SIGNAL */ _session.set_dirty (); return 0; } 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; } { - BLOCK_PROCESS_CALLBACK (); + Glib::Mutex::Lock lm (io_lock); - { - Glib::Mutex::Lock lm (io_lock); - - /* check that our_port is really one of ours */ + /* check that our_port is really one of ours */ - if ( ! _ports.contains(our_port) ) { - return -1; - } + if ( ! _ports.contains(our_port) ) { + return -1; + } - /* connect it to the source */ + /* connect it to the source */ - if (our_port->connect (other_port)) { - return -1; - } + if (our_port->connect (other_port)) { + return -1; } } - - changed (ConnectionsChanged, src); /* EMIT SIGNAL */ + 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) { - IOChange change (NoChange); + 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) { + return -1; + } + + IOChange change; { BLOCK_PROCESS_CALLBACK (); - { Glib::Mutex::Lock lm (io_lock); if (_ports.remove(port)) { - change = IOChange (change|ConfigurationChanged); + change.type = IOChange::Type (change.type | IOChange::ConfigurationChanged); + change.before = before; + change.after = _ports.count (); if (port->connected()) { - change = IOChange (change|ConnectionsChanged); + change.type = IOChange::Type (change.type | IOChange::ConnectionsChanged); } - _session.engine().unregister_port (*port); + _session.engine().unregister_port (port); check_bundles_connected (); } } PortCountChanged (n_ports()); /* EMIT SIGNAL */ + + if (change.type != IOChange::NoChange) { + changed (change, src); + _buffers.attach_buffers (_ports); + } } - if (change & ConfigurationChanged) { + if (change.type & IOChange::ConfigurationChanged) { setup_bundle (); } - if (change != NoChange) { - changed (change, src); - _session.set_dirty (); - return 0; + if (change.type == IOChange::NoChange) { + return -1; } - return -1; + _session.set_dirty (); + + return 0; } -/** Add an output port. +/** Add a port. * - * @param destination Name of input port to connect new port to. + * @param destination Name of port to connect new port to. * @param src Source for emitted ConfigurationChanged signal. * @param type Data type of port. Default value (NIL) will use this IO's default type. */ 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; } + IOChange change; + { BLOCK_PROCESS_CALLBACK (); @@ -282,10 +301,10 @@ IO::add_port (string destination, void* src, DataType type) { Glib::Mutex::Lock lm (io_lock); - /* Create a new output port */ + /* 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; @@ -298,20 +317,23 @@ IO::add_port (string destination, void* src, DataType type) } } + change.before = _ports.count (); _ports.add (our_port); } - + 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; } } - // pan_changed (src); /* EMIT SIGNAL */ - changed (ConfigurationChanged, src); /* EMIT SIGNAL */ setup_bundle (); _session.set_dirty (); @@ -322,29 +344,29 @@ int IO::disconnect (void* src) { { - BLOCK_PROCESS_CALLBACK (); + Glib::Mutex::Lock lm (io_lock); - { - Glib::Mutex::Lock lm (io_lock); - - for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { - i->disconnect_all (); - } - - check_bundles_connected (); + for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) { + i->disconnect_all (); } + + check_bundles_connected (); } - changed (ConnectionsChanged, src); /* EMIT SIGNAL */ + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ return 0; } -bool -IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/) +/** Caller must hold process lock */ +int +IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed) { - Port* port = 0; - bool changed = false; + assert (!AudioEngine::instance()->process_lock().trylock()); + + boost::shared_ptr port; + + changed = false; for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { @@ -356,7 +378,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; } @@ -404,29 +426,37 @@ IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/) } } - return changed; + return 0; } - +/** Caller must hold process lock */ int -IO::ensure_ports (ChanCount count, bool clear, bool lockit, void* src) +IO::ensure_ports (ChanCount count, bool clear, void* src) { + assert (!AudioEngine::instance()->process_lock().trylock()); + bool changed = false; if (count == n_ports() && !clear) { return 0; } - if (lockit) { - BLOCK_PROCESS_CALLBACK (); + IOChange change; + + change.before = _ports.count (); + + { Glib::Mutex::Lock im (io_lock); - changed = ensure_ports_locked (count, clear, src); - } else { - changed = ensure_ports_locked (count, clear, src); + if (ensure_ports_locked (count, clear, changed)) { + return -1; + } } if (changed) { - this->changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + change.after = _ports.count (); + change.type = IOChange::ConfigurationChanged; + this->changed (change, src); /* EMIT SIGNAL */ + _buffers.attach_buffers (_ports); setup_bundle (); _session.set_dirty (); } @@ -434,14 +464,17 @@ IO::ensure_ports (ChanCount count, bool clear, bool lockit, void* src) return 0; } +/** Caller must hold process lock */ int IO::ensure_io (ChanCount count, bool clear, void* src) { - return ensure_ports (count, clear, true, src); + assert (!AudioEngine::instance()->process_lock().trylock()); + + return ensure_ports (count, clear, src); } XMLNode& -IO::get_state (void) +IO::get_state () { return state (true); } @@ -498,6 +531,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; } @@ -532,9 +568,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); @@ -558,6 +592,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; } @@ -587,9 +624,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; @@ -771,7 +806,7 @@ IO::get_port_counts (const XMLNode& node, int version, ChanCount& n, boost::shar if ((prop = node.property ("connection")) != 0) { if ((c = find_possible_bundle (prop->value())) != 0) { - n = ChanCount::max (n, ChanCount(c->type(), c->nchannels())); + n = ChanCount::max (n, c->nchannels()); } return 0; } @@ -780,7 +815,7 @@ IO::get_port_counts (const XMLNode& node, int version, ChanCount& n, boost::shar if ((*iter)->name() == X_("Bundle")) { if ((c = find_possible_bundle (prop->value())) != 0) { - n = ChanCount::max (n, ChanCount(c->type(), c->nchannels())); + n = ChanCount::max (n, c->nchannels()); return 0; } else { return -1; @@ -814,9 +849,13 @@ IO::create_ports (const XMLNode& node, int version) get_port_counts (node, version, n, c); - if (ensure_ports (n, true, true, this)) { - error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg; - return -1; + { + 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; + } } /* XXX use c */ @@ -855,7 +894,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) { @@ -869,12 +908,12 @@ 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); } } - } + } } } @@ -988,9 +1027,13 @@ IO::set_ports (const string& str) return 0; } - // FIXME: audio-only - if (ensure_ports (ChanCount(DataType::AUDIO, nports), true, true, this)) { - return -1; + { + Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + + // FIXME: audio-only + if (ensure_ports (ChanCount(DataType::AUDIO, nports), true, this)) { + return -1; + } } string::size_type start, end, ostart; @@ -1085,7 +1128,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(); @@ -1100,50 +1143,38 @@ 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) { + BLOCK_PROCESS_CALLBACK (); + { - BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); c->connect (_bundle, _session.engine()); @@ -1166,15 +1197,16 @@ IO::connect_ports_to_bundle (boost::shared_ptr c, void* src) } } - changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */ + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ return 0; } int IO::disconnect_ports_from_bundle (boost::shared_ptr c, void* src) { + BLOCK_PROCESS_CALLBACK (); + { - BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); c->disconnect (_bundle, _session.engine()); @@ -1196,7 +1228,7 @@ IO::disconnect_ports_from_bundle (boost::shared_ptr c, void* src) } } - changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */ + changed (IOChange (IOChange::ConnectionsChanged), src); /* EMIT SIGNAL */ return 0; } @@ -1219,7 +1251,7 @@ IO::enable_connecting () void IO::bundle_changed (Bundle::Change /*c*/) { - //XXX + /* XXX */ // connect_input_ports_to_bundle (_input_bundle, this); } @@ -1258,7 +1290,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); @@ -1300,14 +1337,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); @@ -1328,8 +1365,6 @@ IO::setup_bundle () _bundle->suspend_signals (); - _bundle->set_type (default_type ()); - _bundle->remove_channels (); if (_direction == Input) { @@ -1338,10 +1373,17 @@ IO::setup_bundle () snprintf(buf, sizeof (buf), _("%s out"), _name.val().c_str()); } _bundle->set_name (buf); - uint32_t const ni = _ports.num_ports(); - for (uint32_t i = 0; i < ni; ++i) { - _bundle->add_channel (bundle_channel_name (i, ni)); - _bundle->set_port (i, _session.engine().make_port_name_non_relative (_ports.port(i)->name())); + + int c = 0; + for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) { + + uint32_t const N = _ports.count().get (*i); + for (uint32_t j = 0; j < N; ++j) { + _bundle->add_channel (bundle_channel_name (j, N, *i), *i); + _bundle->set_port (c, _session.engine().make_port_name_non_relative (_ports.port(*i, j)->name())); + ++c; + } + } _bundle->resume_signals (); @@ -1395,18 +1437,27 @@ IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr b) } std::string -IO::bundle_channel_name (uint32_t c, uint32_t n) const +IO::bundle_channel_name (uint32_t c, uint32_t n, DataType t) const { char buf[32]; - switch (n) { - case 1: - return _("mono"); - case 2: - return c == 0 ? _("L") : _("R"); - default: + if (t == DataType::AUDIO) { + + switch (n) { + case 1: + return _("mono"); + case 2: + return c == 0 ? _("L") : _("R"); + default: + snprintf (buf, sizeof(buf), _("%d"), (c + 1)); + return buf; + } + + } else { + snprintf (buf, sizeof(buf), _("%d"), (c + 1)); return buf; + } return ""; @@ -1438,13 +1489,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; } @@ -1472,19 +1523,30 @@ IO::connected_to (boost::shared_ptr other) const return false; } -void -IO::process_input (boost::shared_ptr proc, sframes_t start_frame, sframes_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; +} +/** 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); + _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()); @@ -1512,7 +1574,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 @@ -1537,21 +1599,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 @@ -1565,3 +1625,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); +}