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