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());
169 return port_engine.get_connections (_port_handle, c);
176 Port::connect (std::string const & other)
178 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
179 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
183 if (_connecting_blocked) {
187 if (sends_output ()) {
188 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
189 r = port_engine.connect (our_name, other_name);
191 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
192 r = port_engine.connect (other_name, our_name);
196 _connections.insert (other);
203 Port::disconnect (std::string const & other)
205 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
206 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
210 if (sends_output ()) {
211 r = port_engine.disconnect (this_fullname, other_fullname);
213 r = port_engine.disconnect (other_fullname, this_fullname);
217 _connections.erase (other);
220 /* a cheaper, less hacky way to do boost::shared_from_this() ...
222 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
223 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
225 if (pself && pother) {
226 /* Disconnecting from another Ardour port: need to allow
227 a check on whether this may affect anything that we
230 PostDisconnect (pself, pother); // emit signal
238 Port::connected_to (Port* o) const
240 return connected_to (o->name ());
244 Port::connect (Port* o)
246 return connect (o->name ());
250 Port::disconnect (Port* o)
252 return disconnect (o->name ());
256 Port::request_input_monitoring (bool yn)
259 port_engine.request_input_monitoring (_port_handle, yn);
264 Port::ensure_input_monitoring (bool yn)
267 port_engine.ensure_input_monitoring (_port_handle, yn);
272 Port::monitoring_input () const
275 return port_engine.monitoring_input (_port_handle);
283 _last_monitor = false;
287 Port::cycle_start (pframes_t)
289 _port_buffer_offset = 0;
293 Port::increment_port_buffer_offset (pframes_t nframes)
295 _port_buffer_offset += nframes;
299 Port::set_public_latency_range (LatencyRange& range, bool playback) const
301 /* this sets the visible latency that the rest of the port system
302 sees. because we do latency compensation, all (most) of our visible
303 port latency values are identical.
306 DEBUG_TRACE (DEBUG::Latency,
307 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
308 name(), range.min, range.max,
309 (playback ? "PLAYBACK" : "CAPTURE")));;
312 port_engine.set_latency_range (_port_handle, playback, range);
317 Port::set_private_latency_range (LatencyRange& range, bool playback)
320 _private_playback_latency = range;
321 DEBUG_TRACE (DEBUG::Latency, string_compose (
322 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
324 _private_playback_latency.min,
325 _private_playback_latency.max));
327 _private_capture_latency = range;
328 DEBUG_TRACE (DEBUG::Latency, string_compose (
329 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
331 _private_capture_latency.min,
332 _private_capture_latency.max));
335 /* push to public (port system) location so that everyone else can see it */
337 set_public_latency_range (range, playback);
341 Port::private_latency_range (bool playback) const
344 DEBUG_TRACE (DEBUG::Latency, string_compose (
345 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
347 _private_playback_latency.min,
348 _private_playback_latency.max));
349 return _private_playback_latency;
351 DEBUG_TRACE (DEBUG::Latency, string_compose (
352 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
354 _private_playback_latency.min,
355 _private_playback_latency.max));
356 return _private_capture_latency;
361 Port::public_latency_range (bool /*playback*/) const
367 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
369 DEBUG_TRACE (DEBUG::Latency, string_compose (
370 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
371 name(), r.min, r.max,
372 sends_output() ? "PLAYBACK" : "CAPTURE"));
379 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
381 vector<string> connections;
383 get_connections (connections);
385 if (!connections.empty()) {
387 range.min = ~((pframes_t) 0);
390 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
392 for (vector<string>::const_iterator c = connections.begin();
393 c != connections.end(); ++c) {
397 if (!AudioEngine::instance()->port_is_mine (*c)) {
399 /* port belongs to some other port-system client, use
400 * the port engine to lookup its latency information.
403 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
406 lr = port_engine.get_latency_range (remote_port, playback);
408 DEBUG_TRACE (DEBUG::Latency, string_compose (
409 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
410 name(), *c, lr.min, lr.max));
412 range.min = min (range.min, lr.min);
413 range.max = max (range.max, lr.max);
418 /* port belongs to this instance of ardour,
419 so look up its latency information
420 internally, because our published/public
421 values already contain our plugin
422 latency compensation.
425 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
427 lr = remote_port->private_latency_range ((playback ? true : false));
428 DEBUG_TRACE (DEBUG::Latency, string_compose (
429 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
430 name(), *c, lr.min, lr.max));
432 range.min = min (range.min, lr.min);
433 range.max = max (range.max, lr.max);
439 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
444 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
450 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
451 _port_handle = port_engine.register_port (_name, type(), _flags);
453 if (_port_handle == 0) {
454 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
467 /* caller must hold process lock; intended to be used only after reestablish() */
469 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
471 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
480 /** @param n Short port name (no port-system client name) */
482 Port::set_name (std::string const & n)
484 if (n == _name || !_port_handle) {
488 int const r = port_engine.set_port_name (_port_handle, n);
491 AudioEngine::instance()->port_renamed (_name, n);
500 Port::physically_connected () const
506 return port_engine.physically_connected (_port_handle);
510 Port::get_state () const
512 XMLNode* root = new XMLNode (state_node_name);
514 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
516 if (receives_input()) {
517 root->add_property (X_("direction"), X_("input"));
519 root->add_property (X_("direction"), X_("output"));
526 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
527 XMLNode* child = new XMLNode (X_("Connection"));
528 child->add_property (X_("other"), *i);
529 root->add_child_nocopy (*child);
536 Port::set_state (const XMLNode& node, int)
538 const XMLProperty* prop;
540 if (node.name() != state_node_name) {
544 if ((prop = node.property (X_("name"))) != 0) {
545 set_name (prop->value());
548 const XMLNodeList& children (node.children());
550 _connections.clear ();
552 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
554 if ((*c)->name() != X_("Connection")) {
558 if ((prop = (*c)->property (X_("other"))) == 0) {
562 _connections.insert (prop->value());