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 */
90 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
91 port_engine.unregister_port (_port_handle);
96 /** @return true if this port is connected to anything */
98 Port::connected () const
100 return (port_engine.connected (_port_handle) != 0);
104 Port::disconnect_all ()
106 port_engine.disconnect_all (_port_handle);
107 _connections.clear ();
109 /* a cheaper, less hacky way to do boost::shared_from_this() ...
111 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
112 PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
117 /** @param o Port name
118 * @return true if this port is connected to o, otherwise false.
121 Port::connected_to (std::string const & o) const
123 if (!port_engine.available()) {
127 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
131 Port::get_connections (std::vector<std::string> & c) const
133 if (!port_engine.available()) {
134 c.insert (c.end(), _connections.begin(), _connections.end());
138 return port_engine.get_connections (_port_handle, c);
142 Port::connect (std::string const & other)
144 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
145 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
149 if (_connecting_blocked) {
153 if (sends_output ()) {
154 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
155 r = port_engine.connect (our_name, other_name);
157 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
158 r = port_engine.connect (other_name, our_name);
162 _connections.insert (other);
169 Port::disconnect (std::string const & other)
171 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
172 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
176 if (sends_output ()) {
177 r = port_engine.disconnect (this_fullname, other_fullname);
179 r = port_engine.disconnect (other_fullname, this_fullname);
183 _connections.erase (other);
186 /* a cheaper, less hacky way to do boost::shared_from_this() ...
188 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
189 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
191 if (pself && pother) {
192 /* Disconnecting from another Ardour port: need to allow
193 a check on whether this may affect anything that we
196 PostDisconnect (pself, pother); // emit signal
204 Port::connected_to (Port* o) const
206 return connected_to (o->name ());
210 Port::connect (Port* o)
212 return connect (o->name ());
216 Port::disconnect (Port* o)
218 return disconnect (o->name ());
222 Port::request_input_monitoring (bool yn)
224 port_engine.request_input_monitoring (_port_handle, yn);
228 Port::ensure_input_monitoring (bool yn)
230 port_engine.ensure_input_monitoring (_port_handle, yn);
234 Port::monitoring_input () const
237 return port_engine.monitoring_input (_port_handle);
243 _last_monitor = false;
247 Port::cycle_start (pframes_t)
249 _port_buffer_offset = 0;
253 Port::increment_port_buffer_offset (pframes_t nframes)
255 _port_buffer_offset += nframes;
259 Port::set_public_latency_range (LatencyRange& range, bool playback) const
261 /* this sets the visible latency that the rest of the port system
262 sees. because we do latency compensation, all (most) of our visible
263 port latency values are identical.
266 DEBUG_TRACE (DEBUG::Latency,
267 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
268 name(), range.min, range.max,
269 (playback ? "PLAYBACK" : "CAPTURE")));;
271 port_engine.set_latency_range (_port_handle, playback, range);
275 Port::set_private_latency_range (LatencyRange& range, bool playback)
278 _private_playback_latency = range;
279 DEBUG_TRACE (DEBUG::Latency, string_compose (
280 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
282 _private_playback_latency.min,
283 _private_playback_latency.max));
285 _private_capture_latency = range;
286 DEBUG_TRACE (DEBUG::Latency, string_compose (
287 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
289 _private_capture_latency.min,
290 _private_capture_latency.max));
293 /* push to public (port system) location so that everyone else can see it */
295 set_public_latency_range (range, playback);
299 Port::private_latency_range (bool playback) const
302 DEBUG_TRACE (DEBUG::Latency, string_compose (
303 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
305 _private_playback_latency.min,
306 _private_playback_latency.max));
307 return _private_playback_latency;
309 DEBUG_TRACE (DEBUG::Latency, string_compose (
310 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
312 _private_playback_latency.min,
313 _private_playback_latency.max));
314 return _private_capture_latency;
319 Port::public_latency_range (bool /*playback*/) const
323 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
325 DEBUG_TRACE (DEBUG::Latency, string_compose (
326 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
327 name(), r.min, r.max,
328 sends_output() ? "PLAYBACK" : "CAPTURE"));
333 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
335 vector<string> connections;
337 get_connections (connections);
339 if (!connections.empty()) {
341 range.min = ~((pframes_t) 0);
344 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
346 for (vector<string>::const_iterator c = connections.begin();
347 c != connections.end(); ++c) {
351 if (!AudioEngine::instance()->port_is_mine (*c)) {
353 /* port belongs to some other port-system client, use
354 * the port engine to lookup its latency information.
357 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
360 lr = port_engine.get_latency_range (remote_port, playback);
362 DEBUG_TRACE (DEBUG::Latency, string_compose (
363 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
364 name(), *c, lr.min, lr.max));
366 range.min = min (range.min, lr.min);
367 range.max = max (range.max, lr.max);
372 /* port belongs to this instance of ardour,
373 so look up its latency information
374 internally, because our published/public
375 values already contain our plugin
376 latency compensation.
379 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
381 lr = remote_port->private_latency_range ((playback ? true : false));
382 DEBUG_TRACE (DEBUG::Latency, string_compose (
383 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
384 name(), *c, lr.min, lr.max));
386 range.min = min (range.min, lr.min);
387 range.max = max (range.max, lr.max);
393 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
398 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
404 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
405 _port_handle = port_engine.register_port (_name, type(), _flags);
407 if (_port_handle == 0) {
408 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
421 /* caller must hold process lock; intended to be used only after reestablish() */
423 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
425 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
434 /** @param n Short port name (no port-system client name) */
436 Port::set_name (std::string const & n)
442 int const r = port_engine.set_port_name (_port_handle, n);
445 AudioEngine::instance()->port_renamed (_name, n);
454 Port::physically_connected () const
456 return port_engine.physically_connected (_port_handle);
460 Port::get_state () const
462 XMLNode* root = new XMLNode (state_node_name);
464 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
466 if (receives_input()) {
467 root->add_property (X_("direction"), X_("input"));
469 root->add_property (X_("direction"), X_("output"));
476 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
477 XMLNode* child = new XMLNode (X_("Connection"));
478 child->add_property (X_("other"), *i);
479 root->add_child_nocopy (*child);
486 Port::set_state (const XMLNode& node, int)
488 const XMLProperty* prop;
490 if (node.name() != state_node_name) {
494 if ((prop = node.property (X_("name"))) != 0) {
495 set_name (prop->value());
498 const XMLNodeList& children (node.children());
500 _connections.clear ();
502 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
504 if ((*c)->name() != X_("Connection")) {
508 if ((prop = (*c)->property (X_("other"))) == 0) {
512 _connections.insert (prop->value());