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 _port_handle = 0; // created during ::reestablish() later
75 } else if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
76 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
77 throw failed_constructor ();
80 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
81 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
82 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
83 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
86 /** Port destructor */
94 Port::pretty_name(bool fallback_to_name) const
99 if (0 == port_engine.get_port_property (_port_handle,
100 "http://jackaudio.org/metadata/pretty-name",
106 if (fallback_to_name) {
113 Port::set_pretty_name(const std::string& n)
116 if (0 == port_engine.set_port_property (_port_handle,
117 "http://jackaudio.org/metadata/pretty-name", n, ""))
128 engine_connection.disconnect ();
135 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
136 port_engine.unregister_port (_port_handle);
142 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
145 /* we're only interested in disconnect */
148 boost::shared_ptr<Port> p0 = w0.lock ();
149 boost::shared_ptr<Port> p1 = w1.lock ();
150 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
151 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
154 PostDisconnect (p0, p1); // emit signal
157 PostDisconnect (p1, p0); // emit signal
161 /** @return true if this port is connected to anything */
163 Port::connected () const
166 return (port_engine.connected (_port_handle) != 0);
172 Port::disconnect_all ()
176 std::vector<std::string> connections;
177 get_connections (connections);
179 port_engine.disconnect_all (_port_handle);
180 _connections.clear ();
182 /* a cheaper, less hacky way to do boost::shared_from_this() ...
184 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
185 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
186 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
188 PostDisconnect (pself, pother); // emit signal
196 /** @param o Port name
197 * @return true if this port is connected to o, otherwise false.
200 Port::connected_to (std::string const & o) const
206 if (!port_engine.available()) {
210 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
214 Port::get_connections (std::vector<std::string> & c) const
216 if (!port_engine.available()) {
217 c.insert (c.end(), _connections.begin(), _connections.end());
222 return port_engine.get_connections (_port_handle, c);
229 Port::connect (std::string const & other)
231 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
232 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
236 if (_connecting_blocked) {
240 if (sends_output ()) {
241 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
242 r = port_engine.connect (our_name, other_name);
244 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
245 r = port_engine.connect (other_name, our_name);
249 _connections.insert (other);
256 Port::disconnect (std::string const & other)
258 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
259 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
263 if (sends_output ()) {
264 r = port_engine.disconnect (this_fullname, other_fullname);
266 r = port_engine.disconnect (other_fullname, this_fullname);
270 _connections.erase (other);
273 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
274 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
275 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
277 if (pself && pother) {
278 /* Disconnecting from another Ardour port: need to allow
279 a check on whether this may affect anything that we
282 PostDisconnect (pself, pother); // emit signal
290 Port::connected_to (Port* o) const
292 return connected_to (o->name ());
296 Port::connect (Port* o)
298 return connect (o->name ());
302 Port::disconnect (Port* o)
304 return disconnect (o->name ());
308 Port::request_input_monitoring (bool yn)
311 port_engine.request_input_monitoring (_port_handle, yn);
316 Port::ensure_input_monitoring (bool yn)
319 port_engine.ensure_input_monitoring (_port_handle, yn);
324 Port::monitoring_input () const
327 return port_engine.monitoring_input (_port_handle);
335 _last_monitor = false;
339 Port::cycle_start (pframes_t)
341 _port_buffer_offset = 0;
345 Port::increment_port_buffer_offset (pframes_t nframes)
347 _port_buffer_offset += nframes;
351 Port::set_public_latency_range (LatencyRange& range, bool playback) const
353 /* this sets the visible latency that the rest of the port system
354 sees. because we do latency compensation, all (most) of our visible
355 port latency values are identical.
358 DEBUG_TRACE (DEBUG::Latency,
359 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
360 name(), range.min, range.max,
361 (playback ? "PLAYBACK" : "CAPTURE")));;
364 port_engine.set_latency_range (_port_handle, playback, range);
369 Port::set_private_latency_range (LatencyRange& range, bool playback)
372 _private_playback_latency = range;
373 DEBUG_TRACE (DEBUG::Latency, string_compose (
374 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
376 _private_playback_latency.min,
377 _private_playback_latency.max));
379 _private_capture_latency = range;
380 DEBUG_TRACE (DEBUG::Latency, string_compose (
381 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
383 _private_capture_latency.min,
384 _private_capture_latency.max));
387 /* push to public (port system) location so that everyone else can see it */
389 set_public_latency_range (range, playback);
393 Port::private_latency_range (bool playback) const
396 DEBUG_TRACE (DEBUG::Latency, string_compose (
397 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
399 _private_playback_latency.min,
400 _private_playback_latency.max));
401 return _private_playback_latency;
403 DEBUG_TRACE (DEBUG::Latency, string_compose (
404 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
406 _private_playback_latency.min,
407 _private_playback_latency.max));
408 return _private_capture_latency;
413 Port::public_latency_range (bool /*playback*/) const
419 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
421 DEBUG_TRACE (DEBUG::Latency, string_compose (
422 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
423 name(), r.min, r.max,
424 sends_output() ? "PLAYBACK" : "CAPTURE"));
431 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
433 vector<string> connections;
435 get_connections (connections);
437 if (!connections.empty()) {
439 range.min = ~((pframes_t) 0);
442 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
444 for (vector<string>::const_iterator c = connections.begin();
445 c != connections.end(); ++c) {
449 if (!AudioEngine::instance()->port_is_mine (*c)) {
451 /* port belongs to some other port-system client, use
452 * the port engine to lookup its latency information.
455 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
458 lr = port_engine.get_latency_range (remote_port, playback);
460 DEBUG_TRACE (DEBUG::Latency, string_compose (
461 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
462 name(), *c, lr.min, lr.max));
464 range.min = min (range.min, lr.min);
465 range.max = max (range.max, lr.max);
470 /* port belongs to this instance of ardour,
471 so look up its latency information
472 internally, because our published/public
473 values already contain our plugin
474 latency compensation.
477 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
479 lr = remote_port->private_latency_range ((playback ? true : false));
480 DEBUG_TRACE (DEBUG::Latency, string_compose (
481 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
482 name(), *c, lr.min, lr.max));
484 range.min = min (range.min, lr.min);
485 range.max = max (range.max, lr.max);
491 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
496 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
502 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
503 _port_handle = port_engine.register_port (_name, type(), _flags);
505 if (_port_handle == 0) {
506 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
512 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
513 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
521 /* caller must hold process lock; intended to be used only after reestablish() */
523 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
525 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
534 /** @param n Short port name (no port-system client name) */
536 Port::set_name (std::string const & n)
538 if (n == _name || !_port_handle) {
542 int const r = port_engine.set_port_name (_port_handle, n);
545 AudioEngine::instance()->port_renamed (_name, n);
554 Port::physically_connected () const
560 return port_engine.physically_connected (_port_handle);
564 Port::get_state () const
566 XMLNode* root = new XMLNode (state_node_name);
568 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
570 if (receives_input()) {
571 root->set_property (X_("direction"), X_("input"));
573 root->set_property (X_("direction"), X_("output"));
580 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
581 XMLNode* child = new XMLNode (X_("Connection"));
582 child->set_property (X_("other"), *i);
583 root->add_child_nocopy (*child);
590 Port::set_state (const XMLNode& node, int)
592 if (node.name() != state_node_name) {
597 if (node.get_property (X_("name"), str)) {
601 const XMLNodeList& children (node.children());
603 _connections.clear ();
605 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
607 if ((*c)->name() != X_("Connection")) {
611 if (!(*c)->get_property (X_("other"), str)) {
615 _connections.insert (str);