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 <jack/weakjack.h> // so that we can test for new functions at runtime
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;
44 bool Port::_connecting_blocked = false;
45 pframes_t Port::_global_port_buffer_offset = 0;
46 pframes_t Port::_cycle_nframes = 0;
47 std::string Port::state_node_name = X_("Port");
49 /* a handy define to shorten what would otherwise be a needlessly verbose
52 #define port_engine AudioEngine::instance()->port_engine()
53 #define port_manager AudioEngine::instance()
55 /** @param n Port short name */
56 Port::Port (std::string const & n, DataType t, PortFlags f)
57 : _port_buffer_offset (0)
60 , _last_monitor (false)
62 _private_playback_latency.min = 0;
63 _private_playback_latency.max = 0;
64 _private_capture_latency.min = 0;
65 _private_capture_latency.max = 0;
67 /* Unfortunately we have to pass the DataType into this constructor so that
68 we can create the right kind of port; aside from this we'll use the
69 virtual function type () to establish type.
72 assert (_name.find_first_of (':') == std::string::npos);
74 if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
75 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
76 throw failed_constructor ();
79 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
82 /** Port destructor */
92 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
93 port_engine.unregister_port (_port_handle);
98 /** @return true if this port is connected to anything */
100 Port::connected () const
102 return (port_engine.connected (_port_handle) != 0);
106 Port::disconnect_all ()
108 port_engine.disconnect_all (_port_handle);
109 _connections.clear ();
111 /* a cheaper, less hacky way to do boost::shared_from_this() ...
113 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
114 PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
119 /** @param o Port name
120 * @return true if this port is connected to o, otherwise false.
123 Port::connected_to (std::string const & o) const
125 if (!port_engine.available()) {
129 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
133 Port::get_connections (std::vector<std::string> & c) const
135 if (!port_engine.available()) {
136 c.insert (c.end(), _connections.begin(), _connections.end());
140 return port_engine.get_connections (_port_handle, c);
144 Port::connect (std::string const & other)
146 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
147 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
151 if (_connecting_blocked) {
155 if (sends_output ()) {
156 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
157 r = port_engine.connect (our_name, other_name);
159 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
160 r = port_engine.connect (other_name, our_name);
164 _connections.insert (other);
171 Port::disconnect (std::string const & other)
173 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
174 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
178 if (sends_output ()) {
179 r = port_engine.disconnect (this_fullname, other_fullname);
181 r = port_engine.disconnect (other_fullname, this_fullname);
185 _connections.erase (other);
188 /* a cheaper, less hacky way to do boost::shared_from_this() ...
190 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
191 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
193 if (pself && pother) {
194 /* Disconnecting from another Ardour port: need to allow
195 a check on whether this may affect anything that we
198 PostDisconnect (pself, pother); // emit signal
206 Port::connected_to (Port* o) const
208 return connected_to (o->name ());
212 Port::connect (Port* o)
214 return connect (o->name ());
218 Port::disconnect (Port* o)
220 return disconnect (o->name ());
224 Port::request_input_monitoring (bool yn)
226 port_engine.request_input_monitoring (_port_handle, yn);
230 Port::ensure_input_monitoring (bool yn)
232 port_engine.ensure_input_monitoring (_port_handle, yn);
236 Port::monitoring_input () const
239 return port_engine.monitoring_input (_port_handle);
245 _last_monitor = false;
249 Port::cycle_start (pframes_t)
251 _port_buffer_offset = 0;
255 Port::increment_port_buffer_offset (pframes_t nframes)
257 _port_buffer_offset += nframes;
261 Port::set_public_latency_range (LatencyRange& range, bool playback) const
263 /* this sets the visible latency that the rest of the port system
264 sees. because we do latency compensation, all (most) of our visible
265 port latency values are identical.
268 DEBUG_TRACE (DEBUG::Latency,
269 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
270 name(), range.min, range.max,
271 (playback ? "PLAYBACK" : "CAPTURE")));;
273 port_engine.set_latency_range (_port_handle, playback, range);
277 Port::set_private_latency_range (LatencyRange& range, bool playback)
280 _private_playback_latency = range;
281 DEBUG_TRACE (DEBUG::Latency, string_compose (
282 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
284 _private_playback_latency.min,
285 _private_playback_latency.max));
287 _private_capture_latency = range;
288 DEBUG_TRACE (DEBUG::Latency, string_compose (
289 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
291 _private_capture_latency.min,
292 _private_capture_latency.max));
295 /* push to public (port system) location so that everyone else can see it */
297 set_public_latency_range (range, playback);
301 Port::private_latency_range (bool playback) const
304 DEBUG_TRACE (DEBUG::Latency, string_compose (
305 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
307 _private_playback_latency.min,
308 _private_playback_latency.max));
309 return _private_playback_latency;
311 DEBUG_TRACE (DEBUG::Latency, string_compose (
312 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
314 _private_playback_latency.min,
315 _private_playback_latency.max));
316 return _private_capture_latency;
321 Port::public_latency_range (bool /*playback*/) const
325 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
327 DEBUG_TRACE (DEBUG::Latency, string_compose (
328 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
329 name(), r.min, r.max,
330 sends_output() ? "PLAYBACK" : "CAPTURE"));
335 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
337 vector<string> connections;
339 get_connections (connections);
341 if (!connections.empty()) {
343 range.min = ~((pframes_t) 0);
346 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
348 for (vector<string>::const_iterator c = connections.begin();
349 c != connections.end(); ++c) {
353 if (!AudioEngine::instance()->port_is_mine (*c)) {
355 /* port belongs to some other port-system client, use
356 * the port engine to lookup its latency information.
359 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
362 lr = port_engine.get_latency_range (remote_port, playback);
364 DEBUG_TRACE (DEBUG::Latency, string_compose (
365 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
366 name(), *c, lr.min, lr.max));
368 range.min = min (range.min, lr.min);
369 range.max = max (range.max, lr.max);
374 /* port belongs to this instance of ardour,
375 so look up its latency information
376 internally, because our published/public
377 values already contain our plugin
378 latency compensation.
381 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
383 lr = remote_port->private_latency_range ((playback ? JackPlaybackLatency : JackCaptureLatency));
384 DEBUG_TRACE (DEBUG::Latency, string_compose (
385 "\t%1 <-LOCAL-> %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);
395 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
400 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
406 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
407 _port_handle = port_engine.register_port (_name, type(), _flags);
409 if (_port_handle == 0) {
410 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
423 /* caller must hold process lock; intended to be used only after reestablish() */
425 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
427 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
436 /** @param n Short port name (no port-system client name) */
438 Port::set_name (std::string const & n)
444 int const r = port_engine.set_port_name (_port_handle, n);
447 AudioEngine::instance()->port_renamed (_name, n);
456 Port::physically_connected () const
458 return port_engine.physically_connected (_port_handle);
462 Port::get_state () const
464 XMLNode* root = new XMLNode (state_node_name);
466 root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
468 if (receives_input()) {
469 root->add_property (X_("direction"), X_("input"));
471 root->add_property (X_("direction"), X_("output"));
478 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
479 XMLNode* child = new XMLNode (X_("Connection"));
480 child->add_property (X_("other"), *i);
481 root->add_child_nocopy (*child);
488 Port::set_state (const XMLNode& node, int)
490 const XMLProperty* prop;
492 if (node.name() != state_node_name) {
496 if ((prop = node.property (X_("name"))) != 0) {
497 set_name (prop->value());
500 const XMLNodeList& children (node.children());
502 _connections.clear ();
504 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
506 if ((*c)->name() != X_("Connection")) {
510 if ((prop = (*c)->property (X_("other"))) == 0) {
514 _connections.insert (prop->value());