1 /*
2 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
3 * Copyright (C) 2006-2019 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2007-2011 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #ifdef WAF_BUILD
23 #include "libardour-config.h"
24 #endif
25
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/failed_constructor.h"
29
30 #include "ardour/audioengine.h"
31 #include "ardour/debug.h"
32 #include "ardour/port.h"
33 #include "ardour/port_engine.h"
34 #include "ardour/rc_configuration.h"
35
36 #include "pbd/i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 PBD::Signal0<void> Port::PortDrop;
43 PBD::Signal0<void> Port::PortSignalDrop;
44
45 bool Port::_connecting_blocked = false;
46 pframes_t Port::_global_port_buffer_offset = 0;
47 pframes_t Port::_cycle_nframes = 0;
48 double Port::_speed_ratio = 1.0;
49 std::string Port::state_node_name = X_("Port");
50 const uint32_t Port::_resampler_quality = 17;
51
52 /* a handy define to shorten what would otherwise be a needlessly verbose
53 * repeated phrase
54 */
55 #define port_engine AudioEngine::instance()->port_engine()
56 #define port_manager AudioEngine::instance()
57
58 /** @param n Port short name */
Port(std::string const & n,DataType t,PortFlags f)59 Port::Port (std::string const & n, DataType t, PortFlags f)
60 : _name (n)
61 , _flags (f)
62 , _last_monitor (false)
63 , _externally_connected (0)
64 {
65 _private_playback_latency.min = 0;
66 _private_playback_latency.max = 0;
67 _private_capture_latency.min = 0;
68 _private_capture_latency.max = 0;
69
70 /* Unfortunately we have to pass the DataType into this constructor so that
71 we can create the right kind of port; aside from this we'll use the
72 virtual function type () to establish type.
73 */
74
75 assert (_name.find_first_of (':') == std::string::npos);
76
77 if (!port_manager->running ()) {
78 DEBUG_TRACE (DEBUG::Ports, string_compose ("port-engine n/a postpone registering %1\n", name()));
79 _port_handle.reset (); // created during ::reestablish() later
80 } else if ((_port_handle = port_engine.register_port (_name, t, _flags)) == 0) {
81 cerr << "Failed to register port \"" << _name << "\", reason is unknown from here\n";
82 throw failed_constructor ();
83 }
84 DEBUG_TRACE (DEBUG::Ports, string_compose ("registed port %1 handle %2\n", name(), _port_handle));
85
86 PortDrop.connect_same_thread (drop_connection, boost::bind (&Port::session_global_drop, this));
87 PortSignalDrop.connect_same_thread (drop_connection, boost::bind (&Port::signal_drop, this));
88 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
89 }
90
91 /** Port destructor */
~Port()92 Port::~Port ()
93 {
94 DEBUG_TRACE (PBD::DebugBits (DEBUG::Destruction|DEBUG::Ports), string_compose ("destroying port @ %1 named %2\n", this, name()));
95 drop ();
96 }
97
98 std::string
pretty_name(bool fallback_to_name) const99 Port::pretty_name(bool fallback_to_name) const
100 {
101 if (_port_handle) {
102 std::string value;
103 std::string type;
104 if (0 == port_engine.get_port_property (_port_handle,
105 "http://jackaudio.org/metadata/pretty-name",
106 value, type))
107 {
108 return value;
109 }
110 }
111 if (fallback_to_name) {
112 return name ();
113 }
114 return "";
115 }
116
117 bool
set_pretty_name(const std::string & n)118 Port::set_pretty_name(const std::string& n)
119 {
120 if (_port_handle) {
121 if (0 == port_engine.set_port_property (_port_handle,
122 "http://jackaudio.org/metadata/pretty-name", n, ""))
123 {
124 return true;
125 }
126 }
127 return false;
128 }
129
130 void
session_global_drop()131 Port::session_global_drop()
132 {
133 if (_flags & TransportMasterPort) {
134 return;
135 }
136
137 drop ();
138 }
139
140 void
signal_drop()141 Port::signal_drop ()
142 {
143 engine_connection.disconnect ();
144 }
145
146 void
drop()147 Port::drop ()
148 {
149 if (_port_handle) {
150 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
151 port_engine.unregister_port (_port_handle);
152 _port_handle.reset ();;
153 }
154 }
155
156 void
port_connected_or_disconnected(boost::weak_ptr<Port> w0,boost::weak_ptr<Port> w1,bool con)157 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
158 {
159 boost::shared_ptr<Port> p0 = w0.lock ();
160 boost::shared_ptr<Port> p1 = w1.lock ();
161 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
162 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
163
164 if (p0 == pself) {
165 ConnectedOrDisconnected (p0, p1, con); // emit signal
166 }
167 if (p1 == pself) {
168 ConnectedOrDisconnected (p1, p0, con); // emit signal
169 }
170 }
171
172 /** @return true if this port is connected to anything */
173 bool
connected() const174 Port::connected () const
175 {
176 if (_port_handle) {
177 return (port_engine.connected (_port_handle) != 0);
178 }
179 return false;
180 }
181
182 int
disconnect_all()183 Port::disconnect_all ()
184 {
185 if (_port_handle) {
186
187 std::vector<std::string> connections;
188 get_connections (connections);
189
190 port_engine.disconnect_all (_port_handle);
191 _connections.clear ();
192
193 /* a cheaper, less hacky way to do boost::shared_from_this() ...
194 */
195 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
196 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
197 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
198 if (pother) {
199 ConnectedOrDisconnected (pself, pother, false); // emit signal
200 }
201 }
202 }
203
204 return 0;
205 }
206
207 /** @param o Port name
208 * @return true if this port is connected to o, otherwise false.
209 */
210 bool
connected_to(std::string const & o) const211 Port::connected_to (std::string const & o) const
212 {
213 if (!_port_handle) {
214 return false;
215 }
216
217 if (!port_manager->running()) {
218 return false;
219 }
220
221 return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
222 }
223
224 int
get_connections(std::vector<std::string> & c) const225 Port::get_connections (std::vector<std::string> & c) const
226 {
227 if (!port_manager->running()) {
228 c.insert (c.end(), _connections.begin(), _connections.end());
229 return c.size();
230 }
231
232 if (_port_handle) {
233 return port_engine.get_connections (_port_handle, c);
234 }
235
236 return 0;
237 }
238
239 int
connect(std::string const & other)240 Port::connect (std::string const & other)
241 {
242 std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
243 std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
244
245 int r = 0;
246
247 if (_connecting_blocked) {
248 return r;
249 }
250
251 if (sends_output ()) {
252 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
253 r = port_engine.connect (our_name, other_name);
254 } else {
255 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
256 r = port_engine.connect (other_name, our_name);
257 }
258
259 if (r == 0) {
260 _connections.insert (other);
261 }
262
263 return r;
264 }
265
266 int
disconnect(std::string const & other)267 Port::disconnect (std::string const & other)
268 {
269 std::string const other_fullname = port_manager->make_port_name_non_relative (other);
270 std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
271
272 int r = 0;
273
274 if (sends_output ()) {
275 r = port_engine.disconnect (this_fullname, other_fullname);
276 } else {
277 r = port_engine.disconnect (other_fullname, this_fullname);
278 }
279
280 if (r == 0) {
281 _connections.erase (other);
282 }
283
284 /* a cheaper, less hacky way to do boost::shared_from_this() ... */
285 boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
286 boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
287
288 if (pself && pother) {
289 /* Disconnecting from another Ardour port: need to allow
290 a check on whether this may affect anything that we
291 need to know about.
292 */
293 ConnectedOrDisconnected (pself, pother, false); // emit signal
294 }
295
296 return r;
297 }
298
299
300 bool
connected_to(Port * o) const301 Port::connected_to (Port* o) const
302 {
303 return connected_to (o->name ());
304 }
305
306 int
connect(Port * o)307 Port::connect (Port* o)
308 {
309 return connect (o->name ());
310 }
311
312 int
disconnect(Port * o)313 Port::disconnect (Port* o)
314 {
315 return disconnect (o->name ());
316 }
317
318 void
request_input_monitoring(bool yn)319 Port::request_input_monitoring (bool yn)
320 {
321 if (_port_handle) {
322 port_engine.request_input_monitoring (_port_handle, yn);
323 }
324 }
325
326 void
ensure_input_monitoring(bool yn)327 Port::ensure_input_monitoring (bool yn)
328 {
329 if (_port_handle) {
330 port_engine.ensure_input_monitoring (_port_handle, yn);
331 }
332 }
333
334 bool
monitoring_input() const335 Port::monitoring_input () const
336 {
337 if (_port_handle) {
338 return port_engine.monitoring_input (_port_handle);
339 }
340 return false;
341 }
342
343 void
reset()344 Port::reset ()
345 {
346 _last_monitor = false;
347 _externally_connected = 0;
348 }
349
350 void
cycle_start(pframes_t)351 Port::cycle_start (pframes_t)
352 {
353 }
354
355 void
set_public_latency_range(LatencyRange const & range,bool playback) const356 Port::set_public_latency_range (LatencyRange const& range, bool playback) const
357 {
358 /* this sets the visible latency that the rest of the port system
359 sees. because we do latency compensation, all (most) of our visible
360 port latency values are identical.
361 */
362
363 DEBUG_TRACE (DEBUG::LatencyIO,
364 string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
365 name(), range.min, range.max,
366 (playback ? "PLAYBACK" : "CAPTURE")));;
367
368 if (_port_handle) {
369 LatencyRange r (range);
370 if (externally_connected () && 0 == (_flags & TransportSyncPort)) {
371 #if 0
372 r.min *= _speed_ratio;
373 r.max *= _speed_ratio;
374 #endif
375 if (type () == DataType::AUDIO) {
376 r.min += (_resampler_quality - 1);
377 r.max += (_resampler_quality - 1);
378 }
379 }
380 port_engine.set_latency_range (_port_handle, playback, r);
381 }
382 }
383
384 void
set_private_latency_range(LatencyRange & range,bool playback)385 Port::set_private_latency_range (LatencyRange& range, bool playback)
386 {
387 if (playback) {
388 _private_playback_latency = range;
389 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
390 "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
391 name(),
392 _private_playback_latency.min,
393 _private_playback_latency.max));
394 } else {
395 _private_capture_latency = range;
396 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
397 "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
398 name(),
399 _private_capture_latency.min,
400 _private_capture_latency.max));
401 }
402
403 /* push to public (port system) location so that everyone else can see it */
404
405 set_public_latency_range (range, playback);
406 }
407
408 const LatencyRange&
private_latency_range(bool playback) const409 Port::private_latency_range (bool playback) const
410 {
411 if (playback) {
412 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
413 "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
414 name(),
415 _private_playback_latency.min,
416 _private_playback_latency.max));
417 return _private_playback_latency;
418 } else {
419 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
420 "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
421 name(),
422 _private_playback_latency.min,
423 _private_playback_latency.max));
424 return _private_capture_latency;
425 }
426 }
427
428 LatencyRange
public_latency_range(bool) const429 Port::public_latency_range (bool /*playback*/) const
430 {
431 /*Note: this method is no longer used. It exists purely for debugging reasons */
432 LatencyRange r;
433
434 if (_port_handle) {
435 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
436 if (externally_connected () && 0 == (_flags & TransportSyncPort)) {
437 #if 0
438 r.min /= _speed_ratio;
439 r.max /= _speed_ratio;
440 #endif
441 #if 0
442 /* use value as set by set_public_latency_range */
443 if (type () == DataType::AUDIO) {
444 r.min += (_resampler_quality - 1);
445 r.max += (_resampler_quality - 1);
446 }
447 #endif
448 }
449
450 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
451 "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
452 name(), r.min, r.max,
453 sends_output() ? "PLAYBACK" : "CAPTURE"));
454 }
455
456 return r;
457 }
458
459 void
get_connected_latency_range(LatencyRange & range,bool playback) const460 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
461 {
462 vector<string> connections;
463
464 get_connections (connections);
465
466 if (!connections.empty()) {
467
468 range.min = ~((pframes_t) 0);
469 range.max = 0;
470
471 DEBUG_TRACE (DEBUG::LatencyIO, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
472
473 for (vector<string>::const_iterator c = connections.begin();
474 c != connections.end(); ++c) {
475
476 LatencyRange lr;
477
478 if (!AudioEngine::instance()->port_is_mine (*c)) {
479
480 /* port belongs to some other port-system client, use
481 * the port engine to lookup its latency information.
482 */
483
484 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
485
486 if (remote_port) {
487 lr = port_engine.get_latency_range (remote_port, playback);
488 if (externally_connected () && 0 == (_flags & TransportSyncPort)) {
489 #if 0
490 lr.min /= _speed_ratio;
491 lr.max /= _speed_ratio;
492 #endif
493 if (type () == DataType::AUDIO) {
494 lr.min += (_resampler_quality - 1);
495 lr.max += (_resampler_quality - 1);
496 }
497 }
498
499 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
500 "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
501 name(), *c, lr.min, lr.max));
502
503 range.min = min (range.min, lr.min);
504 range.max = max (range.max, lr.max);
505 }
506
507 } else {
508
509 /* port belongs to this instance of ardour,
510 * so look up its latency information
511 * internally, because our published/public
512 * values already contain our plugin
513 * latency compensation.
514 */
515
516 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
517 if (remote_port) {
518 lr = remote_port->private_latency_range ((playback ? true : false));
519 DEBUG_TRACE (DEBUG::LatencyIO, string_compose (
520 "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
521 name(), *c, lr.min, lr.max));
522
523 range.min = min (range.min, lr.min);
524 range.max = max (range.max, lr.max);
525 }
526 }
527 }
528
529 } else {
530 DEBUG_TRACE (DEBUG::LatencyIO, string_compose ("%1: not connected to anything\n", name()));
531 range.min = 0;
532 range.max = 0;
533 }
534
535 DEBUG_TRACE (DEBUG::LatencyIO, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
536 }
537
538 int
reestablish()539 Port::reestablish ()
540 {
541 DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
542 _port_handle = port_engine.register_port (_name, type(), _flags);
543
544 if (_port_handle == 0) {
545 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
546 return -1;
547 }
548
549 DEBUG_TRACE (DEBUG::Ports, string_compose ("Port::reestablish %1 handle %2\n", name(), _port_handle));
550
551 reset ();
552
553 port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection, boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
554 return 0;
555 }
556
557
558 int
reconnect()559 Port::reconnect ()
560 {
561 /* caller must hold process lock; intended to be used only after reestablish() */
562
563 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
564
565 for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
566 if (connect (*i)) {
567 _connections.clear ();
568 return -1;
569 }
570 }
571
572 return 0;
573 }
574
575 /** @param n Short port name (no port-system client name) */
576 int
set_name(std::string const & n)577 Port::set_name (std::string const & n)
578 {
579 if (n == _name || !_port_handle) {
580 return 0;
581 }
582
583 int const r = port_engine.set_port_name (_port_handle, n);
584
585 if (r == 0) {
586 AudioEngine::instance()->port_renamed (_name, n);
587 _name = n;
588 }
589
590
591 return r;
592 }
593
594 bool
physically_connected() const595 Port::physically_connected () const
596 {
597 if (!_port_handle) {
598 return false;
599 }
600
601 return port_engine.physically_connected (_port_handle);
602 }
603
604 XMLNode&
get_state() const605 Port::get_state () const
606 {
607 XMLNode* root = new XMLNode (state_node_name);
608
609 root->set_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
610
611 if (receives_input()) {
612 root->set_property (X_("direction"), X_("input"));
613 } else {
614 root->set_property (X_("direction"), X_("output"));
615 }
616
617 vector<string> c;
618
619 get_connections (c);
620
621 for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
622 XMLNode* child = new XMLNode (X_("Connection"));
623 child->set_property (X_("other"), *i);
624 root->add_child_nocopy (*child);
625 }
626
627 return *root;
628 }
629
630 int
set_state(const XMLNode & node,int)631 Port::set_state (const XMLNode& node, int)
632 {
633 if (node.name() != state_node_name) {
634 return -1;
635 }
636
637 std::string str;
638 if (node.get_property (X_("name"), str)) {
639 set_name (str);
640 }
641
642 const XMLNodeList& children (node.children());
643
644 _connections.clear ();
645
646 for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
647
648 if ((*c)->name() != X_("Connection")) {
649 continue;
650 }
651
652 if (!(*c)->get_property (X_("other"), str)) {
653 continue;
654 }
655
656 _connections.insert (str);
657 }
658
659 return 0;
660 }
661
662 /*static*/ void
set_speed_ratio(double s)663 Port::set_speed_ratio (double s) {
664 /* see VMResampler::set_rratio() for min/max range */
665 if (s == 0.0) {
666 /* no resampling when stopped */
667 _speed_ratio = 1.0;
668 } else {
669 _speed_ratio = std::min ((double) Config->get_max_transport_speed(), std::max (0.02, fabs (s)));
670 }
671 }
672
673 /*static*/ void
set_cycle_samplecnt(pframes_t n)674 Port::set_cycle_samplecnt (pframes_t n)
675 {
676 _cycle_nframes = floor (n * _speed_ratio);
677 }
678