2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "libardour-config.h"
24 #include "pbd/compose.h"
25 #include "pbd/error.h"
26 #include "pbd/failed_constructor.h"
28 #include "ardour/audioengine.h"
29 #include "ardour/debug.h"
30 #include "ardour/port.h"
31 #include "ardour/port_engine.h"
36 using namespace ARDOUR;
39 PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
40 PBD::Signal0<void> Port::PortDrop;
41 PBD::Signal0<void> Port::PortSignalDrop;
43 bool Port::_connecting_blocked = false;
44 pframes_t Port::_global_port_buffer_offset = 0;
45 pframes_t Port::_cycle_nframes = 0;
46 std::string Port::state_node_name = X_("Port");
48 /* a handy define to shorten what would otherwise be a needlessly verbose
51 #define port_engine AudioEngine::instance()->port_engine()
52 #define port_manager AudioEngine::instance()
54 /** @param n Port short name */
55 Port::Port (std::string const & n, DataType t, PortFlags f)
56 : _port_buffer_offset (0)
59 , _last_monitor (false)
61 _private_playback_latency.min = 0;
62 _private_playback_latency.max = 0;
63 _private_capture_latency.min = 0;
64 _private_capture_latency.max = 0;
66 /* Unfortunately we have to pass the DataType into this constructor so that
67 we can create the right kind of port; aside from this we'll use the
68 virtual function type () to establish type.
71 assert (_name.find_first_of (':') == std::string::npos);
73 if (!port_engine.available ()) {
74 DEBUG_TRACE (DEBUG::Ports, string_compose ("port-engine n/a postpone registering %1\n", name()));
75 _port_handle = 0; // created during ::reestablish() later
76 } else if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
77 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
78 throw failed_constructor ();
80 DEBUG_TRACE (DEBUG::Ports, string_compose ("registed port %1 handle %2\n", name(), _port_handle));
82 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
83 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
84 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
85 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
88 /** Port destructor */
96 Port::pretty_name(bool fallback_to_name) const
101 if (0 == port_engine.get_port_property (_port_handle,
102 "http://jackaudio.org/metadata/pretty-name",
108 if (fallback_to_name) {
115 Port::set_pretty_name(const std::string& n)
118 if (0 == port_engine.set_port_property (_port_handle,
119 "http://jackaudio.org/metadata/pretty-name", n, ""))
130 engine_connection.disconnect ();
137 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
138 port_engine.unregister_port (_port_handle);
144 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
147 /* we're only interested in disconnect */
150 boost::shared_ptr<Port> p0 = w0.lock ();
151 boost::shared_ptr<Port> p1 = w1.lock ();
152 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
153 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
156 PostDisconnect (p0, p1); // emit signal
159 PostDisconnect (p1, p0); // emit signal
163 /** @return true if this port is connected to anything */
165 Port::connected () const
168 return (port_engine.connected (_port_handle) != 0);
174 Port::disconnect_all ()
178 std::vector<std::string> connections;
179 get_connections (connections);
181 port_engine.disconnect_all (_port_handle);
182 _connections.clear ();
184 /* a cheaper, less hacky way to do boost::shared_from_this() ...
186 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
187 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
188 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
190 PostDisconnect (pself, pother); // emit signal
198 /** @param o Port name
199 * @return true if this port is connected to o, otherwise false.
202 Port::connected_to (std::string const & o) const
208 if (!port_engine.available()) {
212 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
216 Port::get_connections (std::vector<std::string> & c) const
218 if (!port_engine.available()) {
219 c.insert (c.end(), _connections.begin(), _connections.end());
224 return port_engine.get_connections (_port_handle, c);
231 Port::connect (std::string const & other)
233 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
234 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
238 if (_connecting_blocked) {
242 if (sends_output ()) {
243 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
244 r = port_engine.connect (our_name, other_name);
246 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
247 r = port_engine.connect (other_name, our_name);
251 _connections.insert (other);
258 Port::disconnect (std::string const & other)
260 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
261 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
265 if (sends_output ()) {
266 r = port_engine.disconnect (this_fullname, other_fullname);
268 r = port_engine.disconnect (other_fullname, this_fullname);
272 _connections.erase (other);
275 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
276 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
277 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
279 if (pself && pother) {
280 /* Disconnecting from another Ardour port: need to allow
281 a check on whether this may affect anything that we
284 PostDisconnect (pself, pother); // emit signal
292 Port::connected_to (Port* o) const
294 return connected_to (o->name ());
298 Port::connect (Port* o)
300 return connect (o->name ());
304 Port::disconnect (Port* o)
306 return disconnect (o->name ());
310 Port::request_input_monitoring (bool yn)
313 port_engine.request_input_monitoring (_port_handle, yn);
318 Port::ensure_input_monitoring (bool yn)
321 port_engine.ensure_input_monitoring (_port_handle, yn);
326 Port::monitoring_input () const
329 return port_engine.monitoring_input (_port_handle);
337 _last_monitor = false;
341 Port::cycle_start (pframes_t)
343 _port_buffer_offset = 0;
347 Port::increment_port_buffer_offset (pframes_t nframes)
349 _port_buffer_offset += nframes;
353 Port::set_public_latency_range (LatencyRange& range, bool playback) const
355 /* this sets the visible latency that the rest of the port system
356 sees. because we do latency compensation, all (most) of our visible
357 port latency values are identical.
360 DEBUG_TRACE (DEBUG::Latency,
361 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
362 name(), range.min, range.max,
363 (playback ? "PLAYBACK" : "CAPTURE")));;
366 port_engine.set_latency_range (_port_handle, playback, range);
371 Port::set_private_latency_range (LatencyRange& range, bool playback)
374 _private_playback_latency = range;
375 DEBUG_TRACE (DEBUG::Latency, string_compose (
376 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
378 _private_playback_latency.min,
379 _private_playback_latency.max));
381 _private_capture_latency = range;
382 DEBUG_TRACE (DEBUG::Latency, string_compose (
383 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
385 _private_capture_latency.min,
386 _private_capture_latency.max));
389 /* push to public (port system) location so that everyone else can see it */
391 set_public_latency_range (range, playback);
395 Port::private_latency_range (bool playback) const
398 DEBUG_TRACE (DEBUG::Latency, string_compose (
399 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
401 _private_playback_latency.min,
402 _private_playback_latency.max));
403 return _private_playback_latency;
405 DEBUG_TRACE (DEBUG::Latency, string_compose (
406 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
408 _private_playback_latency.min,
409 _private_playback_latency.max));
410 return _private_capture_latency;
415 Port::public_latency_range (bool /*playback*/) const
421 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
423 DEBUG_TRACE (DEBUG::Latency, string_compose (
424 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
425 name(), r.min, r.max,
426 sends_output() ? "PLAYBACK" : "CAPTURE"));
433 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
435 vector<string> connections;
437 get_connections (connections);
439 if (!connections.empty()) {
441 range.min = ~((pframes_t) 0);
444 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
446 for (vector<string>::const_iterator c = connections.begin();
447 c != connections.end(); ++c) {
451 if (!AudioEngine::instance()->port_is_mine (*c)) {
453 /* port belongs to some other port-system client, use
454 * the port engine to lookup its latency information.
457 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
460 lr = port_engine.get_latency_range (remote_port, playback);
462 DEBUG_TRACE (DEBUG::Latency, string_compose (
463 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
464 name(), *c, lr.min, lr.max));
466 range.min = min (range.min, lr.min);
467 range.max = max (range.max, lr.max);
472 /* port belongs to this instance of ardour,
473 so look up its latency information
474 internally, because our published/public
475 values already contain our plugin
476 latency compensation.
479 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
481 lr = remote_port->private_latency_range ((playback ? true : false));
482 DEBUG_TRACE (DEBUG::Latency, string_compose (
483 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
484 name(), *c, lr.min, lr.max));
486 range.min = min (range.min, lr.min);
487 range.max = max (range.max, lr.max);
493 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
498 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
504 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
505 _port_handle = port_engine.register_port (_name, type(), _flags);
507 if (_port_handle == 0) {
508 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
512 DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reestablish %1 handle %2\n", name(), _port_handle));
516 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
517 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
525 /* caller must hold process lock; intended to be used only after reestablish() */
527 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
529 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
538 /** @param n Short port name (no port-system client name) */
540 Port::set_name (std::string const & n)
542 if (n == _name || !_port_handle) {
546 int const r = port_engine.set_port_name (_port_handle, n);
549 AudioEngine::instance()->port_renamed (_name, n);
558 Port::physically_connected () const
564 return port_engine.physically_connected (_port_handle);
568 Port::get_state () const
570 XMLNode* root = new XMLNode (state_node_name);
572 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
574 if (receives_input()) {
575 root->set_property (X_("direction"), X_("input"));
577 root->set_property (X_("direction"), X_("output"));
584 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
585 XMLNode* child = new XMLNode (X_("Connection"));
586 child->set_property (X_("other"), *i);
587 root->add_child_nocopy (*child);
594 Port::set_state (const XMLNode& node, int)
596 if (node.name() != state_node_name) {
601 if (node.get_property (X_("name"), str)) {
605 const XMLNodeList& children (node.children());
607 _connections.clear ();
609 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
611 if ((*c)->name() != X_("Connection")) {
615 if (!(*c)->get_property (X_("other"), str)) {
619 _connections.insert (str);