2 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
3 * Copyright (C) 2006-2019 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2007-2011 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "libardour-config.h"
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/failed_constructor.h"
30 #include "ardour/audioengine.h"
31 #include "ardour/debug.h"
32 #include "ardour/port.h"
33 #include "ardour/port_engine.h"
38 using namespace ARDOUR;
41 PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
42 PBD::Signal0<void> Port::PortDrop;
43 PBD::Signal0<void> Port::PortSignalDrop;
45 bool Port::_connecting_blocked = false;
46 pframes_t Port::_global_port_buffer_offset = 0;
47 pframes_t Port::_cycle_nframes = 0;
48 double Port::_speed_ratio = 1.0;
49 std::string Port::state_node_name = X_("Port");
50 const uint32_t Port::_resampler_quality = 12;
52 /* a handy define to shorten what would otherwise be a needlessly verbose
55 #define port_engine AudioEngine::instance()->port_engine()
56 #define port_manager AudioEngine::instance()
58 /** @param n Port short name */
59 Port::Port (std::string const & n, DataType t, PortFlags f)
62 , _last_monitor (false)
63 , _externally_connected (0)
65 _private_playback_latency.min = 0;
66 _private_playback_latency.max = 0;
67 _private_capture_latency.min = 0;
68 _private_capture_latency.max = 0;
70 /* Unfortunately we have to pass the DataType into this constructor so that
71 we can create the right kind of port; aside from this we'll use the
72 virtual function type () to establish type.
75 assert (_name.find_first_of (':') == std::string::npos);
77 if (!port_manager->running ()) {
78 DEBUG_TRACE (DEBUG::Ports, string_compose ("port-engine n/a postpone registering %1\n", name()));
79 _port_handle = 0; // created during ::reestablish() later
80 } else if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
81 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
82 throw failed_constructor ();
84 DEBUG_TRACE (DEBUG::Ports, string_compose ("registed port %1 handle %2\n", name(), _port_handle));
86 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
87 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
88 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
91 /** Port destructor */
94 DEBUG_TRACE (DEBUG::Destruction, string_compose ("destroying port @ %1 named %2\n", this, name()));
99 Port::pretty_name(bool fallback_to_name) const
104 if (0 == port_engine.get_port_property (_port_handle,
105 "http://jackaudio.org/metadata/pretty-name",
111 if (fallback_to_name) {
118 Port::set_pretty_name(const std::string& n)
121 if (0 == port_engine.set_port_property (_port_handle,
122 "http://jackaudio.org/metadata/pretty-name", n, ""))
133 engine_connection.disconnect ();
140 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
141 port_engine.unregister_port (_port_handle);
147 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
150 /* we're only interested in disconnect */
153 boost::shared_ptr<Port> p0 = w0.lock ();
154 boost::shared_ptr<Port> p1 = w1.lock ();
155 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
156 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
159 PostDisconnect (p0, p1); // emit signal
162 PostDisconnect (p1, p0); // emit signal
166 /** @return true if this port is connected to anything */
168 Port::connected () const
171 return (port_engine.connected (_port_handle) != 0);
177 Port::disconnect_all ()
181 std::vector<std::string> connections;
182 get_connections (connections);
184 port_engine.disconnect_all (_port_handle);
185 _connections.clear ();
187 /* a cheaper, less hacky way to do boost::shared_from_this() ...
189 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
190 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
191 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
193 PostDisconnect (pself, pother); // emit signal
201 /** @param o Port name
202 * @return true if this port is connected to o, otherwise false.
205 Port::connected_to (std::string const & o) const
211 if (!port_manager->running()) {
215 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
219 Port::get_connections (std::vector<std::string> & c) const
221 if (!port_manager->running()) {
222 c.insert (c.end(), _connections.begin(), _connections.end());
227 return port_engine.get_connections (_port_handle, c);
234 Port::connect (std::string const & other)
236 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
237 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
241 if (_connecting_blocked) {
245 if (sends_output ()) {
246 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
247 r = port_engine.connect (our_name, other_name);
249 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
250 r = port_engine.connect (other_name, our_name);
254 _connections.insert (other);
261 Port::disconnect (std::string const & other)
263 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
264 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
268 if (sends_output ()) {
269 r = port_engine.disconnect (this_fullname, other_fullname);
271 r = port_engine.disconnect (other_fullname, this_fullname);
275 _connections.erase (other);
278 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
279 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
280 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
282 if (pself && pother) {
283 /* Disconnecting from another Ardour port: need to allow
284 a check on whether this may affect anything that we
287 PostDisconnect (pself, pother); // emit signal
295 Port::connected_to (Port* o) const
297 return connected_to (o->name ());
301 Port::connect (Port* o)
303 return connect (o->name ());
307 Port::disconnect (Port* o)
309 return disconnect (o->name ());
313 Port::request_input_monitoring (bool yn)
316 port_engine.request_input_monitoring (_port_handle, yn);
321 Port::ensure_input_monitoring (bool yn)
324 port_engine.ensure_input_monitoring (_port_handle, yn);
329 Port::monitoring_input () const
332 return port_engine.monitoring_input (_port_handle);
340 _last_monitor = false;
344 Port::cycle_start (pframes_t)
349 Port::set_public_latency_range (LatencyRange const& 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 LatencyRange r (range);
363 if (externally_connected ()) {
365 r.min *= _speed_ratio;
366 r.max *= _speed_ratio;
368 r.min += (_resampler_quality - 1);
369 r.max += (_resampler_quality - 1);
371 port_engine.set_latency_range (_port_handle, playback, r);
376 Port::set_private_latency_range (LatencyRange& range, bool playback)
379 _private_playback_latency = range;
380 DEBUG_TRACE (DEBUG::Latency, string_compose (
381 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
383 _private_playback_latency.min,
384 _private_playback_latency.max));
386 _private_capture_latency = range;
387 DEBUG_TRACE (DEBUG::Latency, string_compose (
388 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
390 _private_capture_latency.min,
391 _private_capture_latency.max));
394 /* push to public (port system) location so that everyone else can see it */
396 set_public_latency_range (range, playback);
400 Port::private_latency_range (bool playback) const
403 DEBUG_TRACE (DEBUG::Latency, string_compose (
404 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
406 _private_playback_latency.min,
407 _private_playback_latency.max));
408 return _private_playback_latency;
410 DEBUG_TRACE (DEBUG::Latency, string_compose (
411 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
413 _private_playback_latency.min,
414 _private_playback_latency.max));
415 return _private_capture_latency;
420 Port::public_latency_range (bool /*playback*/) const
426 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
427 if (externally_connected ()) {
429 r.min /= _speed_ratio;
430 r.max /= _speed_ratio;
432 r.min += (_resampler_quality - 1);
433 r.max += (_resampler_quality - 1);
436 DEBUG_TRACE (DEBUG::Latency, string_compose (
437 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
438 name(), r.min, r.max,
439 sends_output() ? "PLAYBACK" : "CAPTURE"));
446 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
448 vector<string> connections;
450 get_connections (connections);
452 if (!connections.empty()) {
454 range.min = ~((pframes_t) 0);
457 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
459 for (vector<string>::const_iterator c = connections.begin();
460 c != connections.end(); ++c) {
464 if (!AudioEngine::instance()->port_is_mine (*c)) {
466 /* port belongs to some other port-system client, use
467 * the port engine to lookup its latency information.
470 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
473 lr = port_engine.get_latency_range (remote_port, playback);
474 if (externally_connected ()) {
476 lr.min /= _speed_ratio;
477 lr.max /= _speed_ratio;
479 lr.min += (_resampler_quality - 1);
480 lr.max += (_resampler_quality - 1);
483 DEBUG_TRACE (DEBUG::Latency, string_compose (
484 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
485 name(), *c, lr.min, lr.max));
487 range.min = min (range.min, lr.min);
488 range.max = max (range.max, lr.max);
493 /* port belongs to this instance of ardour,
494 * so look up its latency information
495 * internally, because our published/public
496 * values already contain our plugin
497 * latency compensation.
500 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
502 lr = remote_port->private_latency_range ((playback ? true : false));
503 DEBUG_TRACE (DEBUG::Latency, string_compose (
504 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
505 name(), *c, lr.min, lr.max));
507 range.min = min (range.min, lr.min);
508 range.max = max (range.max, lr.max);
514 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
519 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
525 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
526 _port_handle = port_engine.register_port (_name, type(), _flags);
528 if (_port_handle == 0) {
529 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
533 DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reestablish %1 handle %2\n", name(), _port_handle));
537 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
545 /* caller must hold process lock; intended to be used only after reestablish() */
547 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
549 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
558 /** @param n Short port name (no port-system client name) */
560 Port::set_name (std::string const & n)
562 if (n == _name || !_port_handle) {
566 int const r = port_engine.set_port_name (_port_handle, n);
569 AudioEngine::instance()->port_renamed (_name, n);
578 Port::physically_connected () const
584 return port_engine.physically_connected (_port_handle);
588 Port::get_state () const
590 XMLNode* root = new XMLNode (state_node_name);
592 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
594 if (receives_input()) {
595 root->set_property (X_("direction"), X_("input"));
597 root->set_property (X_("direction"), X_("output"));
604 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
605 XMLNode* child = new XMLNode (X_("Connection"));
606 child->set_property (X_("other"), *i);
607 root->add_child_nocopy (*child);
614 Port::set_state (const XMLNode& node, int)
616 if (node.name() != state_node_name) {
621 if (node.get_property (X_("name"), str)) {
625 const XMLNodeList& children (node.children());
627 _connections.clear ();
629 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
631 if ((*c)->name() != X_("Connection")) {
635 if (!(*c)->get_property (X_("other"), str)) {
639 _connections.insert (str);
646 Port::set_speed_ratio (double s) {
647 /* see VMResampler::set_rratio() for min/max range */
648 _speed_ratio = std::min (16.0, std::max (0.5, s));
652 Port::set_cycle_samplecnt (pframes_t n) {
653 _cycle_nframes = floor (n * _speed_ratio);