full compilation and linking (coding not finished, will not run)
[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         string::size_type len;
71         string::size_type n;
72         string self = _impl->my_name();
73
74         len = portname.length();
75
76         for (n = 0; n < len; ++n) {
77                 if (portname[n] == ':') {
78                         break;
79                 }
80         }
81
82         if ((n != len) && (portname.substr (0, n) == self)) {
83                 return portname.substr (n+1);
84         }
85
86         return portname;
87 }
88
89 string
90 PortManager::make_port_name_non_relative (const string& portname) const
91 {
92         string str;
93
94         if (portname.find_first_of (':') != string::npos) {
95                 return portname;
96         }
97
98         str  = _impl->my_name();
99         str += ':';
100         str += portname;
101
102         return str;
103 }
104
105 bool
106 PortManager::port_is_mine (const string& portname) const
107 {
108         string self = _impl->my_name();
109
110         if (portname.find_first_of (':') != string::npos) {
111                 if (portname.substr (0, self.length ()) != self) {
112                         return false;
113                 }
114         }
115
116         return true;
117 }
118
119 bool
120 PortManager::port_is_physical (const std::string& portname) const
121 {
122         PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
123         if (!ph) {
124                 return false;
125         }
126
127         return _impl->port_is_physical (ph);
128 }
129
130 void
131 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
132 {
133         _impl->get_physical_outputs (type, s);
134 }
135  
136 void
137 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
138 {
139         _impl->get_physical_inputs (type, s);
140 }
141  
142 ChanCount
143 PortManager::n_physical_outputs () const
144 {
145         return _impl->n_physical_outputs ();
146 }
147  
148 ChanCount
149 PortManager::n_physical_inputs () const
150 {
151         return _impl->n_physical_inputs ();
152 }
153
154 /** @param name Full or short name of port
155  *  @return Corresponding Port or 0.
156  */
157
158 boost::shared_ptr<Port>
159 PortManager::get_port_by_name (const string& portname)
160 {
161         if (!port_is_mine (portname)) {
162                 /* not an ardour port */
163                 return boost::shared_ptr<Port> ();
164         }
165
166         boost::shared_ptr<Ports> pr = ports.reader();
167         std::string rel = make_port_name_relative (portname);
168         Ports::iterator x = pr->find (rel);
169
170         if (x != pr->end()) {
171                 /* its possible that the port was renamed by some 3rd party and
172                    we don't know about it. check for this (the check is quick
173                    and cheap), and if so, rename the port (which will alter
174                    the port map as a side effect).
175                 */
176                 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
177                 if (check != rel) {
178                         x->second->set_name (check);
179                 }
180                 return x->second;
181         }
182
183         return boost::shared_ptr<Port> ();
184 }
185
186 void
187 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
188 {
189         RCUWriter<Ports> writer (ports);
190         boost::shared_ptr<Ports> p = writer.get_copy();
191         Ports::iterator x = p->find (old_relative_name);
192         
193         if (x != p->end()) {
194                 boost::shared_ptr<Port> port = x->second;
195                 p->erase (x);
196                 p->insert (make_pair (new_relative_name, port));
197         }
198 }
199
200 int
201 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
202 {
203         return _impl->get_ports (port_name_pattern, type, flags, s);
204 }
205
206 void
207 PortManager::port_registration_failure (const std::string& portname)
208 {
209         string full_portname = _impl->my_name();
210         full_portname += ':';
211         full_portname += portname;
212
213
214         PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
215         string reason;
216
217         if (p) {
218                 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
219         } else {
220                 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);
221         }
222
223         throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
224 }
225
226 boost::shared_ptr<Port>
227 PortManager::register_port (DataType dtype, const string& portname, bool input)
228 {
229         boost::shared_ptr<Port> newport;
230
231         try {
232                 if (dtype == DataType::AUDIO) {
233                         newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
234                 } else if (dtype == DataType::MIDI) {
235                         newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
236                 } else {
237                         throw PortRegistrationFailure("unable to create port (unknown type)");
238                 }
239
240                 RCUWriter<Ports> writer (ports);
241                 boost::shared_ptr<Ports> ps = writer.get_copy ();
242                 ps->insert (make_pair (make_port_name_relative (portname), newport));
243
244                 /* writer goes out of scope, forces update */
245
246                 return newport;
247         }
248
249         catch (PortRegistrationFailure& err) {
250                 throw err;
251         } catch (std::exception& e) {
252                 throw PortRegistrationFailure(string_compose(
253                                 _("unable to create port: %1"), e.what()).c_str());
254         } catch (...) {
255                 throw PortRegistrationFailure("unable to create port (unknown error)");
256         }
257 }
258
259 boost::shared_ptr<Port>
260 PortManager::register_input_port (DataType type, const string& portname)
261 {
262         return register_port (type, portname, true);
263 }
264
265 boost::shared_ptr<Port>
266 PortManager::register_output_port (DataType type, const string& portname)
267 {
268         return register_port (type, portname, false);
269 }
270
271 int
272 PortManager::unregister_port (boost::shared_ptr<Port> port)
273 {
274         /* caller must hold process lock */
275
276         {
277                 RCUWriter<Ports> writer (ports);
278                 boost::shared_ptr<Ports> ps = writer.get_copy ();
279                 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
280
281                 if (x != ps->end()) {
282                         ps->erase (x);
283                 }
284
285                 /* writer goes out of scope, forces update */
286         }
287
288         ports.flush ();
289
290         return 0;
291 }
292
293 bool
294 PortManager::connected (const string& port_name)
295 {
296         PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
297
298         if (!handle) {
299                 return false;
300         }
301
302         return _impl->connected (handle);
303 }
304
305 int
306 PortManager::connect (const string& source, const string& destination)
307 {
308         int ret;
309
310         string s = make_port_name_non_relative (source);
311         string d = make_port_name_non_relative (destination);
312
313         boost::shared_ptr<Port> src = get_port_by_name (s);
314         boost::shared_ptr<Port> dst = get_port_by_name (d);
315
316         if (src) {
317                 ret = src->connect (d);
318         } else if (dst) {
319                 ret = dst->connect (s);
320         } else {
321                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
322                 ret = -1;
323         }
324
325         if (ret > 0) {
326                 /* already exists - no error, no warning */
327         } else if (ret < 0) {
328                 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
329                                         source, s, destination, d)
330                       << endmsg;
331         }
332
333         return ret;
334 }
335
336 int
337 PortManager::disconnect (const string& source, const string& destination)
338 {
339         int ret;
340
341         string s = make_port_name_non_relative (source);
342         string d = make_port_name_non_relative (destination);
343
344         boost::shared_ptr<Port> src = get_port_by_name (s);
345         boost::shared_ptr<Port> dst = get_port_by_name (d);
346
347         if (src) {
348                         ret = src->disconnect (d);
349         } else if (dst) {
350                         ret = dst->disconnect (s);
351         } else {
352                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
353                 ret = -1;
354         }
355         return ret;
356 }
357
358 int
359 PortManager::disconnect (boost::shared_ptr<Port> port)
360 {
361         return port->disconnect_all ();
362 }
363
364 int
365 PortManager::reestablish_ports ()
366 {
367         Ports::iterator i;
368
369         boost::shared_ptr<Ports> p = ports.reader ();
370
371         for (i = p->begin(); i != p->end(); ++i) {
372                 if (i->second->reestablish ()) {
373                         break;
374                 }
375         }
376
377         if (i != p->end()) {
378                 /* failed */
379                 remove_all_ports ();
380                 return -1;
381         }
382
383         MIDI::Manager::instance()->reestablish ();
384
385         return 0;
386 }
387
388 int
389 PortManager::reconnect_ports ()
390 {
391         boost::shared_ptr<Ports> p = ports.reader ();
392
393         /* re-establish connections */
394         
395         for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
396                 i->second->reconnect ();
397         }
398
399         MIDI::Manager::instance()->reconnect ();
400
401         return 0;
402 }
403
404 void
405 PortManager::connect_callback (const string& a, const string& b, bool conn)
406 {
407         boost::shared_ptr<Port> port_a;
408         boost::shared_ptr<Port> port_b;
409         Ports::iterator x;
410         boost::shared_ptr<Ports> pr = ports.reader ();
411
412         x = pr->find (make_port_name_relative (a));
413         if (x != pr->end()) {
414                 port_a = x->second;
415         }
416
417         x = pr->find (make_port_name_relative (b));
418         if (x != pr->end()) {
419                 port_b = x->second;
420         }
421
422         PortConnectedOrDisconnected (
423                 port_a, a,
424                 port_b, b,
425                 conn
426                 ); /* EMIT SIGNAL */
427 }       
428
429 void
430 PortManager::registration_callback ()
431 {
432         if (!_port_remove_in_progress) {
433                 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
434         }
435 }
436
437 bool
438 PortManager::can_request_input_monitoring () const
439 {
440         return _impl->can_monitor_input ();
441 }
442  
443 void
444 PortManager::request_input_monitoring (const string& name, bool yn) const
445 {
446         PortEngine::PortHandle ph = _impl->get_port_by_name (name);
447
448         if (ph) {
449                 _impl->request_input_monitoring (ph, yn);
450         }
451 }
452  
453 void
454 PortManager::ensure_input_monitoring (const string& name, bool yn) const
455 {
456         PortEngine::PortHandle ph = _impl->get_port_by_name (name);
457
458         if (ph) {
459                 _impl->ensure_input_monitoring (ph, yn);
460         }
461 }
462