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));
80 /** Port destructor */
88 Port::pretty_name(bool fallback_to_name) const
93 if (0 == port_engine.get_port_property (_port_handle,
94 "http://jackaudio.org/metadata/pretty-name",
100 if (fallback_to_name) {
110 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
111 port_engine.unregister_port (_port_handle);
116 /** @return true if this port is connected to anything */
118 Port::connected () const
121 return (port_engine.connected (_port_handle) != 0);
127 Port::disconnect_all ()
131 port_engine.disconnect_all (_port_handle);
132 _connections.clear ();
134 /* a cheaper, less hacky way to do boost::shared_from_this() ...
136 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
137 PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
143 /** @param o Port name
144 * @return true if this port is connected to o, otherwise false.
147 Port::connected_to (std::string const & o) const
153 if (!port_engine.available()) {
157 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
161 Port::get_connections (std::vector<std::string> & c) const
163 if (!port_engine.available()) {
164 c.insert (c.end(), _connections.begin(), _connections.end());
168 return port_engine.get_connections (_port_handle, c);
172 Port::connect (std::string const & other)
174 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
175 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
179 if (_connecting_blocked) {
183 if (sends_output ()) {
184 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
185 r = port_engine.connect (our_name, other_name);
187 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
188 r = port_engine.connect (other_name, our_name);
192 _connections.insert (other);
199 Port::disconnect (std::string const & other)
201 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
202 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
206 if (sends_output ()) {
207 r = port_engine.disconnect (this_fullname, other_fullname);
209 r = port_engine.disconnect (other_fullname, this_fullname);
213 _connections.erase (other);
216 /* a cheaper, less hacky way to do boost::shared_from_this() ...
218 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
219 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
221 if (pself && pother) {
222 /* Disconnecting from another Ardour port: need to allow
223 a check on whether this may affect anything that we
226 PostDisconnect (pself, pother); // emit signal
234 Port::connected_to (Port* o) const
236 return connected_to (o->name ());
240 Port::connect (Port* o)
242 return connect (o->name ());
246 Port::disconnect (Port* o)
248 return disconnect (o->name ());
252 Port::request_input_monitoring (bool yn)
255 port_engine.request_input_monitoring (_port_handle, yn);
260 Port::ensure_input_monitoring (bool yn)
263 port_engine.ensure_input_monitoring (_port_handle, yn);
268 Port::monitoring_input () const
271 return port_engine.monitoring_input (_port_handle);
279 _last_monitor = false;
283 Port::cycle_start (pframes_t)
285 _port_buffer_offset = 0;
289 Port::increment_port_buffer_offset (pframes_t nframes)
291 _port_buffer_offset += nframes;
295 Port::set_public_latency_range (LatencyRange& range, bool playback) const
297 /* this sets the visible latency that the rest of the port system
298 sees. because we do latency compensation, all (most) of our visible
299 port latency values are identical.
302 DEBUG_TRACE (DEBUG::Latency,
303 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
304 name(), range.min, range.max,
305 (playback ? "PLAYBACK" : "CAPTURE")));;
308 port_engine.set_latency_range (_port_handle, playback, range);
313 Port::set_private_latency_range (LatencyRange& range, bool playback)
316 _private_playback_latency = range;
317 DEBUG_TRACE (DEBUG::Latency, string_compose (
318 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
320 _private_playback_latency.min,
321 _private_playback_latency.max));
323 _private_capture_latency = range;
324 DEBUG_TRACE (DEBUG::Latency, string_compose (
325 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
327 _private_capture_latency.min,
328 _private_capture_latency.max));
331 /* push to public (port system) location so that everyone else can see it */
333 set_public_latency_range (range, playback);
337 Port::private_latency_range (bool playback) const
340 DEBUG_TRACE (DEBUG::Latency, string_compose (
341 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
343 _private_playback_latency.min,
344 _private_playback_latency.max));
345 return _private_playback_latency;
347 DEBUG_TRACE (DEBUG::Latency, string_compose (
348 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
350 _private_playback_latency.min,
351 _private_playback_latency.max));
352 return _private_capture_latency;
357 Port::public_latency_range (bool /*playback*/) const
363 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
365 DEBUG_TRACE (DEBUG::Latency, string_compose (
366 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
367 name(), r.min, r.max,
368 sends_output() ? "PLAYBACK" : "CAPTURE"));
375 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
377 vector<string> connections;
379 get_connections (connections);
381 if (!connections.empty()) {
383 range.min = ~((pframes_t) 0);
386 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
388 for (vector<string>::const_iterator c = connections.begin();
389 c != connections.end(); ++c) {
393 if (!AudioEngine::instance()->port_is_mine (*c)) {
395 /* port belongs to some other port-system client, use
396 * the port engine to lookup its latency information.
399 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
402 lr = port_engine.get_latency_range (remote_port, playback);
404 DEBUG_TRACE (DEBUG::Latency, string_compose (
405 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
406 name(), *c, lr.min, lr.max));
408 range.min = min (range.min, lr.min);
409 range.max = max (range.max, lr.max);
414 /* port belongs to this instance of ardour,
415 so look up its latency information
416 internally, because our published/public
417 values already contain our plugin
418 latency compensation.
421 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
423 lr = remote_port->private_latency_range ((playback ? true : false));
424 DEBUG_TRACE (DEBUG::Latency, string_compose (
425 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
426 name(), *c, lr.min, lr.max));
428 range.min = min (range.min, lr.min);
429 range.max = max (range.max, lr.max);
435 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
440 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
446 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
447 _port_handle = port_engine.register_port (_name, type(), _flags);
449 if (_port_handle == 0) {
450 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
463 /* caller must hold process lock; intended to be used only after reestablish() */
465 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
467 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
476 /** @param n Short port name (no port-system client name) */
478 Port::set_name (std::string const & n)
480 if (n == _name || !_port_handle) {
484 int const r = port_engine.set_port_name (_port_handle, n);
487 AudioEngine::instance()->port_renamed (_name, n);
496 Port::physically_connected () const
502 return port_engine.physically_connected (_port_handle);
506 Port::get_state () const
508 XMLNode* root = new XMLNode (state_node_name);
510 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
512 if (receives_input()) {
513 root->add_property (X_("direction"), X_("input"));
515 root->add_property (X_("direction"), X_("output"));
522 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
523 XMLNode* child = new XMLNode (X_("Connection"));
524 child->add_property (X_("other"), *i);
525 root->add_child_nocopy (*child);
532 Port::set_state (const XMLNode& node, int)
534 const XMLProperty* prop;
536 if (node.name() != state_node_name) {
540 if ((prop = node.property (X_("name"))) != 0) {
541 set_name (prop->value());
544 const XMLNodeList& children (node.children());
546 _connections.clear ();
548 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
550 if ((*c)->name() != X_("Connection")) {
554 if ((prop = (*c)->property (X_("other"))) == 0) {
558 _connections.insert (prop->value());