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 double Port::_speed_ratio = 1.0;
47 std::string Port::state_node_name = X_("Port");
48 const uint32_t Port::_resampler_quality = 12;
50 /* a handy define to shorten what would otherwise be a needlessly verbose
53 #define port_engine AudioEngine::instance()->port_engine()
54 #define port_manager AudioEngine::instance()
56 /** @param n Port short name */
57 Port::Port (std::string const & n, DataType t, PortFlags f)
60 , _last_monitor (false)
61 , _externally_connected (0)
63 _private_playback_latency.min = 0;
64 _private_playback_latency.max = 0;
65 _private_capture_latency.min = 0;
66 _private_capture_latency.max = 0;
68 /* Unfortunately we have to pass the DataType into this constructor so that
69 we can create the right kind of port; aside from this we'll use the
70 virtual function type () to establish type.
73 assert (_name.find_first_of (':') == std::string::npos);
75 if (!port_engine.available ()) {
76 DEBUG_TRACE (DEBUG::Ports, string_compose ("port-engine n/a postpone registering %1\n", name()));
77 _port_handle = 0; // created during ::reestablish() later
78 } else if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
79 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
80 throw failed_constructor ();
82 DEBUG_TRACE (DEBUG::Ports, string_compose ("registed port %1 handle %2\n", name(), _port_handle));
84 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
85 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
86 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
89 /** Port destructor */
96 Port::pretty_name(bool fallback_to_name) const
101 if (0 == port_engine.get_port_property (_port_handle,
102 "http://jackaudio.org/metadata/pretty-name",
108 if (fallback_to_name) {
115 Port::set_pretty_name(const std::string& n)
118 if (0 == port_engine.set_port_property (_port_handle,
119 "http://jackaudio.org/metadata/pretty-name", n, ""))
130 engine_connection.disconnect ();
137 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
138 port_engine.unregister_port (_port_handle);
144 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
147 /* we're only interested in disconnect */
150 boost::shared_ptr<Port> p0 = w0.lock ();
151 boost::shared_ptr<Port> p1 = w1.lock ();
152 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
153 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
156 PostDisconnect (p0, p1); // emit signal
159 PostDisconnect (p1, p0); // emit signal
163 /** @return true if this port is connected to anything */
165 Port::connected () const
168 return (port_engine.connected (_port_handle) != 0);
174 Port::disconnect_all ()
178 std::vector<std::string> connections;
179 get_connections (connections);
181 port_engine.disconnect_all (_port_handle);
182 _connections.clear ();
184 /* a cheaper, less hacky way to do boost::shared_from_this() ...
186 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
187 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
188 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
190 PostDisconnect (pself, pother); // emit signal
198 /** @param o Port name
199 * @return true if this port is connected to o, otherwise false.
202 Port::connected_to (std::string const & o) const
208 if (!port_engine.available()) {
212 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
216 Port::get_connections (std::vector<std::string> & c) const
218 if (!port_engine.available()) {
219 c.insert (c.end(), _connections.begin(), _connections.end());
224 return port_engine.get_connections (_port_handle, c);
231 Port::connect (std::string const & other)
233 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
234 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
238 if (_connecting_blocked) {
242 if (sends_output ()) {
243 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
244 r = port_engine.connect (our_name, other_name);
246 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
247 r = port_engine.connect (other_name, our_name);
251 _connections.insert (other);
258 Port::disconnect (std::string const & other)
260 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
261 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
265 if (sends_output ()) {
266 r = port_engine.disconnect (this_fullname, other_fullname);
268 r = port_engine.disconnect (other_fullname, this_fullname);
272 _connections.erase (other);
275 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
276 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
277 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
279 if (pself && pother) {
280 /* Disconnecting from another Ardour port: need to allow
281 a check on whether this may affect anything that we
284 PostDisconnect (pself, pother); // emit signal
292 Port::connected_to (Port* o) const
294 return connected_to (o->name ());
298 Port::connect (Port* o)
300 return connect (o->name ());
304 Port::disconnect (Port* o)
306 return disconnect (o->name ());
310 Port::request_input_monitoring (bool yn)
313 port_engine.request_input_monitoring (_port_handle, yn);
318 Port::ensure_input_monitoring (bool yn)
321 port_engine.ensure_input_monitoring (_port_handle, yn);
326 Port::monitoring_input () const
329 return port_engine.monitoring_input (_port_handle);
337 _last_monitor = false;
341 Port::cycle_start (pframes_t)
346 Port::set_public_latency_range (LatencyRange const& range, bool playback) const
348 /* this sets the visible latency that the rest of the port system
349 sees. because we do latency compensation, all (most) of our visible
350 port latency values are identical.
353 DEBUG_TRACE (DEBUG::Latency,
354 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
355 name(), range.min, range.max,
356 (playback ? "PLAYBACK" : "CAPTURE")));;
359 LatencyRange r (range);
360 if (externally_connected ()) {
362 r.min *= _speed_ratio;
363 r.max *= _speed_ratio;
365 r.min += (_resampler_quality - 1);
366 r.max += (_resampler_quality - 1);
368 port_engine.set_latency_range (_port_handle, playback, r);
373 Port::set_private_latency_range (LatencyRange& range, bool playback)
376 _private_playback_latency = range;
377 DEBUG_TRACE (DEBUG::Latency, string_compose (
378 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
380 _private_playback_latency.min,
381 _private_playback_latency.max));
383 _private_capture_latency = range;
384 DEBUG_TRACE (DEBUG::Latency, string_compose (
385 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
387 _private_capture_latency.min,
388 _private_capture_latency.max));
391 /* push to public (port system) location so that everyone else can see it */
393 set_public_latency_range (range, playback);
397 Port::private_latency_range (bool playback) const
400 DEBUG_TRACE (DEBUG::Latency, string_compose (
401 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
403 _private_playback_latency.min,
404 _private_playback_latency.max));
405 return _private_playback_latency;
407 DEBUG_TRACE (DEBUG::Latency, string_compose (
408 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
410 _private_playback_latency.min,
411 _private_playback_latency.max));
412 return _private_capture_latency;
417 Port::public_latency_range (bool /*playback*/) const
423 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
424 if (externally_connected ()) {
426 r.min /= _speed_ratio;
427 r.max /= _speed_ratio;
429 r.min += (_resampler_quality - 1);
430 r.max += (_resampler_quality - 1);
433 DEBUG_TRACE (DEBUG::Latency, string_compose (
434 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
435 name(), r.min, r.max,
436 sends_output() ? "PLAYBACK" : "CAPTURE"));
443 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
445 vector<string> connections;
447 get_connections (connections);
449 if (!connections.empty()) {
451 range.min = ~((pframes_t) 0);
454 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
456 for (vector<string>::const_iterator c = connections.begin();
457 c != connections.end(); ++c) {
461 if (!AudioEngine::instance()->port_is_mine (*c)) {
463 /* port belongs to some other port-system client, use
464 * the port engine to lookup its latency information.
467 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
470 lr = port_engine.get_latency_range (remote_port, playback);
471 if (externally_connected ()) {
473 lr.min /= _speed_ratio;
474 lr.max /= _speed_ratio;
476 lr.min += (_resampler_quality - 1);
477 lr.max += (_resampler_quality - 1);
480 DEBUG_TRACE (DEBUG::Latency, string_compose (
481 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
482 name(), *c, lr.min, lr.max));
484 range.min = min (range.min, lr.min);
485 range.max = max (range.max, lr.max);
490 /* port belongs to this instance of ardour,
491 so look up its latency information
492 internally, because our published/public
493 values already contain our plugin
494 latency compensation.
497 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
499 lr = remote_port->private_latency_range ((playback ? true : false));
500 DEBUG_TRACE (DEBUG::Latency, string_compose (
501 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
502 name(), *c, lr.min, lr.max));
504 range.min = min (range.min, lr.min);
505 range.max = max (range.max, lr.max);
511 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
516 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
522 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
523 _port_handle = port_engine.register_port (_name, type(), _flags);
525 if (_port_handle == 0) {
526 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
530 DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reestablish %1 handle %2\n", name(), _port_handle));
534 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
542 /* caller must hold process lock; intended to be used only after reestablish() */
544 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
546 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
555 /** @param n Short port name (no port-system client name) */
557 Port::set_name (std::string const & n)
559 if (n == _name || !_port_handle) {
563 int const r = port_engine.set_port_name (_port_handle, n);
566 AudioEngine::instance()->port_renamed (_name, n);
575 Port::physically_connected () const
581 return port_engine.physically_connected (_port_handle);
585 Port::get_state () const
587 XMLNode* root = new XMLNode (state_node_name);
589 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
591 if (receives_input()) {
592 root->set_property (X_("direction"), X_("input"));
594 root->set_property (X_("direction"), X_("output"));
601 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
602 XMLNode* child = new XMLNode (X_("Connection"));
603 child->set_property (X_("other"), *i);
604 root->add_child_nocopy (*child);
611 Port::set_state (const XMLNode& node, int)
613 if (node.name() != state_node_name) {
618 if (node.get_property (X_("name"), str)) {
622 const XMLNodeList& children (node.children());
624 _connections.clear ();
626 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
628 if ((*c)->name() != X_("Connection")) {
632 if (!(*c)->get_property (X_("other"), str)) {
636 _connections.insert (str);
643 Port::set_speed_ratio (double s) {
644 /* see VMResampler::set_rratio() for min/max range */
645 _speed_ratio = std::min (16.0, std::max (0.5, s));
649 Port::set_cycle_samplecnt (pframes_t n) {
650 _cycle_nframes = floor (n * _speed_ratio);