more stuff compiles
[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 /** @param name Full or short name of port
131  *  @return Corresponding Port or 0.
132  */
133
134 boost::shared_ptr<Port>
135 PortManager::get_port_by_name (const string& portname)
136 {
137         if (!_impl->connected()) {
138                 fatal << _("get_port_by_name() called before engine was started") << endmsg;
139                 /*NOTREACHED*/
140         }
141
142         if (!port_is_mine (portname)) {
143                 /* not an ardour port */
144                 return boost::shared_ptr<Port> ();
145         }
146
147         boost::shared_ptr<Ports> pr = ports.reader();
148         std::string rel = make_port_name_relative (portname);
149         Ports::iterator x = pr->find (rel);
150
151         if (x != pr->end()) {
152                 /* its possible that the port was renamed by some 3rd party and
153                    we don't know about it. check for this (the check is quick
154                    and cheap), and if so, rename the port (which will alter
155                    the port map as a side effect).
156                 */
157                 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
158                 if (check != rel) {
159                         x->second->set_name (check);
160                 }
161                 return x->second;
162         }
163
164         return boost::shared_ptr<Port> ();
165 }
166
167 void
168 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
169 {
170         RCUWriter<Ports> writer (ports);
171         boost::shared_ptr<Ports> p = writer.get_copy();
172         Ports::iterator x = p->find (old_relative_name);
173         
174         if (x != p->end()) {
175                 boost::shared_ptr<Port> port = x->second;
176                 p->erase (x);
177                 p->insert (make_pair (new_relative_name, port));
178         }
179 }
180
181 int
182 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
183 {
184         return _impl->get_ports (port_name_pattern, type, flags, s);
185 }
186
187 void
188 PortManager::port_registration_failure (const std::string& portname)
189 {
190         string full_portname = _impl->my_name();
191         full_portname += ':';
192         full_portname += portname;
193
194
195         PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
196         string reason;
197
198         if (p) {
199                 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
200         } else {
201                 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);
202         }
203
204         throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
205 }
206
207 boost::shared_ptr<Port>
208 PortManager::register_port (DataType dtype, const string& portname, bool input)
209 {
210         boost::shared_ptr<Port> newport;
211
212         try {
213                 if (dtype == DataType::AUDIO) {
214                         newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
215                 } else if (dtype == DataType::MIDI) {
216                         newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
217                 } else {
218                         throw PortRegistrationFailure("unable to create port (unknown type)");
219                 }
220
221                 RCUWriter<Ports> writer (ports);
222                 boost::shared_ptr<Ports> ps = writer.get_copy ();
223                 ps->insert (make_pair (make_port_name_relative (portname), newport));
224
225                 /* writer goes out of scope, forces update */
226
227                 return newport;
228         }
229
230         catch (PortRegistrationFailure& err) {
231                 throw err;
232         } catch (std::exception& e) {
233                 throw PortRegistrationFailure(string_compose(
234                                 _("unable to create port: %1"), e.what()).c_str());
235         } catch (...) {
236                 throw PortRegistrationFailure("unable to create port (unknown error)");
237         }
238 }
239
240 boost::shared_ptr<Port>
241 PortManager::register_input_port (DataType type, const string& portname)
242 {
243         return register_port (type, portname, true);
244 }
245
246 boost::shared_ptr<Port>
247 PortManager::register_output_port (DataType type, const string& portname)
248 {
249         return register_port (type, portname, false);
250 }
251
252 int
253 PortManager::unregister_port (boost::shared_ptr<Port> port)
254 {
255         /* caller must hold process lock */
256
257         if (!_impl->connected()) {
258                 /* probably happening when the engine has been halted by JACK,
259                    in which case, there is nothing we can do here.
260                    */
261                 return 0;
262         }
263
264         {
265                 RCUWriter<Ports> writer (ports);
266                 boost::shared_ptr<Ports> ps = writer.get_copy ();
267                 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
268
269                 if (x != ps->end()) {
270                         ps->erase (x);
271                 }
272
273                 /* writer goes out of scope, forces update */
274         }
275
276         ports.flush ();
277
278         return 0;
279 }
280
281 bool
282 PortManager::connected (const string& port_name)
283 {
284         PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
285
286         if (!handle) {
287                 return false;
288         }
289
290         return _impl->connected (handle);
291 }
292
293 int
294 PortManager::connect (const string& source, const string& destination)
295 {
296         int ret;
297
298         if (!_impl->connected()) {
299                 return -1;
300         }
301
302         string s = make_port_name_non_relative (source);
303         string d = make_port_name_non_relative (destination);
304
305         boost::shared_ptr<Port> src = get_port_by_name (s);
306         boost::shared_ptr<Port> dst = get_port_by_name (d);
307
308         if (src) {
309                 ret = src->connect (d);
310         } else if (dst) {
311                 ret = dst->connect (s);
312         } else {
313                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
314                 ret = -1;
315         }
316
317         if (ret > 0) {
318                 /* already exists - no error, no warning */
319         } else if (ret < 0) {
320                 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
321                                         source, s, destination, d)
322                       << endmsg;
323         }
324
325         return ret;
326 }
327
328 int
329 PortManager::disconnect (const string& source, const string& destination)
330 {
331         int ret;
332
333         if (!_impl->connected()) {
334                 return -1;
335         }
336
337         string s = make_port_name_non_relative (source);
338         string d = make_port_name_non_relative (destination);
339
340         boost::shared_ptr<Port> src = get_port_by_name (s);
341         boost::shared_ptr<Port> dst = get_port_by_name (d);
342
343         if (src) {
344                         ret = src->disconnect (d);
345         } else if (dst) {
346                         ret = dst->disconnect (s);
347         } else {
348                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
349                 ret = -1;
350         }
351         return ret;
352 }
353
354 int
355 PortManager::disconnect (boost::shared_ptr<Port> port)
356 {
357         return port->disconnect_all ();
358 }
359
360 int
361 PortManager::reestablish_ports ()
362 {
363         Ports::iterator i;
364
365         boost::shared_ptr<Ports> p = ports.reader ();
366
367         for (i = p->begin(); i != p->end(); ++i) {
368                 if (i->second->reestablish ()) {
369                         break;
370                 }
371         }
372
373         if (i != p->end()) {
374                 /* failed */
375                 remove_all_ports ();
376                 return -1;
377         }
378
379         MIDI::Manager::instance()->reestablish ();
380
381         return 0;
382 }
383
384 int
385 PortManager::reconnect_ports ()
386 {
387         boost::shared_ptr<Ports> p = ports.reader ();
388
389         /* re-establish connections */
390         
391         for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
392                 i->second->reconnect ();
393         }
394
395         MIDI::Manager::instance()->reconnect ();
396
397         return 0;
398 }
399
400 void
401 PortManager::connect_callback (const string& a, const string& b, bool conn)
402 {
403         boost::shared_ptr<Port> port_a;
404         boost::shared_ptr<Port> port_b;
405         Ports::iterator x;
406         boost::shared_ptr<Ports> pr = ports.reader ();
407
408         x = pr->find (make_port_name_relative (a));
409         if (x != pr->end()) {
410                 port_a = x->second;
411         }
412
413         x = pr->find (make_port_name_relative (b));
414         if (x != pr->end()) {
415                 port_b = x->second;
416         }
417
418         PortConnectedOrDisconnected (
419                 port_a, a,
420                 port_b, b,
421                 conn
422                 ); /* EMIT SIGNAL */
423 }       
424
425 void
426 PortManager::registration_callback ()
427 {
428         if (!_port_remove_in_progress) {
429                 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
430         }
431 }