change audioengine's port container to a std::map to provide faster results from...
[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 <stdexcept>
25
26 #include <jack/weakjack.h> // so that we can test for new functions at runtime
27
28 #include "pbd/error.h"
29 #include "pbd/compose.h"
30
31 #include "ardour/debug.h"
32 #include "ardour/port.h"
33 #include "ardour/audioengine.h"
34 #include "pbd/failed_constructor.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 PBD::Signal2<void,boost::shared_ptr<Port>, boost::shared_ptr<Port> > Port::PostDisconnect;
43
44 AudioEngine* Port::_engine = 0;
45 bool         Port::_connecting_blocked = false;
46 pframes_t    Port::_global_port_buffer_offset = 0;
47 pframes_t    Port::_cycle_nframes = 0;
48
49 /** @param n Port short name */
50 Port::Port (std::string const & n, DataType t, Flags f)
51         : _port_buffer_offset (0)
52         , _name (n)
53         , _flags (f)
54         , _last_monitor (false)
55 {
56         _private_playback_latency.min = 0;
57         _private_playback_latency.max = 0;
58         _private_capture_latency.min = 0;
59         _private_capture_latency.max = 0;
60
61         /* Unfortunately we have to pass the DataType into this constructor so that
62            we can create the right kind of JACK port; aside from this we'll use the
63            virtual function type () to establish type.
64         */
65
66         assert (_name.find_first_of (':') == std::string::npos);
67
68         if (!_engine->connected()) {
69                 throw failed_constructor ();
70         }
71
72         if ((_jack_port = jack_port_register (_engine->jack (), _name.c_str (), t.to_jack_type (), _flags, 0)) == 0) {
73                 cerr << "Failed to register JACK port, reason is unknown from here\n";
74                 throw failed_constructor ();
75         }
76 }
77
78 /** Port destructor */
79 Port::~Port ()
80 {
81         if (_engine->jack ()) {
82                 jack_port_unregister (_engine->jack (), _jack_port);
83         }
84 }
85
86 /** @return true if this port is connected to anything */
87 bool
88 Port::connected () const
89 {
90         return (jack_port_connected (_jack_port) != 0);
91 }
92
93 int
94 Port::disconnect_all ()
95 {
96         jack_port_disconnect (_engine->jack(), _jack_port);
97         _connections.clear ();
98
99         /* a cheaper, less hacky way to do boost::shared_from_this() ... 
100          */
101         boost::shared_ptr<Port> pself = _engine->get_port_by_name (name());
102         PostDisconnect (pself, boost::shared_ptr<Port>()); // emit signal
103
104         return 0;
105 }
106
107 /** @param o Port name
108  * @return true if this port is connected to o, otherwise false.
109  */
110 bool
111 Port::connected_to (std::string const & o) const
112 {
113         if (!_engine->connected()) {
114                 /* in some senses, this answer isn't the right one all the time,
115                    because we know about our connections and will re-establish
116                    them when we reconnect to JACK.
117                 */
118                 return false;
119         }
120
121         return jack_port_connected_to (_jack_port,
122                                        _engine->make_port_name_non_relative(o).c_str ());
123 }
124
125 /** @param o Filled in with port full names of ports that we are connected to */
126 int
127 Port::get_connections (std::vector<std::string> & c) const
128 {
129         int n = 0;
130
131         if (_engine->connected()) {
132                 const char** jc = jack_port_get_connections (_jack_port);
133                 if (jc) {
134                         for (int i = 0; jc[i]; ++i) {
135                                 c.push_back (jc[i]);
136                                 ++n;
137                         }
138
139                         if (jack_free) {
140                                 jack_free (jc);
141                         } else {
142                                 free (jc);
143                         }
144                 }
145         }
146
147         return n;
148 }
149
150 int
151 Port::connect (std::string const & other)
152 {
153         std::string const other_shrt = _engine->make_port_name_non_relative (other);
154         std::string const this_shrt = _engine->make_port_name_non_relative (_name);
155
156         int r = 0;
157
158         if (_connecting_blocked) {
159                 return r;
160         }
161
162         if (sends_output ()) {
163                 r = jack_connect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ());
164         } else {
165                 r = jack_connect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str());
166         }
167
168         if (r == 0) {
169                 _connections.insert (other);
170         }
171
172         return r;
173 }
174
175 int
176 Port::disconnect (std::string const & other)
177 {
178         std::string const other_fullname = _engine->make_port_name_non_relative (other);
179         std::string const this_fullname = _engine->make_port_name_non_relative (_name);
180
181         int r = 0;
182
183         if (sends_output ()) {
184                 r = jack_disconnect (_engine->jack (), this_fullname.c_str (), other_fullname.c_str ());
185         } else {
186                 r = jack_disconnect (_engine->jack (), other_fullname.c_str (), this_fullname.c_str ());
187         }
188
189         if (r == 0) {
190                 _connections.erase (other);
191         }
192
193         /* a cheaper, less hacky way to do boost::shared_from_this() ... 
194          */
195         boost::shared_ptr<Port> pself = _engine->get_port_by_name (name());
196         boost::shared_ptr<Port> pother = _engine->get_port_by_name (other);
197
198         if (pself && pother) {
199                 /* Disconnecting from another Ardour port: need to allow
200                    a check on whether this may affect anything that we
201                    need to know about.
202                 */
203                 PostDisconnect (pself, pother); // emit signal 
204         }
205
206         return r;
207 }
208
209
210 bool
211 Port::connected_to (Port* o) const
212 {
213         return connected_to (o->name ());
214 }
215
216 int
217 Port::connect (Port* o)
218 {
219         return connect (o->name ());
220 }
221
222 int
223 Port::disconnect (Port* o)
224 {
225         return disconnect (o->name ());
226 }
227
228 void
229 Port::set_engine (AudioEngine* e)
230 {
231         _engine = e;
232 }
233
234 void
235 Port::ensure_jack_monitors_input (bool yn)
236 {
237         jack_port_ensure_monitor (_jack_port, yn);
238 }
239
240 bool
241 Port::jack_monitoring_input () const
242 {
243         return jack_port_monitoring_input (_jack_port);
244 }
245
246 void
247 Port::reset ()
248 {
249         _last_monitor = false;
250 }
251
252 void
253 Port::cycle_start (pframes_t)
254 {
255         _port_buffer_offset = 0;
256 }
257
258 void
259 Port::increment_port_buffer_offset (pframes_t nframes)
260 {
261         _port_buffer_offset += nframes;
262 }
263
264 void
265 Port::set_public_latency_range (jack_latency_range_t& range, bool playback) const
266 {
267         /* this sets the visible latency that the rest of JACK sees. because we do latency
268            compensation, all (most) of our visible port latency values are identical.
269         */
270
271         if (!jack_port_set_latency_range) {
272                 return;
273         }
274
275         DEBUG_TRACE (DEBUG::Latency,
276                      string_compose ("SET PORT %1 %4 PUBLIC latency now [%2 - %3]\n",
277                                      name(), range.min, range.max,
278                                      (playback ? "PLAYBACK" : "CAPTURE")));;
279
280         jack_port_set_latency_range (_jack_port,
281                                      (playback ? JackPlaybackLatency : JackCaptureLatency),
282                                      &range);
283 }
284
285 void
286 Port::set_private_latency_range (jack_latency_range_t& range, bool playback)
287 {
288         if (playback) {
289                 _private_playback_latency = range;
290                 DEBUG_TRACE (DEBUG::Latency, string_compose (
291                                      "SET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
292                                      name(),
293                                      _private_playback_latency.min,
294                                      _private_playback_latency.max));
295         } else {
296                 _private_capture_latency = range;
297                 DEBUG_TRACE (DEBUG::Latency, string_compose (
298                                      "SET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
299                                      name(),
300                                      _private_capture_latency.min,
301                                      _private_capture_latency.max));
302         }
303
304         /* push to public (JACK) location so that everyone else can see it */
305
306         set_public_latency_range (range, playback);
307 }
308
309 const jack_latency_range_t&
310 Port::private_latency_range (bool playback) const
311 {
312         if (playback) {
313                 DEBUG_TRACE (DEBUG::Latency, string_compose (
314                                      "GET PORT %1 playback PRIVATE latency now [%2 - %3]\n",
315                                      name(),
316                                      _private_playback_latency.min,
317                                      _private_playback_latency.max));
318                 return _private_playback_latency;
319         } else {
320                 DEBUG_TRACE (DEBUG::Latency, string_compose (
321                                      "GET PORT %1 capture PRIVATE latency now [%2 - %3]\n",
322                                      name(),
323                                      _private_playback_latency.min,
324                                      _private_playback_latency.max));
325                 return _private_capture_latency;
326         }
327 }
328
329 jack_latency_range_t
330 Port::public_latency_range (bool /*playback*/) const
331 {
332         jack_latency_range_t r;
333
334         jack_port_get_latency_range (_jack_port,
335                                      sends_output() ? JackPlaybackLatency : JackCaptureLatency,
336                                      &r);
337         DEBUG_TRACE (DEBUG::Latency, string_compose (
338                              "GET PORT %1: %4 PUBLIC latency range %2 .. %3\n",
339                              name(), r.min, r.max,
340                              sends_output() ? "PLAYBACK" : "CAPTURE"));
341         return r;
342 }
343
344 void
345 Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const
346 {
347         if (!jack_port_get_latency_range) {
348                 return;
349         }
350
351         vector<string> connections;
352         jack_client_t* jack = _engine->jack();
353
354         if (!jack) {
355                 range.min = 0;
356                 range.max = 0;
357                 PBD::warning << _("get_connected_latency_range() called while disconnected from JACK") << endmsg;
358                 return;
359         }
360
361         get_connections (connections);
362
363         if (!connections.empty()) {
364
365                 range.min = ~((jack_nframes_t) 0);
366                 range.max = 0;
367
368                 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: %2 connections to check for latency range\n", name(), connections.size()));
369
370                 for (vector<string>::const_iterator c = connections.begin();
371                      c != connections.end(); ++c) {
372
373                         jack_latency_range_t lr;
374
375                         if (!AudioEngine::instance()->port_is_mine (*c)) {
376
377                                 /* port belongs to some other JACK client, use
378                                  * JACK to lookup its latency information.
379                                  */
380
381                                 jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str());
382
383                                 if (remote_port) {
384                                         jack_port_get_latency_range (
385                                                 remote_port,
386                                                 (playback ? JackPlaybackLatency : JackCaptureLatency),
387                                                 &lr);
388
389                                         DEBUG_TRACE (DEBUG::Latency, string_compose (
390                                                              "\t%1 <-> %2 : latter has latency range %3 .. %4\n",
391                                                              name(), *c, lr.min, lr.max));
392
393                                         range.min = min (range.min, lr.min);
394                                         range.max = max (range.max, lr.max);
395                                 }
396
397                         } else {
398
399                                 /* port belongs to this instance of ardour,
400                                    so look up its latency information
401                                    internally, because our published/public
402                                    values already contain our plugin
403                                    latency compensation.
404                                 */
405
406                                 boost::shared_ptr<Port> remote_port = AudioEngine::instance()->get_port_by_name (*c);
407                                 if (remote_port) {
408                                         lr = remote_port->private_latency_range ((playback ? JackPlaybackLatency : JackCaptureLatency));
409                                         DEBUG_TRACE (DEBUG::Latency, string_compose (
410                                                              "\t%1 <-LOCAL-> %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                 }
418
419         } else {
420                 DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: not connected to anything\n", name()));
421                 range.min = 0;
422                 range.max = 0;
423         }
424
425         DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: final connected latency range [ %2 .. %3 ] \n", name(), range.min, range.max));
426 }
427
428 int
429 Port::reestablish ()
430 {
431         jack_client_t* jack = _engine->jack();
432
433         if (!jack) {
434                 return -1;
435         }
436
437         _jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0);
438
439         if (_jack_port == 0) {
440                 PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
441                 return -1;
442         }
443
444         reset ();
445
446         return 0;
447 }
448
449
450 int
451 Port::reconnect ()
452 {
453         /* caller must hold process lock; intended to be used only after reestablish() */
454
455         for (std::set<string>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
456                 if (connect (*i)) {
457                         return -1;
458                 }
459         }
460
461         return 0;
462 }
463
464 /** @param n Short port name (no JACK client name) */
465 int
466 Port::set_name (std::string const & n)
467 {
468         if (n == _name) {
469                 return 0;
470         }
471
472         int const r = jack_port_set_name (_jack_port, n.c_str());
473
474         if (r == 0) {
475                 _engine->port_renamed (_name, n);
476                 _name = n;
477         }
478
479
480         return r;
481 }
482
483 void
484 Port::request_jack_monitors_input (bool yn)
485 {
486         jack_port_request_monitor (_jack_port, yn);
487 }
488
489 bool
490 Port::physically_connected () const
491 {
492         const char** jc = jack_port_get_connections (_jack_port);
493
494         if (jc) {
495                 for (int i = 0; jc[i]; ++i) {
496
497                         jack_port_t* port = jack_port_by_name (_engine->jack(), jc[i]);
498
499                         if (port && (jack_port_flags (port) & JackPortIsPhysical)) {
500                                 if (jack_free) {
501                                         jack_free (jc);
502                                 } else {
503                                         free (jc);
504                                 }
505                                 return true;
506                         }
507                 }
508                 if (jack_free) {
509                         jack_free (jc);
510                 } else {
511                         free (jc);
512                 }
513         }
514
515         return false;
516 }
517