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
101 return (port_engine.connected (_port_handle) != 0);
107 Port::disconnect_all ()
111 port_engine.disconnect_all (_port_handle);
112 _connections.clear ();
114 /* a cheaper, less hacky way to do boost::shared_from_this() ...
116 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
117 PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
123 /** @param o Port name
124 * @return true if this port is connected to o, otherwise false.
127 Port::connected_to (std::string const & o) const
133 if (!port_engine.available()) {
137 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
141 Port::get_connections (std::vector<std::string> & c) const
143 if (!port_engine.available()) {
144 c.insert (c.end(), _connections.begin(), _connections.end());
148 return port_engine.get_connections (_port_handle, c);
152 Port::connect (std::string const & other)
154 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
155 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
159 if (_connecting_blocked) {
163 if (sends_output ()) {
164 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
165 r = port_engine.connect (our_name, other_name);
167 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
168 r = port_engine.connect (other_name, our_name);
172 _connections.insert (other);
179 Port::disconnect (std::string const & other)
181 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
182 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
186 if (sends_output ()) {
187 r = port_engine.disconnect (this_fullname, other_fullname);
189 r = port_engine.disconnect (other_fullname, this_fullname);
193 _connections.erase (other);
196 /* a cheaper, less hacky way to do boost::shared_from_this() ...
198 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
199 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
201 if (pself && pother) {
202 /* Disconnecting from another Ardour port: need to allow
203 a check on whether this may affect anything that we
206 PostDisconnect (pself, pother); // emit signal
214 Port::connected_to (Port* o) const
216 return connected_to (o->name ());
220 Port::connect (Port* o)
222 return connect (o->name ());
226 Port::disconnect (Port* o)
228 return disconnect (o->name ());
232 Port::request_input_monitoring (bool yn)
235 port_engine.request_input_monitoring (_port_handle, yn);
240 Port::ensure_input_monitoring (bool yn)
243 port_engine.ensure_input_monitoring (_port_handle, yn);
248 Port::monitoring_input () const
251 return port_engine.monitoring_input (_port_handle);
259 _last_monitor = false;
263 Port::cycle_start (pframes_t)
265 _port_buffer_offset = 0;
269 Port::increment_port_buffer_offset (pframes_t nframes)
271 _port_buffer_offset += nframes;
275 Port::set_public_latency_range (LatencyRange& range, bool playback) const
277 /* this sets the visible latency that the rest of the port system
278 sees. because we do latency compensation, all (most) of our visible
279 port latency values are identical.
282 DEBUG_TRACE (DEBUG::Latency,
283 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
284 name(), range.min, range.max,
285 (playback ? "PLAYBACK" : "CAPTURE")));;
288 port_engine.set_latency_range (_port_handle, playback, range);
293 Port::set_private_latency_range (LatencyRange& range, bool playback)
296 _private_playback_latency = range;
297 DEBUG_TRACE (DEBUG::Latency, string_compose (
298 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
300 _private_playback_latency.min,
301 _private_playback_latency.max));
303 _private_capture_latency = range;
304 DEBUG_TRACE (DEBUG::Latency, string_compose (
305 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
307 _private_capture_latency.min,
308 _private_capture_latency.max));
311 /* push to public (port system) location so that everyone else can see it */
313 set_public_latency_range (range, playback);
317 Port::private_latency_range (bool playback) const
320 DEBUG_TRACE (DEBUG::Latency, string_compose (
321 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
323 _private_playback_latency.min,
324 _private_playback_latency.max));
325 return _private_playback_latency;
327 DEBUG_TRACE (DEBUG::Latency, string_compose (
328 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
330 _private_playback_latency.min,
331 _private_playback_latency.max));
332 return _private_capture_latency;
337 Port::public_latency_range (bool /*playback*/) const
343 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
345 DEBUG_TRACE (DEBUG::Latency, string_compose (
346 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
347 name(), r.min, r.max,
348 sends_output() ? "PLAYBACK" : "CAPTURE"));
355 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
357 vector<string> connections;
359 get_connections (connections);
361 if (!connections.empty()) {
363 range.min = ~((pframes_t) 0);
366 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
368 for (vector<string>::const_iterator c = connections.begin();
369 c != connections.end(); ++c) {
373 if (!AudioEngine::instance()->port_is_mine (*c)) {
375 /* port belongs to some other port-system client, use
376 * the port engine to lookup its latency information.
379 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
382 lr = port_engine.get_latency_range (remote_port, playback);
384 DEBUG_TRACE (DEBUG::Latency, string_compose (
385 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
386 name(), *c, lr.min, lr.max));
388 range.min = min (range.min, lr.min);
389 range.max = max (range.max, lr.max);
394 /* port belongs to this instance of ardour,
395 so look up its latency information
396 internally, because our published/public
397 values already contain our plugin
398 latency compensation.
401 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
403 lr = remote_port->private_latency_range ((playback ? true : false));
404 DEBUG_TRACE (DEBUG::Latency, string_compose (
405 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
406 name(), *c, lr.min, lr.max));
408 range.min = min (range.min, lr.min);
409 range.max = max (range.max, lr.max);
415 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
420 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
426 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
427 _port_handle = port_engine.register_port (_name, type(), _flags);
429 if (_port_handle == 0) {
430 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
443 /* caller must hold process lock; intended to be used only after reestablish() */
445 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
447 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
456 /** @param n Short port name (no port-system client name) */
458 Port::set_name (std::string const & n)
460 if (n == _name || !_port_handle) {
464 int const r = port_engine.set_port_name (_port_handle, n);
467 AudioEngine::instance()->port_renamed (_name, n);
476 Port::physically_connected () const
482 return port_engine.physically_connected (_port_handle);
486 Port::get_state () const
488 XMLNode* root = new XMLNode (state_node_name);
490 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
492 if (receives_input()) {
493 root->add_property (X_("direction"), X_("input"));
495 root->add_property (X_("direction"), X_("output"));
502 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
503 XMLNode* child = new XMLNode (X_("Connection"));
504 child->add_property (X_("other"), *i);
505 root->add_child_nocopy (*child);
512 Port::set_state (const XMLNode& node, int)
514 const XMLProperty* prop;
516 if (node.name() != state_node_name) {
520 if ((prop = node.property (X_("name"))) != 0) {
521 set_name (prop->value());
524 const XMLNodeList& children (node.children());
526 _connections.clear ();
528 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
530 if ((*c)->name() != X_("Connection")) {
534 if ((prop = (*c)->property (X_("other"))) == 0) {
538 _connections.insert (prop->value());