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