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