new file
[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
21 #include "ardour/port_manager.h"
22
23 using namespace ARDOUR;
24
25 PortManager::PortManager ()
26         , ports (new Ports)
27 {
28 }
29
30 void
31 AudioEngine::remove_all_ports ()
32 {
33         /* make sure that JACK callbacks that will be invoked as we cleanup
34          * ports know that they have nothing to do.
35          */
36
37         port_remove_in_progress = true;
38
39         /* process lock MUST be held by caller
40         */
41
42         {
43                 RCUWriter<Ports> writer (ports);
44                 boost::shared_ptr<Ports> ps = writer.get_copy ();
45                 ps->clear ();
46         }
47
48         /* clear dead wood list in RCU */
49
50         ports.flush ();
51
52         port_remove_in_progress = false;
53 }
54
55
56 string
57 AudioEngine::make_port_name_relative (const string& portname) const
58 {
59         string::size_type len;
60         string::size_type n;
61
62         len = portname.length();
63
64         for (n = 0; n < len; ++n) {
65                 if (portname[n] == ':') {
66                         break;
67                 }
68         }
69
70         if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
71                 return portname.substr (n+1);
72         }
73
74         return portname;
75 }
76
77 string
78 AudioEngine::make_port_name_non_relative (const string& portname) const
79 {
80         string str;
81
82         if (portname.find_first_of (':') != string::npos) {
83                 return portname;
84         }
85
86         str  = jack_client_name;
87         str += ':';
88         str += portname;
89
90         return str;
91 }
92
93 bool
94 AudioEngine::port_is_mine (const string& portname) const
95 {
96         if (portname.find_first_of (':') != string::npos) {
97                 if (portname.substr (0, jack_client_name.length ()) != jack_client_name) {
98                         return false;
99                 }
100         }
101         return true;
102 }
103
104 bool
105 AudioEngine::port_is_physical (const std::string& portname) const
106 {
107         GET_PRIVATE_JACK_POINTER_RET(_jack, false);
108
109         jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str());
110
111         if (!port) {
112                 return false;
113         }
114
115         return jack_port_flags (port) & JackPortIsPhysical;
116 }
117
118 ChanCount
119 AudioEngine::n_physical (unsigned long flags) const
120 {
121         ChanCount c;
122
123         GET_PRIVATE_JACK_POINTER_RET (_jack, c);
124
125         const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
126         if (ports == 0) {
127                 return c;
128         }
129
130         for (uint32_t i = 0; ports[i]; ++i) {
131                 if (!strstr (ports[i], "Midi-Through")) {
132                         DataType t (jack_port_type (jack_port_by_name (_jack, ports[i])));
133                         c.set (t, c.get (t) + 1);
134                 }
135         }
136
137         free (ports);
138
139         return c;
140 }
141
142 ChanCount
143 AudioEngine::n_physical_inputs () const
144 {
145         return n_physical (JackPortIsInput);
146 }
147
148 ChanCount
149 AudioEngine::n_physical_outputs () const
150 {
151         return n_physical (JackPortIsOutput);
152 }
153
154 void
155 AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy)
156 {
157         GET_PRIVATE_JACK_POINTER (_jack);
158         const char ** ports;
159
160         if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) {
161                 return;
162         }
163
164         if (ports) {
165                 for (uint32_t i = 0; ports[i]; ++i) {
166                         if (strstr (ports[i], "Midi-Through")) {
167                                 continue;
168                         }
169                         phy.push_back (ports[i]);
170                 }
171                 free (ports);
172         }
173 }
174
175 /** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
176  *  a physical input connector.
177  */
178 void
179 AudioEngine::get_physical_inputs (DataType type, vector<string>& ins)
180 {
181         get_physical (type, JackPortIsOutput, ins);
182 }
183
184 /** Get physical ports for which JackPortIsInput is set; ie those that correspond to
185  *  a physical output connector.
186  */
187 void
188 AudioEngine::get_physical_outputs (DataType type, vector<string>& outs)
189 {
190         get_physical (type, JackPortIsInput, outs);
191 }
192
193
194 bool
195 AudioEngine::can_request_hardware_monitoring ()
196 {
197         GET_PRIVATE_JACK_POINTER_RET (_jack,false);
198         const char ** ports;
199
200         if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
201                 return false;
202         }
203
204         free (ports);
205
206         return true;
207 }
208
209
210 /** @param name Full or short name of port
211  *  @return Corresponding Port or 0.
212  */
213
214 boost::shared_ptr<Port>
215 AudioEngine::get_port_by_name (const string& portname)
216 {
217         if (!_running) {
218                 if (!_has_run) {
219                         fatal << _("get_port_by_name() called before engine was started") << endmsg;
220                         /*NOTREACHED*/
221                 } else {
222                         boost::shared_ptr<Port> ();
223                 }
224         }
225
226         if (!port_is_mine (portname)) {
227                 /* not an ardour port */
228                 return boost::shared_ptr<Port> ();
229         }
230
231         boost::shared_ptr<Ports> pr = ports.reader();
232         std::string rel = make_port_name_relative (portname);
233         Ports::iterator x = pr->find (rel);
234
235         if (x != pr->end()) {
236                 /* its possible that the port was renamed by some 3rd party and
237                    we don't know about it. check for this (the check is quick
238                    and cheap), and if so, rename the port (which will alter
239                    the port map as a side effect).
240                 */
241                 const std::string check = make_port_name_relative (jack_port_name (x->second->jack_port()));
242                 if (check != rel) {
243                         x->second->set_name (check);
244                 }
245                 return x->second;
246         }
247
248         return boost::shared_ptr<Port> ();
249 }
250
251 void
252 AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
253 {
254         RCUWriter<Ports> writer (ports);
255         boost::shared_ptr<Ports> p = writer.get_copy();
256         Ports::iterator x = p->find (old_relative_name);
257         
258         if (x != p->end()) {
259                 boost::shared_ptr<Port> port = x->second;
260                 p->erase (x);
261                 p->insert (make_pair (new_relative_name, port));
262         }
263 }
264
265 const char **
266 AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags)
267 {
268         GET_PRIVATE_JACK_POINTER_RET (_jack,0);
269         if (!_running) {
270                 if (!_has_run) {
271                         fatal << _("get_ports called before engine was started") << endmsg;
272                         /*NOTREACHED*/
273                 } else {
274                         return 0;
275                 }
276         }
277         return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
278 }
279
280 void
281 AudioEngine::port_registration_failure (const std::string& portname)
282 {
283         GET_PRIVATE_JACK_POINTER (_jack);
284         string full_portname = jack_client_name;
285         full_portname += ':';
286         full_portname += portname;
287
288
289         jack_port_t* p = jack_port_by_name (_priv_jack, full_portname.c_str());
290         string reason;
291
292         if (p) {
293                 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
294         } else {
295                 reason = string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with more ports if you need this many tracks."), PROGRAM_NAME);
296         }
297
298         throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
299 }
300
301 boost::shared_ptr<Port>
302 AudioEngine::register_port (DataType dtype, const string& portname, bool input)
303 {
304         boost::shared_ptr<Port> newport;
305
306         try {
307                 if (dtype == DataType::AUDIO) {
308                         newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput)));
309                 } else if (dtype == DataType::MIDI) {
310                         newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput)));
311                 } else {
312                         throw PortRegistrationFailure("unable to create port (unknown type)");
313                 }
314
315                 RCUWriter<Ports> writer (ports);
316                 boost::shared_ptr<Ports> ps = writer.get_copy ();
317                 ps->insert (make_pair (make_port_name_relative (portname), newport));
318
319                 /* writer goes out of scope, forces update */
320
321                 return newport;
322         }
323
324         catch (PortRegistrationFailure& err) {
325                 throw err;
326         } catch (std::exception& e) {
327                 throw PortRegistrationFailure(string_compose(
328                                 _("unable to create port: %1"), e.what()).c_str());
329         } catch (...) {
330                 throw PortRegistrationFailure("unable to create port (unknown error)");
331         }
332 }
333
334 boost::shared_ptr<Port>
335 AudioEngine::register_input_port (DataType type, const string& portname)
336 {
337         return register_port (type, portname, true);
338 }
339
340 boost::shared_ptr<Port>
341 AudioEngine::register_output_port (DataType type, const string& portname)
342 {
343         return register_port (type, portname, false);
344 }
345
346 int
347 AudioEngine::unregister_port (boost::shared_ptr<Port> port)
348 {
349         /* caller must hold process lock */
350
351         if (!_running) {
352                 /* probably happening when the engine has been halted by JACK,
353                    in which case, there is nothing we can do here.
354                    */
355                 return 0;
356         }
357
358         {
359                 RCUWriter<Ports> writer (ports);
360                 boost::shared_ptr<Ports> ps = writer.get_copy ();
361                 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
362
363                 if (x != ps->end()) {
364                         ps->erase (x);
365                 }
366
367                 /* writer goes out of scope, forces update */
368         }
369
370         ports.flush ();
371
372         return 0;
373 }
374
375 bool
376 PortManager::connected (const string& port_name)
377 {
378         PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
379
380         if (!handle) {
381                 return false;
382         }
383
384         return _impl->connected (handle);
385 }
386
387 int
388 AudioEngine::connect (const string& source, const string& destination)
389 {
390         int ret;
391
392         if (!_running) {
393                 if (!_has_run) {
394                         fatal << _("connect called before engine was started") << endmsg;
395                         /*NOTREACHED*/
396                 } else {
397                         return -1;
398                 }
399         }
400
401         string s = make_port_name_non_relative (source);
402         string d = make_port_name_non_relative (destination);
403
404
405         boost::shared_ptr<Port> src = get_port_by_name (s);
406         boost::shared_ptr<Port> dst = get_port_by_name (d);
407
408         if (src) {
409                 ret = src->connect (d);
410         } else if (dst) {
411                 ret = dst->connect (s);
412         } else {
413                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
414                 ret = -1;
415         }
416
417         if (ret > 0) {
418                 /* already exists - no error, no warning */
419         } else if (ret < 0) {
420                 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
421                                         source, s, destination, d)
422                       << endmsg;
423         }
424
425         return ret;
426 }
427
428 int
429 AudioEngine::disconnect (const string& source, const string& destination)
430 {
431         int ret;
432
433         if (!_running) {
434                 if (!_has_run) {
435                         fatal << _("disconnect called before engine was started") << endmsg;
436                         /*NOTREACHED*/
437                 } else {
438                         return -1;
439                 }
440         }
441
442         string s = make_port_name_non_relative (source);
443         string d = make_port_name_non_relative (destination);
444
445         boost::shared_ptr<Port> src = get_port_by_name (s);
446         boost::shared_ptr<Port> dst = get_port_by_name (d);
447
448         if (src) {
449                         ret = src->disconnect (d);
450         } else if (dst) {
451                         ret = dst->disconnect (s);
452         } else {
453                 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
454                 ret = -1;
455         }
456         return ret;
457 }
458
459 int
460 AudioEngine::disconnect (boost::shared_ptr<Port> port)
461 {
462         GET_PRIVATE_JACK_POINTER_RET (_jack,-1);
463
464         if (!_running) {
465                 if (!_has_run) {
466                         fatal << _("disconnect called before engine was started") << endmsg;
467                         /*NOTREACHED*/
468                 } else {
469                         return -1;
470                 }
471         }
472
473         return port->disconnect_all ();
474 }
475