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);
177 Port::connect (std::string const & other)
179 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
180 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
184 if (_connecting_blocked) {
188 if (sends_output ()) {
189 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
190 r = port_engine.connect (our_name, other_name);
192 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
193 r = port_engine.connect (other_name, our_name);
197 _connections.insert (other);
204 Port::disconnect (std::string const & other)
206 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
207 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
211 if (sends_output ()) {
212 r = port_engine.disconnect (this_fullname, other_fullname);
214 r = port_engine.disconnect (other_fullname, this_fullname);
218 _connections.erase (other);
221 /* a cheaper, less hacky way to do boost::shared_from_this() ...
223 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
224 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
226 if (pself && pother) {
227 /* Disconnecting from another Ardour port: need to allow
228 a check on whether this may affect anything that we
231 PostDisconnect (pself, pother); // emit signal
239 Port::connected_to (Port* o) const
241 return connected_to (o->name ());
245 Port::connect (Port* o)
247 return connect (o->name ());
251 Port::disconnect (Port* o)
253 return disconnect (o->name ());
257 Port::request_input_monitoring (bool yn)
260 port_engine.request_input_monitoring (_port_handle, yn);
265 Port::ensure_input_monitoring (bool yn)
268 port_engine.ensure_input_monitoring (_port_handle, yn);
273 Port::monitoring_input () const
276 return port_engine.monitoring_input (_port_handle);
284 _last_monitor = false;
288 Port::cycle_start (pframes_t)
290 _port_buffer_offset = 0;
294 Port::increment_port_buffer_offset (pframes_t nframes)
296 _port_buffer_offset += nframes;
300 Port::set_public_latency_range (LatencyRange& range, bool playback) const
302 /* this sets the visible latency that the rest of the port system
303 sees. because we do latency compensation, all (most) of our visible
304 port latency values are identical.
307 DEBUG_TRACE (DEBUG::Latency,
308 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
309 name(), range.min, range.max,
310 (playback ? "PLAYBACK" : "CAPTURE")));;
313 port_engine.set_latency_range (_port_handle, playback, range);
318 Port::set_private_latency_range (LatencyRange& range, bool playback)
321 _private_playback_latency = range;
322 DEBUG_TRACE (DEBUG::Latency, string_compose (
323 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
325 _private_playback_latency.min,
326 _private_playback_latency.max));
328 _private_capture_latency = range;
329 DEBUG_TRACE (DEBUG::Latency, string_compose (
330 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
332 _private_capture_latency.min,
333 _private_capture_latency.max));
336 /* push to public (port system) location so that everyone else can see it */
338 set_public_latency_range (range, playback);
342 Port::private_latency_range (bool playback) const
345 DEBUG_TRACE (DEBUG::Latency, string_compose (
346 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
348 _private_playback_latency.min,
349 _private_playback_latency.max));
350 return _private_playback_latency;
352 DEBUG_TRACE (DEBUG::Latency, string_compose (
353 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
355 _private_playback_latency.min,
356 _private_playback_latency.max));
357 return _private_capture_latency;
362 Port::public_latency_range (bool /*playback*/) const
368 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
370 DEBUG_TRACE (DEBUG::Latency, string_compose (
371 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
372 name(), r.min, r.max,
373 sends_output() ? "PLAYBACK" : "CAPTURE"));
380 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
382 vector<string> connections;
384 get_connections (connections);
386 if (!connections.empty()) {
388 range.min = ~((pframes_t) 0);
391 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
393 for (vector<string>::const_iterator c = connections.begin();
394 c != connections.end(); ++c) {
398 if (!AudioEngine::instance()->port_is_mine (*c)) {
400 /* port belongs to some other port-system client, use
401 * the port engine to lookup its latency information.
404 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
407 lr = port_engine.get_latency_range (remote_port, playback);
409 DEBUG_TRACE (DEBUG::Latency, string_compose (
410 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
411 name(), *c, lr.min, lr.max));
413 range.min = min (range.min, lr.min);
414 range.max = max (range.max, lr.max);
419 /* port belongs to this instance of ardour,
420 so look up its latency information
421 internally, because our published/public
422 values already contain our plugin
423 latency compensation.
426 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
428 lr = remote_port->private_latency_range ((playback ? true : false));
429 DEBUG_TRACE (DEBUG::Latency, string_compose (
430 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
431 name(), *c, lr.min, lr.max));
433 range.min = min (range.min, lr.min);
434 range.max = max (range.max, lr.max);
440 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
445 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
451 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
452 _port_handle = port_engine.register_port (_name, type(), _flags);
454 if (_port_handle == 0) {
455 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
468 /* caller must hold process lock; intended to be used only after reestablish() */
470 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
472 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
481 /** @param n Short port name (no port-system client name) */
483 Port::set_name (std::string const & n)
485 if (n == _name || !_port_handle) {
489 int const r = port_engine.set_port_name (_port_handle, n);
492 AudioEngine::instance()->port_renamed (_name, n);
501 Port::physically_connected () const
507 return port_engine.physically_connected (_port_handle);
511 Port::get_state () const
513 XMLNode* root = new XMLNode (state_node_name);
515 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
517 if (receives_input()) {
518 root->add_property (X_("direction"), X_("input"));
520 root->add_property (X_("direction"), X_("output"));
527 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
528 XMLNode* child = new XMLNode (X_("Connection"));
529 child->add_property (X_("other"), *i);
530 root->add_child_nocopy (*child);
537 Port::set_state (const XMLNode& node, int)
539 const XMLProperty* prop;
541 if (node.name() != state_node_name) {
545 if ((prop = node.property (X_("name"))) != 0) {
546 set_name (prop->value());
549 const XMLNodeList& children (node.children());
551 _connections.clear ();
553 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
555 if ((*c)->name() != X_("Connection")) {
559 if ((prop = (*c)->property (X_("other"))) == 0) {
563 _connections.insert (prop->value());