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;
42 bool Port::_connecting_blocked = false;
43 pframes_t Port::_global_port_buffer_offset = 0;
44 pframes_t Port::_cycle_nframes = 0;
45 std::string Port::state_node_name = X_("Port");
47 /* a handy define to shorten what would otherwise be a needlessly verbose
50 #define port_engine AudioEngine::instance()->port_engine()
51 #define port_manager AudioEngine::instance()
53 /** @param n Port short name */
54 Port::Port (std::string const & n, DataType t, PortFlags f)
55 : _port_buffer_offset (0)
58 , _last_monitor (false)
60 _private_playback_latency.min = 0;
61 _private_playback_latency.max = 0;
62 _private_capture_latency.min = 0;
63 _private_capture_latency.max = 0;
65 /* Unfortunately we have to pass the DataType into this constructor so that
66 we can create the right kind of port; aside from this we'll use the
67 virtual function type () to establish type.
70 assert (_name.find_first_of (':') == std::string::npos);
72 if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
73 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
74 throw failed_constructor ();
77 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
78 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
79 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
82 /** Port destructor */
90 Port::pretty_name(bool fallback_to_name) const
95 if (0 == port_engine.get_port_property (_port_handle,
96 "http://jackaudio.org/metadata/pretty-name",
102 if (fallback_to_name) {
109 Port::set_pretty_name(const std::string& n)
112 if (0 == port_engine.set_port_property (_port_handle,
113 "http://jackaudio.org/metadata/pretty-name", n, ""))
125 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
126 port_engine.unregister_port (_port_handle);
132 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
135 /* we're only interested in disconnect */
138 boost::shared_ptr<Port> p0 = w0.lock ();
139 boost::shared_ptr<Port> p1 = w1.lock ();
140 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
141 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
144 PostDisconnect (p0, p1); // emit signal
147 PostDisconnect (p1, p0); // emit signal
151 /** @return true if this port is connected to anything */
153 Port::connected () const
156 return (port_engine.connected (_port_handle) != 0);
162 Port::disconnect_all ()
166 std::vector<std::string> connections;
167 get_connections (connections);
169 port_engine.disconnect_all (_port_handle);
170 _connections.clear ();
172 /* a cheaper, less hacky way to do boost::shared_from_this() ...
174 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
175 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
176 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
178 PostDisconnect (pself, pother); // emit signal
186 /** @param o Port name
187 * @return true if this port is connected to o, otherwise false.
190 Port::connected_to (std::string const & o) const
196 if (!port_engine.available()) {
200 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
204 Port::get_connections (std::vector<std::string> & c) const
206 if (!port_engine.available()) {
207 c.insert (c.end(), _connections.begin(), _connections.end());
212 return port_engine.get_connections (_port_handle, c);
219 Port::connect (std::string const & other)
221 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
222 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
226 if (_connecting_blocked) {
230 if (sends_output ()) {
231 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
232 r = port_engine.connect (our_name, other_name);
234 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
235 r = port_engine.connect (other_name, our_name);
239 _connections.insert (other);
246 Port::disconnect (std::string const & other)
248 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
249 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
253 if (sends_output ()) {
254 r = port_engine.disconnect (this_fullname, other_fullname);
256 r = port_engine.disconnect (other_fullname, this_fullname);
260 _connections.erase (other);
263 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
264 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
265 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
267 if (pself && pother) {
268 /* Disconnecting from another Ardour port: need to allow
269 a check on whether this may affect anything that we
272 PostDisconnect (pself, pother); // emit signal
280 Port::connected_to (Port* o) const
282 return connected_to (o->name ());
286 Port::connect (Port* o)
288 return connect (o->name ());
292 Port::disconnect (Port* o)
294 return disconnect (o->name ());
298 Port::request_input_monitoring (bool yn)
301 port_engine.request_input_monitoring (_port_handle, yn);
306 Port::ensure_input_monitoring (bool yn)
309 port_engine.ensure_input_monitoring (_port_handle, yn);
314 Port::monitoring_input () const
317 return port_engine.monitoring_input (_port_handle);
325 _last_monitor = false;
329 Port::cycle_start (pframes_t)
331 _port_buffer_offset = 0;
335 Port::increment_port_buffer_offset (pframes_t nframes)
337 _port_buffer_offset += nframes;
341 Port::set_public_latency_range (LatencyRange& range, bool playback) const
343 /* this sets the visible latency that the rest of the port system
344 sees. because we do latency compensation, all (most) of our visible
345 port latency values are identical.
348 DEBUG_TRACE (DEBUG::Latency,
349 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
350 name(), range.min, range.max,
351 (playback ? "PLAYBACK" : "CAPTURE")));;
354 port_engine.set_latency_range (_port_handle, playback, range);
359 Port::set_private_latency_range (LatencyRange& range, bool playback)
362 _private_playback_latency = range;
363 DEBUG_TRACE (DEBUG::Latency, string_compose (
364 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
366 _private_playback_latency.min,
367 _private_playback_latency.max));
369 _private_capture_latency = range;
370 DEBUG_TRACE (DEBUG::Latency, string_compose (
371 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
373 _private_capture_latency.min,
374 _private_capture_latency.max));
377 /* push to public (port system) location so that everyone else can see it */
379 set_public_latency_range (range, playback);
383 Port::private_latency_range (bool playback) const
386 DEBUG_TRACE (DEBUG::Latency, string_compose (
387 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
389 _private_playback_latency.min,
390 _private_playback_latency.max));
391 return _private_playback_latency;
393 DEBUG_TRACE (DEBUG::Latency, string_compose (
394 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
396 _private_playback_latency.min,
397 _private_playback_latency.max));
398 return _private_capture_latency;
403 Port::public_latency_range (bool /*playback*/) const
409 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
411 DEBUG_TRACE (DEBUG::Latency, string_compose (
412 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
413 name(), r.min, r.max,
414 sends_output() ? "PLAYBACK" : "CAPTURE"));
421 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
423 vector<string> connections;
425 get_connections (connections);
427 if (!connections.empty()) {
429 range.min = ~((pframes_t) 0);
432 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
434 for (vector<string>::const_iterator c = connections.begin();
435 c != connections.end(); ++c) {
439 if (!AudioEngine::instance()->port_is_mine (*c)) {
441 /* port belongs to some other port-system client, use
442 * the port engine to lookup its latency information.
445 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
448 lr = port_engine.get_latency_range (remote_port, playback);
450 DEBUG_TRACE (DEBUG::Latency, string_compose (
451 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
452 name(), *c, lr.min, lr.max));
454 range.min = min (range.min, lr.min);
455 range.max = max (range.max, lr.max);
460 /* port belongs to this instance of ardour,
461 so look up its latency information
462 internally, because our published/public
463 values already contain our plugin
464 latency compensation.
467 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
469 lr = remote_port->private_latency_range ((playback ? true : false));
470 DEBUG_TRACE (DEBUG::Latency, string_compose (
471 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
472 name(), *c, lr.min, lr.max));
474 range.min = min (range.min, lr.min);
475 range.max = max (range.max, lr.max);
481 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
486 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
492 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
493 _port_handle = port_engine.register_port (_name, type(), _flags);
495 if (_port_handle == 0) {
496 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
502 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
503 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
511 /* caller must hold process lock; intended to be used only after reestablish() */
513 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
515 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
524 /** @param n Short port name (no port-system client name) */
526 Port::set_name (std::string const & n)
528 if (n == _name || !_port_handle) {
532 int const r = port_engine.set_port_name (_port_handle, n);
535 AudioEngine::instance()->port_renamed (_name, n);
544 Port::physically_connected () const
550 return port_engine.physically_connected (_port_handle);
554 Port::get_state () const
556 XMLNode* root = new XMLNode (state_node_name);
558 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
560 if (receives_input()) {
561 root->add_property (X_("direction"), X_("input"));
563 root->add_property (X_("direction"), X_("output"));
570 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
571 XMLNode* child = new XMLNode (X_("Connection"));
572 child->add_property (X_("other"), *i);
573 root->add_child_nocopy (*child);
580 Port::set_state (const XMLNode& node, int)
582 const XMLProperty* prop;
584 if (node.name() != state_node_name) {
588 if ((prop = node.property (X_("name"))) != 0) {
589 set_name (prop->value());
592 const XMLNodeList& children (node.children());
594 _connections.clear ();
596 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
598 if ((*c)->name() != X_("Connection")) {
602 if ((prop = (*c)->property (X_("other"))) == 0) {
606 _connections.insert (prop->value());