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_manager->running ()) {
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 */
92 DEBUG_TRACE (DEBUG::Destruction, string_compose ("destroying port @ %1 named %2\n", this, name()));
97 Port::pretty_name(bool fallback_to_name) const
102 if (0 == port_engine.get_port_property (_port_handle,
103 "http://jackaudio.org/metadata/pretty-name",
109 if (fallback_to_name) {
116 Port::set_pretty_name(const std::string& n)
119 if (0 == port_engine.set_port_property (_port_handle,
120 "http://jackaudio.org/metadata/pretty-name", n, ""))
131 engine_connection.disconnect ();
138 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
139 port_engine.unregister_port (_port_handle);
145 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
148 /* we're only interested in disconnect */
151 boost::shared_ptr<Port> p0 = w0.lock ();
152 boost::shared_ptr<Port> p1 = w1.lock ();
153 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
154 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
157 PostDisconnect (p0, p1); // emit signal
160 PostDisconnect (p1, p0); // emit signal
164 /** @return true if this port is connected to anything */
166 Port::connected () const
169 return (port_engine.connected (_port_handle) != 0);
175 Port::disconnect_all ()
179 std::vector<std::string> connections;
180 get_connections (connections);
182 port_engine.disconnect_all (_port_handle);
183 _connections.clear ();
185 /* a cheaper, less hacky way to do boost::shared_from_this() ...
187 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
188 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
189 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
191 PostDisconnect (pself, pother); // emit signal
199 /** @param o Port name
200 * @return true if this port is connected to o, otherwise false.
203 Port::connected_to (std::string const & o) const
209 if (!port_manager->running()) {
213 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
217 Port::get_connections (std::vector<std::string> & c) const
219 if (!port_manager->running()) {
220 c.insert (c.end(), _connections.begin(), _connections.end());
225 return port_engine.get_connections (_port_handle, c);
232 Port::connect (std::string const & other)
234 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
235 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
239 if (_connecting_blocked) {
243 if (sends_output ()) {
244 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
245 r = port_engine.connect (our_name, other_name);
247 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
248 r = port_engine.connect (other_name, our_name);
252 _connections.insert (other);
259 Port::disconnect (std::string const & other)
261 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
262 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
266 if (sends_output ()) {
267 r = port_engine.disconnect (this_fullname, other_fullname);
269 r = port_engine.disconnect (other_fullname, this_fullname);
273 _connections.erase (other);
276 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
277 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
278 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
280 if (pself && pother) {
281 /* Disconnecting from another Ardour port: need to allow
282 a check on whether this may affect anything that we
285 PostDisconnect (pself, pother); // emit signal
293 Port::connected_to (Port* o) const
295 return connected_to (o->name ());
299 Port::connect (Port* o)
301 return connect (o->name ());
305 Port::disconnect (Port* o)
307 return disconnect (o->name ());
311 Port::request_input_monitoring (bool yn)
314 port_engine.request_input_monitoring (_port_handle, yn);
319 Port::ensure_input_monitoring (bool yn)
322 port_engine.ensure_input_monitoring (_port_handle, yn);
327 Port::monitoring_input () const
330 return port_engine.monitoring_input (_port_handle);
338 _last_monitor = false;
342 Port::cycle_start (pframes_t)
347 Port::set_public_latency_range (LatencyRange const& range, bool playback) const
349 /* this sets the visible latency that the rest of the port system
350 sees. because we do latency compensation, all (most) of our visible
351 port latency values are identical.
354 DEBUG_TRACE (DEBUG::Latency,
355 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
356 name(), range.min, range.max,
357 (playback ? "PLAYBACK" : "CAPTURE")));;
360 LatencyRange r (range);
361 if (externally_connected ()) {
363 r.min *= _speed_ratio;
364 r.max *= _speed_ratio;
366 r.min += (_resampler_quality - 1);
367 r.max += (_resampler_quality - 1);
369 port_engine.set_latency_range (_port_handle, playback, r);
374 Port::set_private_latency_range (LatencyRange& range, bool playback)
377 _private_playback_latency = range;
378 DEBUG_TRACE (DEBUG::Latency, string_compose (
379 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
381 _private_playback_latency.min,
382 _private_playback_latency.max));
384 _private_capture_latency = range;
385 DEBUG_TRACE (DEBUG::Latency, string_compose (
386 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
388 _private_capture_latency.min,
389 _private_capture_latency.max));
392 /* push to public (port system) location so that everyone else can see it */
394 set_public_latency_range (range, playback);
398 Port::private_latency_range (bool playback) const
401 DEBUG_TRACE (DEBUG::Latency, string_compose (
402 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
404 _private_playback_latency.min,
405 _private_playback_latency.max));
406 return _private_playback_latency;
408 DEBUG_TRACE (DEBUG::Latency, string_compose (
409 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
411 _private_playback_latency.min,
412 _private_playback_latency.max));
413 return _private_capture_latency;
418 Port::public_latency_range (bool /*playback*/) const
424 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
425 if (externally_connected ()) {
427 r.min /= _speed_ratio;
428 r.max /= _speed_ratio;
430 r.min += (_resampler_quality - 1);
431 r.max += (_resampler_quality - 1);
434 DEBUG_TRACE (DEBUG::Latency, string_compose (
435 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
436 name(), r.min, r.max,
437 sends_output() ? "PLAYBACK" : "CAPTURE"));
444 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
446 vector<string> connections;
448 get_connections (connections);
450 if (!connections.empty()) {
452 range.min = ~((pframes_t) 0);
455 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
457 for (vector<string>::const_iterator c = connections.begin();
458 c != connections.end(); ++c) {
462 if (!AudioEngine::instance()->port_is_mine (*c)) {
464 /* port belongs to some other port-system client, use
465 * the port engine to lookup its latency information.
468 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
471 lr = port_engine.get_latency_range (remote_port, playback);
472 if (externally_connected ()) {
474 lr.min /= _speed_ratio;
475 lr.max /= _speed_ratio;
477 lr.min += (_resampler_quality - 1);
478 lr.max += (_resampler_quality - 1);
481 DEBUG_TRACE (DEBUG::Latency, string_compose (
482 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
483 name(), *c, lr.min, lr.max));
485 range.min = min (range.min, lr.min);
486 range.max = max (range.max, lr.max);
491 /* port belongs to this instance of ardour,
492 so look up its latency information
493 internally, because our published/public
494 values already contain our plugin
495 latency compensation.
498 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
500 lr = remote_port->private_latency_range ((playback ? true : false));
501 DEBUG_TRACE (DEBUG::Latency, string_compose (
502 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
503 name(), *c, lr.min, lr.max));
505 range.min = min (range.min, lr.min);
506 range.max = max (range.max, lr.max);
512 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
517 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
523 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
524 _port_handle = port_engine.register_port (_name, type(), _flags);
526 if (_port_handle == 0) {
527 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
531 DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reestablish %1 handle %2\n", name(), _port_handle));
535 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
543 /* caller must hold process lock; intended to be used only after reestablish() */
545 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
547 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
556 /** @param n Short port name (no port-system client name) */
558 Port::set_name (std::string const & n)
560 if (n == _name || !_port_handle) {
564 int const r = port_engine.set_port_name (_port_handle, n);
567 AudioEngine::instance()->port_renamed (_name, n);
576 Port::physically_connected () const
582 return port_engine.physically_connected (_port_handle);
586 Port::get_state () const
588 XMLNode* root = new XMLNode (state_node_name);
590 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
592 if (receives_input()) {
593 root->set_property (X_("direction"), X_("input"));
595 root->set_property (X_("direction"), X_("output"));
602 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
603 XMLNode* child = new XMLNode (X_("Connection"));
604 child->set_property (X_("other"), *i);
605 root->add_child_nocopy (*child);
612 Port::set_state (const XMLNode& node, int)
614 if (node.name() != state_node_name) {
619 if (node.get_property (X_("name"), str)) {
623 const XMLNodeList& children (node.children());
625 _connections.clear ();
627 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
629 if ((*c)->name() != X_("Connection")) {
633 if (!(*c)->get_property (X_("other"), str)) {
637 _connections.insert (str);
644 Port::set_speed_ratio (double s) {
645 /* see VMResampler::set_rratio() for min/max range */
646 _speed_ratio = std::min (16.0, std::max (0.5, s));
650 Port::set_cycle_samplecnt (pframes_t n) {
651 _cycle_nframes = floor (n * _speed_ratio);