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) {
107 Port::set_pretty_name(const std::string& n)
110 if (0 == port_engine.set_port_property (_port_handle,
111 "http://jackaudio.org/metadata/pretty-name", n, ""))
123 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
124 port_engine.unregister_port (_port_handle);
129 /** @return true if this port is connected to anything */
131 Port::connected () const
134 return (port_engine.connected (_port_handle) != 0);
140 Port::disconnect_all ()
144 std::vector<std::string> connections;
145 get_connections (connections);
147 port_engine.disconnect_all (_port_handle);
148 _connections.clear ();
150 /* a cheaper, less hacky way to do boost::shared_from_this() ...
152 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
153 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
154 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
156 PostDisconnect (pself, pother); // emit signal
164 /** @param o Port name
165 * @return true if this port is connected to o, otherwise false.
168 Port::connected_to (std::string const & o) const
174 if (!port_engine.available()) {
178 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
182 Port::get_connections (std::vector<std::string> & c) const
184 if (!port_engine.available()) {
185 c.insert (c.end(), _connections.begin(), _connections.end());
190 return port_engine.get_connections (_port_handle, c);
197 Port::connect (std::string const & other)
199 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
200 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
204 if (_connecting_blocked) {
208 if (sends_output ()) {
209 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
210 r = port_engine.connect (our_name, other_name);
212 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
213 r = port_engine.connect (other_name, our_name);
217 _connections.insert (other);
224 Port::disconnect (std::string const & other)
226 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
227 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
231 if (sends_output ()) {
232 r = port_engine.disconnect (this_fullname, other_fullname);
234 r = port_engine.disconnect (other_fullname, this_fullname);
238 _connections.erase (other);
241 /* a cheaper, less hacky way to do boost::shared_from_this() ...
243 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
244 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
246 if (pself && pother) {
247 /* Disconnecting from another Ardour port: need to allow
248 a check on whether this may affect anything that we
251 PostDisconnect (pself, pother); // emit signal
259 Port::connected_to (Port* o) const
261 return connected_to (o->name ());
265 Port::connect (Port* o)
267 return connect (o->name ());
271 Port::disconnect (Port* o)
273 return disconnect (o->name ());
277 Port::request_input_monitoring (bool yn)
280 port_engine.request_input_monitoring (_port_handle, yn);
285 Port::ensure_input_monitoring (bool yn)
288 port_engine.ensure_input_monitoring (_port_handle, yn);
293 Port::monitoring_input () const
296 return port_engine.monitoring_input (_port_handle);
304 _last_monitor = false;
308 Port::cycle_start (pframes_t)
310 _port_buffer_offset = 0;
314 Port::increment_port_buffer_offset (pframes_t nframes)
316 _port_buffer_offset += nframes;
320 Port::set_public_latency_range (LatencyRange& range, bool playback) const
322 /* this sets the visible latency that the rest of the port system
323 sees. because we do latency compensation, all (most) of our visible
324 port latency values are identical.
327 DEBUG_TRACE (DEBUG::Latency,
328 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
329 name(), range.min, range.max,
330 (playback ? "PLAYBACK" : "CAPTURE")));;
333 port_engine.set_latency_range (_port_handle, playback, range);
338 Port::set_private_latency_range (LatencyRange& range, bool playback)
341 _private_playback_latency = range;
342 DEBUG_TRACE (DEBUG::Latency, string_compose (
343 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
345 _private_playback_latency.min,
346 _private_playback_latency.max));
348 _private_capture_latency = range;
349 DEBUG_TRACE (DEBUG::Latency, string_compose (
350 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
352 _private_capture_latency.min,
353 _private_capture_latency.max));
356 /* push to public (port system) location so that everyone else can see it */
358 set_public_latency_range (range, playback);
362 Port::private_latency_range (bool playback) const
365 DEBUG_TRACE (DEBUG::Latency, string_compose (
366 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
368 _private_playback_latency.min,
369 _private_playback_latency.max));
370 return _private_playback_latency;
372 DEBUG_TRACE (DEBUG::Latency, string_compose (
373 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
375 _private_playback_latency.min,
376 _private_playback_latency.max));
377 return _private_capture_latency;
382 Port::public_latency_range (bool /*playback*/) const
388 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
390 DEBUG_TRACE (DEBUG::Latency, string_compose (
391 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
392 name(), r.min, r.max,
393 sends_output() ? "PLAYBACK" : "CAPTURE"));
400 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
402 vector<string> connections;
404 get_connections (connections);
406 if (!connections.empty()) {
408 range.min = ~((pframes_t) 0);
411 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
413 for (vector<string>::const_iterator c = connections.begin();
414 c != connections.end(); ++c) {
418 if (!AudioEngine::instance()->port_is_mine (*c)) {
420 /* port belongs to some other port-system client, use
421 * the port engine to lookup its latency information.
424 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
427 lr = port_engine.get_latency_range (remote_port, playback);
429 DEBUG_TRACE (DEBUG::Latency, string_compose (
430 "\t%1 <-> %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);
439 /* port belongs to this instance of ardour,
440 so look up its latency information
441 internally, because our published/public
442 values already contain our plugin
443 latency compensation.
446 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
448 lr = remote_port->private_latency_range ((playback ? true : false));
449 DEBUG_TRACE (DEBUG::Latency, string_compose (
450 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
451 name(), *c, lr.min, lr.max));
453 range.min = min (range.min, lr.min);
454 range.max = max (range.max, lr.max);
460 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
465 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
471 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
472 _port_handle = port_engine.register_port (_name, type(), _flags);
474 if (_port_handle == 0) {
475 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
488 /* caller must hold process lock; intended to be used only after reestablish() */
490 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
492 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
501 /** @param n Short port name (no port-system client name) */
503 Port::set_name (std::string const & n)
505 if (n == _name || !_port_handle) {
509 int const r = port_engine.set_port_name (_port_handle, n);
512 AudioEngine::instance()->port_renamed (_name, n);
521 Port::physically_connected () const
527 return port_engine.physically_connected (_port_handle);
531 Port::get_state () const
533 XMLNode* root = new XMLNode (state_node_name);
535 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
537 if (receives_input()) {
538 root->add_property (X_("direction"), X_("input"));
540 root->add_property (X_("direction"), X_("output"));
547 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
548 XMLNode* child = new XMLNode (X_("Connection"));
549 child->add_property (X_("other"), *i);
550 root->add_child_nocopy (*child);
557 Port::set_state (const XMLNode& node, int)
559 const XMLProperty* prop;
561 if (node.name() != state_node_name) {
565 if ((prop = node.property (X_("name"))) != 0) {
566 set_name (prop->value());
569 const XMLNodeList& children (node.children());
571 _connections.clear ();
573 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
575 if ((*c)->name() != X_("Connection")) {
579 if ((prop = (*c)->property (X_("other"))) == 0) {
583 _connections.insert (prop->value());