5c807a6979363d5bee49bbfc7dba66767909d0aa
[ardour.git] / libs / ardour / port_manager.cc
1 /*
2     Copyright (C) 2013 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 #include "pbd/error.h"
21
22 #include "midi++/manager.h"
23
24 #include "ardour/port_manager.h"
25 #include "ardour/audio_port.h"
26 #include "ardour/midi_port.h"
27
28 #include "i18n.h"
29
30 using namespace ARDOUR;
31 using namespace PBD;
32 using std::string;
33 using std::vector;
34
35 PortManager::PortManager ()
36         : ports (new Ports)
37         , _port_remove_in_progress (false)
38 {
39 }
40
41 void
42 PortManager::remove_all_ports ()
43 {
44         /* make sure that JACK callbacks that will be invoked as we cleanup
45          * ports know that they have nothing to do.
46          */
47
48         _port_remove_in_progress = true;
49
50         /* process lock MUST be held by caller
51         */
52
53         {
54                 RCUWriter<Ports> writer (ports);
55                 boost::shared_ptr<Ports> ps = writer.get_copy ();
56                 ps->clear ();
57         }
58
59         /* clear dead wood list in RCU */
60
61         ports.flush ();
62
63         _port_remove_in_progress = false;
64 }
65
66
67 string
68 PortManager::make_port_name_relative (const string& portname) const
69 {
70         if (!_impl) {
71                 return portname;
72         }
73
74         string::size_type len;
75         string::size_type n;
76         string self = _impl->my_name();
77
78         len = portname.length();
79
80         for (n = 0; n < len; ++n) {
81                 if (portname[n] == ':') {
82                         break;
83                 }
84         }
85
86         if ((n != len) && (portname.substr (0, n) == self)) {
87                 return portname.substr (n+1);
88         }
89
90         return portname;
91 }
92
93 string
94 PortManager::make_port_name_non_relative (const string& portname) const
95 {
96         string str;
97
98         if (portname.find_first_of (':') != string::npos) {
99                 return portname;
100         }
101
102         str  = _impl->my_name();
103         str += ':';
104         str += portname;
105
106         return str;
107 }
108
109 bool
110 PortManager::port_is_mine (const string& portname) const
111 {
112         if (!_impl) {
113                 return true;
114         }
115
116         string self = _impl->my_name();
117
118         if (portname.find_first_of (':') != string::npos) {
119                 if (portname.substr (0, self.length ()) != self) {
120                         return false;
121                 }
122         }
123
124         return true;
125 }
126
127 bool
128 PortManager::port_is_physical (const std::string& portname) const
129 {
130         if (!_impl) {
131                 return false;
132         }
133
134         PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
135         if (!ph) {
136                 return false;
137         }
138
139         return _impl->port_is_physical (ph);
140 }
141
142 void
143 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
144 {
145         if (!_impl) {
146                 return;
147         }
148         _impl->get_physical_outputs (type, s);
149 }
150  
151 void
152 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
153 {
154         if (!_impl) {
155                 return;
156         }
157
158         _impl->get_physical_inputs (type, s);
159 }
160  
161 ChanCount
162 PortManager::n_physical_outputs () const
163 {
164         if (!_impl) {
165                 return ChanCount::ZERO;
166         }
167
168         return _impl->n_physical_outputs ();
169 }
170  
171 ChanCount
172 PortManager::n_physical_inputs () const
173 {
174         if (!_impl) {
175                 return ChanCount::ZERO;
176         }
177         return _impl->n_physical_inputs ();
178 }
179
180 /** @param name Full or short name of port
181  *  @return Corresponding Port or 0.
182  */
183
184 boost::shared_ptr<Port>
185 PortManager::get_port_by_name (const string& portname)
186 {
187         if (!_impl) {
188                 return boost::shared_ptr<Port>();
189         }
190
191         if (!port_is_mine (portname)) {
192                 /* not an ardour port */
193                 return boost::shared_ptr<Port> ();
194         }
195
196         boost::shared_ptr<Ports> pr = ports.reader();
197         std::string rel = make_port_name_relative (portname);
198         Ports::iterator x = pr->find (rel);
199
200         if (x != pr->end()) {
201                 /* its possible that the port was renamed by some 3rd party and
202                    we don't know about it. check for this (the check is quick
203                    and cheap), and if so, rename the port (which will alter
204                    the port map as a side effect).
205                 */
206                 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
207                 if (check != rel) {
208                         x->second->set_name (check);
209                 }
210                 return x->second;
211         }
212
213         return boost::shared_ptr<Port> ();
214 }
215
216 void
217 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
218 {
219         RCUWriter<Ports> writer (ports);
220         boost::shared_ptr<Ports> p = writer.get_copy();
221         Ports::iterator x = p->find (old_relative_name);
222         
223         if (x != p->end()) {
224                 boost::shared_ptr<Port> port = x->second;
225                 p->erase (x);
226                 p->insert (make_pair (new_relative_name, port));
227         }
228 }
229
230 int
231 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
232 {
233         if (!_impl) {
234                 return 0;
235         }
236
237         return _impl->get_ports (port_name_pattern, type, flags, s);
238 }
239
240 void
241 PortManager::port_registration_failure (const std::string& portname)
242 {
243         if (!_impl) {
244                 return;
245         }
246
247         string full_portname = _impl->my_name();
248         full_portname += ':';
249         full_portname += portname;
250
251
252         PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
253         string reason;
254
255         if (p) {
256                 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
257         } else {
258                 reason = string_compose (_("No more ports are available. You will need to stop %1 and restart with more ports if you need this many tracks."), PROGRAM_NAME);
259         }
260
261         throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
262 }
263
264 boost::shared_ptr<Port>
265 PortManager::register_port (DataType dtype, const string& portname, bool input)
266 {
267         boost::shared_ptr<Port> newport;
268
269         try {
270                 if (dtype == DataType::AUDIO) {
271                         newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
272                 } else if (dtype == DataType::MIDI) {
273                         newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
274                 } else {
275                         throw PortRegistrationFailure("unable to create port (unknown type)");
276                 }
277
278                 RCUWriter<Ports> writer (ports);
279                 boost::shared_ptr<Ports> ps = writer.get_copy ();
280                 ps->insert (make_pair (make_port_name_relative (portname), newport));
281
282                 /* writer goes out of scope, forces update */
283
284                 return newport;
285         }
286
287         catch (PortRegistrationFailure& err) {
288                 throw err;
289         } catch (std::exception& e) {
290                 throw PortRegistrationFailure(string_compose(
291                                 _("unable to create port: %1"), e.what()).c_str());
292         } catch (...) {
293                 throw PortRegistrationFailure("unable to create port (unknown error)");
294         }
295 }
296
297 boost::shared_ptr<Port>
298 PortManager::register_input_port (DataType type, const string& portname)
299 {
300         return register_port (type, portname, true);
301 }
302
303 boost::shared_ptr<Port>
304 PortManager::register_output_port (DataType type, const string& portname)
305 {
306         return register_port (type, portname, false);
307 }
308
309 int
310 PortManager::unregister_port (boost::shared_ptr<Port> port)
311 {
312         /* caller must hold process lock */
313
314         {
315                 RCUWriter<Ports> writer (ports);
316                 boost::shared_ptr<Ports> ps = writer.get_copy ();
317                 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
318
319                 if (x != ps->end()) {
320                         ps->erase (x);
321                 }
322
323                 /* writer goes out of scope, forces update */
324         }
325
326         ports.flush ();
327
328         return 0;
329 }
330
331 bool
332 PortManager::connected (const string& port_name)
333 {
334         if (!_impl) {
335                 return false;
336         }
337
338         PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
339
340         if (!handle) {
341                 return false;
342         }
343
344         return _impl->connected (handle);
345 }
346
347 int
348 PortManager::connect (const string& source, const string& destination)
349 {
350         int ret;
351
352         string s = make_port_name_non_relative (source);
353         string d = make_port_name_non_relative (destination);
354
355         boost::shared_ptr<Port> src = get_port_by_name (s);
356         boost::shared_ptr<Port> dst = get_port_by_name (d);
357
358         if (src) {
359                 ret = src->connect (d);
360         } else if (dst) {
361                 ret = dst->connect (s);
362         } else {
363                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
364                 ret = -1;
365         }
366
367         if (ret > 0) {
368                 /* already exists - no error, no warning */
369         } else if (ret < 0) {
370                 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
371                                         source, s, destination, d)
372                       << endmsg;
373         }
374
375         return ret;
376 }
377
378 int
379 PortManager::disconnect (const string& source, const string& destination)
380 {
381         int ret;
382
383         string s = make_port_name_non_relative (source);
384         string d = make_port_name_non_relative (destination);
385
386         boost::shared_ptr<Port> src = get_port_by_name (s);
387         boost::shared_ptr<Port> dst = get_port_by_name (d);
388
389         if (src) {
390                         ret = src->disconnect (d);
391         } else if (dst) {
392                         ret = dst->disconnect (s);
393         } else {
394                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
395                 ret = -1;
396         }
397         return ret;
398 }
399
400 int
401 PortManager::disconnect (boost::shared_ptr<Port> port)
402 {
403         return port->disconnect_all ();
404 }
405
406 int
407 PortManager::reestablish_ports ()
408 {
409         Ports::iterator i;
410
411         boost::shared_ptr<Ports> p = ports.reader ();
412
413         for (i = p->begin(); i != p->end(); ++i) {
414                 if (i->second->reestablish ()) {
415                         break;
416                 }
417         }
418
419         if (i != p->end()) {
420                 /* failed */
421                 remove_all_ports ();
422                 return -1;
423         }
424
425         MIDI::Manager::instance()->reestablish ();
426
427         return 0;
428 }
429
430 int
431 PortManager::reconnect_ports ()
432 {
433         boost::shared_ptr<Ports> p = ports.reader ();
434
435         /* re-establish connections */
436         
437         for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
438                 i->second->reconnect ();
439         }
440
441         MIDI::Manager::instance()->reconnect ();
442
443         return 0;
444 }
445
446 void
447 PortManager::connect_callback (const string& a, const string& b, bool conn)
448 {
449         boost::shared_ptr<Port> port_a;
450         boost::shared_ptr<Port> port_b;
451         Ports::iterator x;
452         boost::shared_ptr<Ports> pr = ports.reader ();
453
454         x = pr->find (make_port_name_relative (a));
455         if (x != pr->end()) {
456                 port_a = x->second;
457         }
458
459         x = pr->find (make_port_name_relative (b));
460         if (x != pr->end()) {
461                 port_b = x->second;
462         }
463
464         PortConnectedOrDisconnected (
465                 port_a, a,
466                 port_b, b,
467                 conn
468                 ); /* EMIT SIGNAL */
469 }       
470
471 void
472 PortManager::registration_callback ()
473 {
474         if (!_port_remove_in_progress) {
475                 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
476         }
477 }
478
479 bool
480 PortManager::can_request_input_monitoring () const
481 {
482         if (!_impl) {
483                 return false;
484         }
485
486         return _impl->can_monitor_input ();
487 }
488  
489 void
490 PortManager::request_input_monitoring (const string& name, bool yn) const
491 {
492         if (!_impl) {
493                 return;
494         }
495
496         PortEngine::PortHandle ph = _impl->get_port_by_name (name);
497
498         if (ph) {
499                 _impl->request_input_monitoring (ph, yn);
500         }
501 }
502  
503 void
504 PortManager::ensure_input_monitoring (const string& name, bool yn) const
505 {
506         if (!_impl) {
507                 return;
508         }
509
510         PortEngine::PortHandle ph = _impl->get_port_by_name (name);
511
512         if (ph) {
513                 _impl->ensure_input_monitoring (ph, yn);
514         }
515 }
516
517 uint32_t
518 PortManager::port_name_size() const
519 {
520         if (!_impl) {
521                 return 0;
522         }
523         
524         return _impl->port_name_size ();
525 }
526
527 string
528 PortManager::my_name() const
529 {
530         if (!_impl) {
531                 return string();
532         }
533         
534         return _impl->my_name();
535 }
536
537 int
538 PortManager::graph_order_callback ()
539 {
540         if (!_port_remove_in_progress) {
541                 GraphReordered(); /* EMIT SIGNAL */
542         }
543
544         return 0;
545 }