ardour
port.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23 
24 #include "pbd/compose.h"
25 #include "pbd/error.h"
26 #include "pbd/failed_constructor.h"
27 
28 #include "ardour/audioengine.h"
29 #include "ardour/debug.h"
30 #include "ardour/port.h"
31 #include "ardour/port_engine.h"
32 
33 #include "i18n.h"
34 
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38 
39 PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
40 PBD::Signal0<void> Port::PortDrop;
41 
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");
46 
47 /* a handy define to shorten what would otherwise be a needlessly verbose
48  * repeated phrase
49  */
50 #define port_engine AudioEngine::instance()->port_engine()
51 #define port_manager AudioEngine::instance()
52 
54 Port::Port (std::string const & n, DataType t, PortFlags f)
55  : _port_buffer_offset (0)
56  , _name (n)
57  , _flags (f)
58  , _last_monitor (false)
59 {
64 
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.
68  */
69 
70  assert (_name.find_first_of (':') == std::string::npos);
71 
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 ();
75  }
76 
77  PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::drop, this));
78 }
79 
82 {
83  drop ();
84 }
85 
86 
87 std::string
88 Port::pretty_name(bool fallback_to_name) const
89 {
90  if (_port_handle) {
91  std::string value;
92  std::string type;
93  if (0 == port_engine.get_port_property (_port_handle,
94  "http://jackaudio.org/metadata/pretty-name",
95  value, type))
96  {
97  return value;
98  }
99  }
100  if (fallback_to_name) {
101  return name ();
102  }
103  return "";
104 }
105 
106 void
108 {
109  if (_port_handle) {
110  DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
111  port_engine.unregister_port (_port_handle);
112  _port_handle = 0;
113  }
114 }
115 
117 bool
119 {
120  if (_port_handle) {
121  return (port_engine.connected (_port_handle) != 0);
122  }
123  return false;
124 }
125 
126 int
128 {
129  if (_port_handle) {
130 
131  port_engine.disconnect_all (_port_handle);
132  _connections.clear ();
133 
134  /* a cheaper, less hacky way to do boost::shared_from_this() ...
135  */
136  boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
137  PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
138  }
139 
140  return 0;
141 }
142 
146 bool
147 Port::connected_to (std::string const & o) const
148 {
149  if (!_port_handle) {
150  return false;
151  }
152 
153  if (!port_engine.available()) {
154  return false;
155  }
156 
157  return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
158 }
159 
160 int
161 Port::get_connections (std::vector<std::string> & c) const
162 {
163  if (!port_engine.available()) {
164  c.insert (c.end(), _connections.begin(), _connections.end());
165  return c.size();
166  }
167 
168  return port_engine.get_connections (_port_handle, c);
169 }
170 
171 int
172 Port::connect (std::string const & other)
173 {
174  std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
175  std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
176 
177  int r = 0;
178 
179  if (_connecting_blocked) {
180  return r;
181  }
182 
183  if (sends_output ()) {
184  DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
185  r = port_engine.connect (our_name, other_name);
186  } else {
187  DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
188  r = port_engine.connect (other_name, our_name);
189  }
190 
191  if (r == 0) {
192  _connections.insert (other);
193  }
194 
195  return r;
196 }
197 
198 int
199 Port::disconnect (std::string const & other)
200 {
201  std::string const other_fullname = port_manager->make_port_name_non_relative (other);
202  std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
203 
204  int r = 0;
205 
206  if (sends_output ()) {
207  r = port_engine.disconnect (this_fullname, other_fullname);
208  } else {
209  r = port_engine.disconnect (other_fullname, this_fullname);
210  }
211 
212  if (r == 0) {
213  _connections.erase (other);
214  }
215 
216  /* a cheaper, less hacky way to do boost::shared_from_this() ...
217  */
220 
221  if (pself && pother) {
222  /* Disconnecting from another Ardour port: need to allow
223  a check on whether this may affect anything that we
224  need to know about.
225  */
226  PostDisconnect (pself, pother); // emit signal
227  }
228 
229  return r;
230 }
231 
232 
233 bool
235 {
236  return connected_to (o->name ());
237 }
238 
239 int
241 {
242  return connect (o->name ());
243 }
244 
245 int
247 {
248  return disconnect (o->name ());
249 }
250 
251 void
253 {
254  if (_port_handle) {
255  port_engine.request_input_monitoring (_port_handle, yn);
256  }
257 }
258 
259 void
261 {
262  if (_port_handle) {
263  port_engine.ensure_input_monitoring (_port_handle, yn);
264  }
265 }
266 
267 bool
269 {
270  if (_port_handle) {
271  return port_engine.monitoring_input (_port_handle);
272  }
273  return false;
274 }
275 
276 void
278 {
279  _last_monitor = false;
280 }
281 
282 void
284 {
286 }
287 
288 void
290 {
291  _port_buffer_offset += nframes;
292 }
293 
294 void
295 Port::set_public_latency_range (LatencyRange& range, bool playback) const
296 {
297  /* this sets the visible latency that the rest of the port system
298  sees. because we do latency compensation, all (most) of our visible
299  port latency values are identical.
300  */
301 
303  string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
304  name(), range.min, range.max,
305  (playback ? "PLAYBACK" : "CAPTURE")));;
306 
307  if (_port_handle) {
308  port_engine.set_latency_range (_port_handle, playback, range);
309  }
310 }
311 
312 void
314 {
315  if (playback) {
318  "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
319  name(),
322  } else {
323  _private_capture_latency = range;
325  "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
326  name(),
329  }
330 
331  /* push to public (port system) location so that everyone else can see it */
332 
333  set_public_latency_range (range, playback);
334 }
335 
336 const LatencyRange&
337 Port::private_latency_range (bool playback) const
338 {
339  if (playback) {
341  "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
342  name(),
346  } else {
348  "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
349  name(),
353  }
354 }
355 
357 Port::public_latency_range (bool /*playback*/) const
358 {
359  LatencyRange r;
360 
361 
362  if (_port_handle) {
363  r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
364 
366  "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
367  name(), r.min, r.max,
368  sends_output() ? "PLAYBACK" : "CAPTURE"));
369  }
370 
371  return r;
372 }
373 
374 void
375 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
376 {
377  vector<string> connections;
378 
379  get_connections (connections);
380 
381  if (!connections.empty()) {
382 
383  range.min = ~((pframes_t) 0);
384  range.max = 0;
385 
386  DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
387 
388  for (vector<string>::const_iterator c = connections.begin();
389  c != connections.end(); ++c) {
390 
391  LatencyRange lr;
392 
393  if (!AudioEngine::instance()->port_is_mine (*c)) {
394 
395  /* port belongs to some other port-system client, use
396  * the port engine to lookup its latency information.
397  */
398 
399  PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
400 
401  if (remote_port) {
402  lr = port_engine.get_latency_range (remote_port, playback);
403 
405  "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
406  name(), *c, lr.min, lr.max));
407 
408  range.min = min (range.min, lr.min);
409  range.max = max (range.max, lr.max);
410  }
411 
412  } else {
413 
414  /* port belongs to this instance of ardour,
415  so look up its latency information
416  internally, because our published/public
417  values already contain our plugin
418  latency compensation.
419  */
420 
422  if (remote_port) {
423  lr = remote_port->private_latency_range ((playback ? true : false));
425  "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
426  name(), *c, lr.min, lr.max));
427 
428  range.min = min (range.min, lr.min);
429  range.max = max (range.max, lr.max);
430  }
431  }
432  }
433 
434  } else {
435  DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
436  range.min = 0;
437  range.max = 0;
438  }
439 
440  DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
441 }
442 
443 int
445 {
446  DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
447  _port_handle = port_engine.register_port (_name, type(), _flags);
448 
449  if (_port_handle == 0) {
450  PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
451  return -1;
452  }
453 
454  reset ();
455 
456  return 0;
457 }
458 
459 
460 int
462 {
463  /* caller must hold process lock; intended to be used only after reestablish() */
464 
465  DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
466 
467  for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
468  if (connect (*i)) {
469  return -1;
470  }
471  }
472 
473  return 0;
474 }
475 
477 int
478 Port::set_name (std::string const & n)
479 {
480  if (n == _name || !_port_handle) {
481  return 0;
482  }
483 
484  int const r = port_engine.set_port_name (_port_handle, n);
485 
486  if (r == 0) {
488  _name = n;
489  }
490 
491 
492  return r;
493 }
494 
495 bool
497 {
498  if (!_port_handle) {
499  return false;
500  }
501 
502  return port_engine.physically_connected (_port_handle);
503 }
504 
505 XMLNode&
507 {
508  XMLNode* root = new XMLNode (state_node_name);
509 
510  root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
511 
512  if (receives_input()) {
513  root->add_property (X_("direction"), X_("input"));
514  } else {
515  root->add_property (X_("direction"), X_("output"));
516  }
517 
518  vector<string> c;
519 
520  get_connections (c);
521 
522  for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
523  XMLNode* child = new XMLNode (X_("Connection"));
524  child->add_property (X_("other"), *i);
525  root->add_child_nocopy (*child);
526  }
527 
528  return *root;
529 }
530 
531 int
532 Port::set_state (const XMLNode& node, int)
533 {
534  const XMLProperty* prop;
535 
536  if (node.name() != state_node_name) {
537  return -1;
538  }
539 
540  if ((prop = node.property (X_("name"))) != 0) {
541  set_name (prop->value());
542  }
543 
544  const XMLNodeList& children (node.children());
545 
546  _connections.clear ();
547 
548  for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
549 
550  if ((*c)->name() != X_("Connection")) {
551  continue;
552  }
553 
554  if ((prop = (*c)->property (X_("other"))) == 0) {
555  continue;
556  }
557 
558  _connections.insert (prop->value());
559  }
560 
561  return 0;
562 }
virtual DataType type() const =0
std::string to_string(T t, std::ios_base &(*f)(std::ios_base &))
Definition: convert.h:53
virtual ~Port()
Definition: port.cc:81
virtual int set_state(const XMLNode &, int version)
Definition: port.cc:532
LIBARDOUR_API uint64_t Latency
Definition: debug.cc:32
std::string pretty_name(bool fallback_to_name=false) const
Definition: port.cc:88
const std::string & value() const
Definition: xml++.h:159
#define port_manager
Definition: port.cc:51
int connect(std::string const &)
Definition: port.cc:172
virtual XMLNode & get_state(void) const
Definition: port.cc:506
void drop()
Definition: port.cc:107
LIBARDOUR_API uint64_t Ports
Definition: debug.cc:64
bool connected_to(std::string const &) const
Definition: port.cc:147
PBD::ScopedConnection drop_connection
Definition: port.h:172
static std::string state_node_name
Definition: port.h:144
const std::string & name() const
Definition: xml++.h:104
bool sends_output() const
Definition: port.h:74
std::string _name
port short name
Definition: port.h:162
void get_connected_latency_range(LatencyRange &range, bool playback) const
Definition: port.cc:375
uint32_t pframes_t
Definition: types.h:61
tuple f
Definition: signals.py:35
int reestablish()
Definition: port.cc:444
void ensure_input_monitoring(bool)
Definition: port.cc:260
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
static PBD::Signal2< void, boost::shared_ptr< Port >, boost::shared_ptr< Port > > PostDisconnect
Definition: port.h:125
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
void port_renamed(const std::string &, const std::string &)
static bool _connecting_blocked
Definition: port.h:152
std::string name() const
Definition: port.h:54
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
#define port_engine
Definition: port.cc:50
static AudioEngine * instance()
Definition: audioengine.h:196
virtual void reset()
Definition: port.cc:277
bool monitoring_input() const
Definition: port.cc:268
static PBD::Signal0< void > PortDrop
Definition: port.h:126
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
LatencyRange _private_playback_latency
Definition: port.h:158
void set_public_latency_range(LatencyRange &range, bool playback) const
Definition: port.cc:295
framecnt_t _port_buffer_offset
Definition: port.h:156
bool physically_connected() const
Definition: port.cc:496
#define _(Text)
Definition: i18n.h:11
const LatencyRange & private_latency_range(bool playback) const
Definition: port.cc:337
static const char * state_node_name
Definition: chan_count.cc:26
#define X_(Text)
Definition: i18n.h:13
XMLProperty * property(const char *)
Definition: xml++.cc:413
int reconnect()
Definition: port.cc:461
Definition: amp.h:29
virtual void cycle_start(pframes_t)
Definition: port.cc:283
PortFlags
Definition: types.h:610
PortEngine::PortHandle _port_handle
Definition: port.h:150
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
bool connected() const
Definition: port.cc:118
LatencyRange public_latency_range(bool playback) const
Definition: port.cc:357
int set_name(std::string const &)
Definition: port.cc:478
bool _last_monitor
Definition: port.h:164
XMLProperty * add_property(const char *name, const std::string &value)
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
int get_connections(std::vector< std::string > &) const
Definition: port.cc:161
bool receives_input() const
Definition: port.h:69
boost::shared_ptr< Port > get_port_by_name(const std::string &)
Definition: xml++.h:95
uint32_t min
Definition: types.h:622
std::set< std::string > _connections
Definition: port.h:169
virtual void increment_port_buffer_offset(pframes_t n)
Definition: port.cc:289
Definition: debug.h:30
void request_input_monitoring(bool)
Definition: port.cc:252
int disconnect_all()
Definition: port.cc:127
void set_private_latency_range(LatencyRange &range, bool playback)
Definition: port.cc:313
uint32_t max
Definition: types.h:623
std::string make_port_name_non_relative(const std::string &name) const
Definition: port_manager.cc:96
int disconnect(std::string const &)
Definition: port.cc:199
bool port_is_mine(const std::string &fullname) const
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
LatencyRange _private_capture_latency
Definition: port.h:159
PortFlags _flags
flags
Definition: port.h:163