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_handle = port_engine.register_port (_name, t, _flags)) == 0) {
74 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
75 throw failed_constructor ();
78 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
79 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
80 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
81 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
84 /** Port destructor */
92 Port::pretty_name(bool fallback_to_name) const
97 if (0 == port_engine.get_port_property (_port_handle,
98 "http://jackaudio.org/metadata/pretty-name",
104 if (fallback_to_name) {
111 Port::set_pretty_name(const std::string& n)
114 if (0 == port_engine.set_port_property (_port_handle,
115 "http://jackaudio.org/metadata/pretty-name", n, ""))
126 engine_connection.disconnect ();
133 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
134 port_engine.unregister_port (_port_handle);
140 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
143 /* we're only interested in disconnect */
146 boost::shared_ptr<Port> p0 = w0.lock ();
147 boost::shared_ptr<Port> p1 = w1.lock ();
148 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
149 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
152 PostDisconnect (p0, p1); // emit signal
155 PostDisconnect (p1, p0); // emit signal
159 /** @return true if this port is connected to anything */
161 Port::connected () const
164 return (port_engine.connected (_port_handle) != 0);
170 Port::disconnect_all ()
174 std::vector<std::string> connections;
175 get_connections (connections);
177 port_engine.disconnect_all (_port_handle);
178 _connections.clear ();
180 /* a cheaper, less hacky way to do boost::shared_from_this() ...
182 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
183 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
184 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
186 PostDisconnect (pself, pother); // emit signal
194 /** @param o Port name
195 * @return true if this port is connected to o, otherwise false.
198 Port::connected_to (std::string const & o) const
204 if (!port_engine.available()) {
208 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
212 Port::get_connections (std::vector<std::string> & c) const
214 if (!port_engine.available()) {
215 c.insert (c.end(), _connections.begin(), _connections.end());
220 return port_engine.get_connections (_port_handle, c);
227 Port::connect (std::string const & other)
229 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
230 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
234 if (_connecting_blocked) {
238 if (sends_output ()) {
239 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
240 r = port_engine.connect (our_name, other_name);
242 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
243 r = port_engine.connect (other_name, our_name);
247 _connections.insert (other);
254 Port::disconnect (std::string const & other)
256 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
257 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
261 if (sends_output ()) {
262 r = port_engine.disconnect (this_fullname, other_fullname);
264 r = port_engine.disconnect (other_fullname, this_fullname);
268 _connections.erase (other);
271 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
272 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
273 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
275 if (pself && pother) {
276 /* Disconnecting from another Ardour port: need to allow
277 a check on whether this may affect anything that we
280 PostDisconnect (pself, pother); // emit signal
288 Port::connected_to (Port* o) const
290 return connected_to (o->name ());
294 Port::connect (Port* o)
296 return connect (o->name ());
300 Port::disconnect (Port* o)
302 return disconnect (o->name ());
306 Port::request_input_monitoring (bool yn)
309 port_engine.request_input_monitoring (_port_handle, yn);
314 Port::ensure_input_monitoring (bool yn)
317 port_engine.ensure_input_monitoring (_port_handle, yn);
322 Port::monitoring_input () const
325 return port_engine.monitoring_input (_port_handle);
333 _last_monitor = false;
337 Port::cycle_start (pframes_t)
339 _port_buffer_offset = 0;
343 Port::increment_port_buffer_offset (pframes_t nframes)
345 _port_buffer_offset += nframes;
349 Port::set_public_latency_range (LatencyRange& range, bool playback) const
351 /* this sets the visible latency that the rest of the port system
352 sees. because we do latency compensation, all (most) of our visible
353 port latency values are identical.
356 DEBUG_TRACE (DEBUG::Latency,
357 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
358 name(), range.min, range.max,
359 (playback ? "PLAYBACK" : "CAPTURE")));;
362 port_engine.set_latency_range (_port_handle, playback, range);
367 Port::set_private_latency_range (LatencyRange& range, bool playback)
370 _private_playback_latency = range;
371 DEBUG_TRACE (DEBUG::Latency, string_compose (
372 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
374 _private_playback_latency.min,
375 _private_playback_latency.max));
377 _private_capture_latency = range;
378 DEBUG_TRACE (DEBUG::Latency, string_compose (
379 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
381 _private_capture_latency.min,
382 _private_capture_latency.max));
385 /* push to public (port system) location so that everyone else can see it */
387 set_public_latency_range (range, playback);
391 Port::private_latency_range (bool playback) const
394 DEBUG_TRACE (DEBUG::Latency, string_compose (
395 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
397 _private_playback_latency.min,
398 _private_playback_latency.max));
399 return _private_playback_latency;
401 DEBUG_TRACE (DEBUG::Latency, string_compose (
402 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
404 _private_playback_latency.min,
405 _private_playback_latency.max));
406 return _private_capture_latency;
411 Port::public_latency_range (bool /*playback*/) const
417 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
419 DEBUG_TRACE (DEBUG::Latency, string_compose (
420 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
421 name(), r.min, r.max,
422 sends_output() ? "PLAYBACK" : "CAPTURE"));
429 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
431 vector<string> connections;
433 get_connections (connections);
435 if (!connections.empty()) {
437 range.min = ~((pframes_t) 0);
440 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
442 for (vector<string>::const_iterator c = connections.begin();
443 c != connections.end(); ++c) {
447 if (!AudioEngine::instance()->port_is_mine (*c)) {
449 /* port belongs to some other port-system client, use
450 * the port engine to lookup its latency information.
453 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
456 lr = port_engine.get_latency_range (remote_port, playback);
458 DEBUG_TRACE (DEBUG::Latency, string_compose (
459 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
460 name(), *c, lr.min, lr.max));
462 range.min = min (range.min, lr.min);
463 range.max = max (range.max, lr.max);
468 /* port belongs to this instance of ardour,
469 so look up its latency information
470 internally, because our published/public
471 values already contain our plugin
472 latency compensation.
475 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
477 lr = remote_port->private_latency_range ((playback ? true : false));
478 DEBUG_TRACE (DEBUG::Latency, string_compose (
479 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
480 name(), *c, lr.min, lr.max));
482 range.min = min (range.min, lr.min);
483 range.max = max (range.max, lr.max);
489 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
494 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
500 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
501 _port_handle = port_engine.register_port (_name, type(), _flags);
503 if (_port_handle == 0) {
504 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
510 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
511 boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
519 /* caller must hold process lock; intended to be used only after reestablish() */
521 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
523 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
532 /** @param n Short port name (no port-system client name) */
534 Port::set_name (std::string const & n)
536 if (n == _name || !_port_handle) {
540 int const r = port_engine.set_port_name (_port_handle, n);
543 AudioEngine::instance()->port_renamed (_name, n);
552 Port::physically_connected () const
558 return port_engine.physically_connected (_port_handle);
562 Port::get_state () const
564 XMLNode* root = new XMLNode (state_node_name);
566 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
568 if (receives_input()) {
569 root->add_property (X_("direction"), X_("input"));
571 root->add_property (X_("direction"), X_("output"));
578 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
579 XMLNode* child = new XMLNode (X_("Connection"));
580 child->add_property (X_("other"), *i);
581 root->add_child_nocopy (*child);
588 Port::set_state (const XMLNode& node, int)
590 XMLProperty const * prop;
592 if (node.name() != state_node_name) {
596 if ((prop = node.property (X_("name"))) != 0) {
597 set_name (prop->value());
600 const XMLNodeList& children (node.children());
602 _connections.clear ();
604 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
606 if ((*c)->name() != X_("Connection")) {
610 if ((prop = (*c)->property (X_("other"))) == 0) {
614 _connections.insert (prop->value());