notify IO about port disconnection due to port removal
[ardour.git] / libs / ardour / port.cc
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
53 /** @param n Port short name */
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 {
60         _private_playback_latency.min = 0;
61         _private_playback_latency.max = 0;
62         _private_capture_latency.min = 0;
63         _private_capture_latency.max = 0;
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         port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
79                         boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
80 }
81
82 /** Port destructor */
83 Port::~Port ()
84 {
85         drop ();
86 }
87
88
89 std::string
90 Port::pretty_name(bool fallback_to_name) const
91 {
92         if (_port_handle) {
93                 std::string value;
94                 std::string type;
95                 if (0 == port_engine.get_port_property (_port_handle,
96                                         "http://jackaudio.org/metadata/pretty-name",
97                                         value, type))
98                 {
99                         return value;
100                 }
101         }
102         if (fallback_to_name) {
103                 return name ();
104         }
105         return "";
106 }
107
108 bool
109 Port::set_pretty_name(const std::string& n)
110 {
111         if (_port_handle) {
112                 if (0 == port_engine.set_port_property (_port_handle,
113                                         "http://jackaudio.org/metadata/pretty-name", n, ""))
114                 {
115                         return true;
116                 }
117         }
118         return false;
119 }
120
121 void
122 Port::drop ()
123 {
124         if (_port_handle) {
125                 DEBUG_TRACE (DEBUG::Ports, string_compose ("drop handle for port %1\n", name()));
126                 port_engine.unregister_port (_port_handle);
127                 _port_handle = 0;
128         }
129 }
130
131 void
132 Port::port_connected_or_disconnected (boost::weak_ptr<Port> w0, boost::weak_ptr<Port> w1, bool con)
133 {
134         if (con) {
135                 /* we're only interested in disconnect */
136                 return;
137         }
138         boost::shared_ptr<Port> p0 = w0.lock ();
139         boost::shared_ptr<Port> p1 = w1.lock ();
140         /* a cheaper, less hacky way to do boost::shared_from_this() ...  */
141         boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
142
143         if (p0 == pself) {
144                 PostDisconnect (p0, p1); // emit signal
145         }
146         if (p1 == pself) {
147                 PostDisconnect (p1, p0); // emit signal
148         }
149 }
150
151 /** @return true if this port is connected to anything */
152 bool
153 Port::connected () const
154 {
155         if (_port_handle) {
156                 return (port_engine.connected (_port_handle) != 0);
157         }
158         return false;
159 }
160
161 int
162 Port::disconnect_all ()
163 {
164         if (_port_handle) {
165
166                 std::vector<std::string> connections;
167                 get_connections (connections);
168
169                 port_engine.disconnect_all (_port_handle);
170                 _connections.clear ();
171
172                 /* a cheaper, less hacky way to do boost::shared_from_this() ...
173                  */
174                 boost::shared_ptr<Port> pself = port_manager->get_port_by_name (name());
175                 for (vector<string>::const_iterator c = connections.begin(); c != connections.end() && pself; ++c) {
176                         boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (*c);
177                         if (pother) {
178                                 PostDisconnect (pself, pother); // emit signal
179                         }
180                 }
181         }
182
183         return 0;
184 }
185
186 /** @param o Port name
187  * @return true if this port is connected to o, otherwise false.
188  */
189 bool
190 Port::connected_to (std::string const & o) const
191 {
192         if (!_port_handle) {
193                 return false;
194         }
195
196         if (!port_engine.available()) {
197                 return false;
198         }
199
200         return port_engine.connected_to (_port_handle, AudioEngine::instance()->make_port_name_non_relative (o));
201 }
202
203 int
204 Port::get_connections (std::vector<std::string> & c) const
205 {
206         if (!port_engine.available()) {
207                 c.insert (c.end(), _connections.begin(), _connections.end());
208                 return c.size();
209         }
210
211         if (_port_handle) {
212                 return port_engine.get_connections (_port_handle, c);
213         }
214
215         return 0;
216 }
217
218 int
219 Port::connect (std::string const & other)
220 {
221         std::string const other_name = AudioEngine::instance()->make_port_name_non_relative (other);
222         std::string const our_name = AudioEngine::instance()->make_port_name_non_relative (_name);
223
224         int r = 0;
225
226         if (_connecting_blocked) {
227                 return r;
228         }
229
230         if (sends_output ()) {
231                 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", our_name, other_name));
232                 r = port_engine.connect (our_name, other_name);
233         } else {
234                 DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2\n", other_name, our_name));
235                 r = port_engine.connect (other_name, our_name);
236         }
237
238         if (r == 0) {
239                 _connections.insert (other);
240         }
241
242         return r;
243 }
244
245 int
246 Port::disconnect (std::string const & other)
247 {
248         std::string const other_fullname = port_manager->make_port_name_non_relative (other);
249         std::string const this_fullname = port_manager->make_port_name_non_relative (_name);
250
251         int r = 0;
252
253         if (sends_output ()) {
254                 r = port_engine.disconnect (this_fullname, other_fullname);
255         } else {
256                 r = port_engine.disconnect (other_fullname, this_fullname);
257         }
258
259         if (r == 0) {
260                 _connections.erase (other);
261         }
262
263         /* a cheaper, less hacky way to do boost::shared_from_this() ...  */
264         boost::shared_ptr<Port> pself = AudioEngine::instance()->get_port_by_name (name());
265         boost::shared_ptr<Port> pother = AudioEngine::instance()->get_port_by_name (other);
266
267         if (pself && pother) {
268                 /* Disconnecting from another Ardour port: need to allow
269                    a check on whether this may affect anything that we
270                    need to know about.
271                 */
272                 PostDisconnect (pself, pother); // emit signal
273         }
274
275         return r;
276 }
277
278
279 bool
280 Port::connected_to (Port* o) const
281 {
282         return connected_to (o->name ());
283 }
284
285 int
286 Port::connect (Port* o)
287 {
288         return connect (o->name ());
289 }
290
291 int
292 Port::disconnect (Port* o)
293 {
294         return disconnect (o->name ());
295 }
296
297 void
298 Port::request_input_monitoring (bool yn)
299 {
300         if (_port_handle) {
301                 port_engine.request_input_monitoring (_port_handle, yn);
302         }
303 }
304
305 void
306 Port::ensure_input_monitoring (bool yn)
307 {
308         if (_port_handle) {
309                 port_engine.ensure_input_monitoring (_port_handle, yn);
310         }
311 }
312
313 bool
314 Port::monitoring_input () const
315 {
316         if (_port_handle) {
317                 return port_engine.monitoring_input (_port_handle);
318         }
319         return false;
320 }
321
322 void
323 Port::reset ()
324 {
325         _last_monitor = false;
326 }
327
328 void
329 Port::cycle_start (pframes_t)
330 {
331         _port_buffer_offset = 0;
332 }
333
334 void
335 Port::increment_port_buffer_offset (pframes_t nframes)
336 {
337         _port_buffer_offset += nframes;
338 }
339
340 void
341 Port::set_public_latency_range (LatencyRange& range, bool playback) const
342 {
343         /* this sets the visible latency that the rest of the port system
344            sees. because we do latency compensation, all (most) of our visible
345            port latency values are identical.
346         */
347
348         DEBUG_TRACE (DEBUG::Latency,
349                      string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
350                                      name(), range.min, range.max,
351                                      (playback ? "PLAYBACK" : "CAPTURE")));;
352
353         if (_port_handle) {
354                 port_engine.set_latency_range (_port_handle, playback, range);
355         }
356 }
357
358 void
359 Port::set_private_latency_range (LatencyRange& range, bool playback)
360 {
361         if (playback) {
362                 _private_playback_latency = range;
363                 DEBUG_TRACE (DEBUG::Latency, string_compose (
364                                      "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
365                                      name(),
366                                      _private_playback_latency.min,
367                                      _private_playback_latency.max));
368         } else {
369                 _private_capture_latency = range;
370                 DEBUG_TRACE (DEBUG::Latency, string_compose (
371                                      "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
372                                      name(),
373                                      _private_capture_latency.min,
374                                      _private_capture_latency.max));
375         }
376
377         /* push to public (port system) location so that everyone else can see it */
378
379         set_public_latency_range (range, playback);
380 }
381
382 const LatencyRange&
383 Port::private_latency_range (bool playback) const
384 {
385         if (playback) {
386                 DEBUG_TRACE (DEBUG::Latency, string_compose (
387                                      "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
388                                      name(),
389                                      _private_playback_latency.min,
390                                      _private_playback_latency.max));
391                 return _private_playback_latency;
392         } else {
393                 DEBUG_TRACE (DEBUG::Latency, string_compose (
394                                      "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
395                                      name(),
396                                      _private_playback_latency.min,
397                                      _private_playback_latency.max));
398                 return _private_capture_latency;
399         }
400 }
401
402 LatencyRange
403 Port::public_latency_range (bool /*playback*/) const
404 {
405         LatencyRange r;
406
407
408         if (_port_handle) {
409                 r = port_engine.get_latency_range (_port_handle, sends_output() ? true : false);
410
411                 DEBUG_TRACE (DEBUG::Latency, string_compose (
412                                      "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
413                                      name(), r.min, r.max,
414                                      sends_output() ? "PLAYBACK" : "CAPTURE"));
415         }
416
417         return r;
418 }
419
420 void
421 Port::get_connected_latency_range (LatencyRange& range, bool playback) const
422 {
423         vector<string> connections;
424
425         get_connections (connections);
426
427         if (!connections.empty()) {
428
429                 range.min = ~((pframes_t) 0);
430                 range.max = 0;
431
432                 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
433
434                 for (vector<string>::const_iterator c = connections.begin();
435                      c != connections.end(); ++c) {
436
437                         LatencyRange lr;
438
439                         if (!AudioEngine::instance()->port_is_mine (*c)) {
440
441                                 /* port belongs to some other port-system client, use
442                                  * the port engine to lookup its latency information.
443                                  */
444
445                                 PortEngine::PortHandle remote_port = port_engine.get_port_by_name (*c);
446
447                                 if (remote_port) {
448                                         lr = port_engine.get_latency_range (remote_port, playback);
449
450                                         DEBUG_TRACE (DEBUG::Latency, string_compose (
451                                                              "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
452                                                              name(), *c, lr.min, lr.max));
453
454                                         range.min = min (range.min, lr.min);
455                                         range.max = max (range.max, lr.max);
456                                 }
457
458                         } else {
459
460                                 /* port belongs to this instance of ardour,
461                                    so look up its latency information
462                                    internally, because our published/public
463                                    values already contain our plugin
464                                    latency compensation.
465                                 */
466
467                                 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
468                                 if (remote_port) {
469                                         lr = remote_port->private_latency_range ((playback ? true : false));
470                                         DEBUG_TRACE (DEBUG::Latency, string_compose (
471                                                              "\t%1 <-LOCAL-> %2 : latter has latency range %3 .. %4\n",
472                                                              name(), *c, lr.min, lr.max));
473
474                                         range.min = min (range.min, lr.min);
475                                         range.max = max (range.max, lr.max);
476                                 }
477                         }
478                 }
479
480         } else {
481                 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
482                 range.min = 0;
483                 range.max = 0;
484         }
485
486         DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
487 }
488
489 int
490 Port::reestablish ()
491 {
492         DEBUG_TRACE (DEBUG::Ports, string_compose ("re-establish %1 port %2\n", type().to_string(), _name));
493         _port_handle = port_engine.register_port (_name, type(), _flags);
494
495         if (_port_handle == 0) {
496                 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
497                 return -1;
498         }
499
500         reset ();
501
502         port_manager->PortConnectedOrDisconnected.connect_same_thread (engine_connection,
503                         boost::bind (&Port::port_connected_or_disconnected, this, _1, _3, _5));
504         return 0;
505 }
506
507
508 int
509 Port::reconnect ()
510 {
511         /* caller must hold process lock; intended to be used only after reestablish() */
512
513         DEBUG_TRACE (DEBUG::Ports, string_compose ("Connect %1 to %2 destinations\n",name(), _connections.size()));
514
515         for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
516                 if (connect (*i)) {
517                         return -1;
518                 }
519         }
520
521         return 0;
522 }
523
524 /** @param n Short port name (no port-system client name) */
525 int
526 Port::set_name (std::string const & n)
527 {
528         if (n == _name || !_port_handle) {
529                 return 0;
530         }
531
532         int const r = port_engine.set_port_name (_port_handle, n);
533
534         if (r == 0) {
535                 AudioEngine::instance()->port_renamed (_name, n);
536                 _name = n;
537         }
538
539
540         return r;
541 }
542
543 bool
544 Port::physically_connected () const
545 {
546         if (!_port_handle) {
547                 return false;
548         }
549
550         return port_engine.physically_connected (_port_handle);
551 }
552
553 XMLNode&
554 Port::get_state () const
555 {
556         XMLNode* root = new XMLNode (state_node_name);
557
558         root->add_property (X_("name"), AudioEngine::instance()->make_port_name_relative (name()));
559
560         if (receives_input()) {
561                 root->add_property (X_("direction"), X_("input"));
562         } else {
563                 root->add_property (X_("direction"), X_("output"));
564         }
565
566         vector<string> c;
567
568         get_connections (c);
569
570         for (vector<string>::const_iterator i = c.begin(); i != c.end(); ++i) {
571                 XMLNode* child = new XMLNode (X_("Connection"));
572                 child->add_property (X_("other"), *i);
573                 root->add_child_nocopy (*child);
574         }
575
576         return *root;
577 }
578
579 int
580 Port::set_state (const XMLNode& node, int)
581 {
582         const XMLProperty* prop;
583
584         if (node.name() != state_node_name) {
585                 return -1;
586         }
587
588         if ((prop = node.property (X_("name"))) != 0) {
589                 set_name (prop->value());
590         }
591
592         const XMLNodeList& children (node.children());
593
594         _connections.clear ();
595
596         for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
597
598                 if ((*c)->name() != X_("Connection")) {
599                         continue;
600                 }
601
602                 if ((prop = (*c)->property (X_("other"))) == 0) {
603                         continue;
604                 }
605
606                 _connections.insert (prop->value());
607         }
608
609         return 0;
610 }