ardour
port_manager.cc
Go to the documentation of this file.
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 "ardour/async_midi_port.h"
23 #include "ardour/audio_backend.h"
24 #include "ardour/audio_port.h"
25 #include "ardour/debug.h"
26 #include "ardour/midi_port.h"
28 #include "ardour/port_manager.h"
29 
30 #include "i18n.h"
31 
32 using namespace ARDOUR;
33 using namespace PBD;
34 using std::string;
35 using std::vector;
36 
38  : ports (new Ports)
39  , _port_remove_in_progress (false)
40 {
41 }
42 
43 void
45 {
46  /* make sure that JACK callbacks that will be invoked as we cleanup
47  * ports know that they have nothing to do.
48  */
49 
51 
52  /* process lock MUST be held by caller
53  */
54 
55  {
56  RCUWriter<Ports> writer (ports);
57  boost::shared_ptr<Ports> ps = writer.get_copy ();
58  ps->clear ();
59  }
60 
61  /* clear dead wood list in RCU */
62 
63  ports.flush ();
64 
66 }
67 
68 
69 string
70 PortManager::make_port_name_relative (const string& portname) const
71 {
72  if (!_backend) {
73  return portname;
74  }
75 
76  string::size_type len;
77  string::size_type n;
78  string self = _backend->my_name();
79 
80  len = portname.length();
81 
82  for (n = 0; n < len; ++n) {
83  if (portname[n] == ':') {
84  break;
85  }
86  }
87 
88  if ((n != len) && (portname.substr (0, n) == self)) {
89  return portname.substr (n+1);
90  }
91 
92  return portname;
93 }
94 
95 string
96 PortManager::make_port_name_non_relative (const string& portname) const
97 {
98  string str;
99 
100  if (portname.find_first_of (':') != string::npos) {
101  return portname;
102  }
103 
104  str = _backend->my_name();
105  str += ':';
106  str += portname;
107 
108  return str;
109 }
110 
111 std::string
112 PortManager::get_pretty_name_by_name(const std::string& portname) const
113 {
114  PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
115  if (ph) {
116  std::string value;
117  std::string type;
118  if (0 == _backend->get_port_property (ph,
119  "http://jackaudio.org/metadata/pretty-name",
120  value, type))
121  {
122  return value;
123  }
124  }
125  return "";
126 }
127 
128 bool
129 PortManager::port_is_mine (const string& portname) const
130 {
131  if (!_backend) {
132  return true;
133  }
134 
135  string self = _backend->my_name();
136 
137  if (portname.find_first_of (':') != string::npos) {
138  if (portname.substr (0, self.length ()) != self) {
139  return false;
140  }
141  }
142 
143  return true;
144 }
145 
146 bool
147 PortManager::port_is_physical (const std::string& portname) const
148 {
149  if (!_backend) {
150  return false;
151  }
152 
153  PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
154  if (!ph) {
155  return false;
156  }
157 
158  return _backend->port_is_physical (ph);
159 }
160 
161 void
162 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
163 {
164  if (!_backend) {
165  return;
166  }
167  _backend->get_physical_outputs (type, s);
168 }
169 
170 void
171 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
172 {
173  if (!_backend) {
174  return;
175  }
176 
177  _backend->get_physical_inputs (type, s);
178 }
179 
180 ChanCount
182 {
183  if (!_backend) {
184  return ChanCount::ZERO;
185  }
186 
187  return _backend->n_physical_outputs ();
188 }
189 
190 ChanCount
192 {
193  if (!_backend) {
194  return ChanCount::ZERO;
195  }
196  return _backend->n_physical_inputs ();
197 }
198 
204 PortManager::get_port_by_name (const string& portname)
205 {
206  if (!_backend) {
207  return boost::shared_ptr<Port>();
208  }
209 
210  if (!port_is_mine (portname)) {
211  /* not an ardour port */
212  return boost::shared_ptr<Port> ();
213  }
214 
216  std::string rel = make_port_name_relative (portname);
217  Ports::iterator x = pr->find (rel);
218 
219  if (x != pr->end()) {
220  /* its possible that the port was renamed by some 3rd party and
221  we don't know about it. check for this (the check is quick
222  and cheap), and if so, rename the port (which will alter
223  the port map as a side effect).
224  */
225  const std::string check = make_port_name_relative (_backend->get_port_name (x->second->port_handle()));
226  if (check != rel) {
227  x->second->set_name (check);
228  }
229  return x->second;
230  }
231 
232  return boost::shared_ptr<Port> ();
233 }
234 
235 void
236 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
237 {
238  RCUWriter<Ports> writer (ports);
239  boost::shared_ptr<Ports> p = writer.get_copy();
240  Ports::iterator x = p->find (old_relative_name);
241 
242  if (x != p->end()) {
243  boost::shared_ptr<Port> port = x->second;
244  p->erase (x);
245  p->insert (make_pair (new_relative_name, port));
246  }
247 }
248 
249 int
251 {
253  for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
254  if (p->second->type() == type) {
255  pl.push_back (p->second);
256  }
257  }
258  return pl.size();
259 }
260 
261 int
262 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
263 {
264  s.clear();
265 
266  if (!_backend) {
267  return 0;
268  }
269 
270  return _backend->get_ports (port_name_pattern, type, flags, s);
271 }
272 
273 void
274 PortManager::port_registration_failure (const std::string& portname)
275 {
276  if (!_backend) {
277  return;
278  }
279 
280  string full_portname = _backend->my_name();
281  full_portname += ':';
282  full_portname += portname;
283 
284 
285  PortEngine::PortHandle p = _backend->get_port_by_name (full_portname);
286  string reason;
287 
288  if (p) {
289  reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
290  } else {
291  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);
292  }
293 
294  throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
295 }
296 
298 PortManager::register_port (DataType dtype, const string& portname, bool input, bool async)
299 {
300  boost::shared_ptr<Port> newport;
301 
302  try {
303  if (dtype == DataType::AUDIO) {
304  DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
305  portname, input));
306  newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
307  } else if (dtype == DataType::MIDI) {
308  if (async) {
309  DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
310  portname, input));
311  newport.reset (new AsyncMIDIPort (portname, (input ? IsInput : IsOutput)));
312  } else {
313  DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
314  portname, input));
315  newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
316  }
317  } else {
318  throw PortRegistrationFailure("unable to create port (unknown type)");
319  }
320 
321  RCUWriter<Ports> writer (ports);
322  boost::shared_ptr<Ports> ps = writer.get_copy ();
323  ps->insert (make_pair (make_port_name_relative (portname), newport));
324 
325  /* writer goes out of scope, forces update */
326 
327  }
328 
329  catch (PortRegistrationFailure& err) {
330  throw err;
331  } catch (std::exception& e) {
333  _("unable to create port: %1"), e.what()).c_str());
334  } catch (...) {
335  throw PortRegistrationFailure("unable to create port (unknown error)");
336  }
337 
338  DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
339  return newport;
340 }
341 
343 PortManager::register_input_port (DataType type, const string& portname, bool async)
344 {
345  return register_port (type, portname, true, async);
346 }
347 
349 PortManager::register_output_port (DataType type, const string& portname, bool async)
350 {
351  return register_port (type, portname, false, async);
352 }
353 
354 int
356 {
357  /* caller must hold process lock */
358 
359  {
360  RCUWriter<Ports> writer (ports);
361  boost::shared_ptr<Ports> ps = writer.get_copy ();
362  Ports::iterator x = ps->find (make_port_name_relative (port->name()));
363 
364  if (x != ps->end()) {
365  ps->erase (x);
366  }
367 
368  /* writer goes out of scope, forces update */
369  }
370 
371  ports.flush ();
372 
373  return 0;
374 }
375 
376 bool
377 PortManager::connected (const string& port_name)
378 {
379  if (!_backend) {
380  return false;
381  }
382 
383  PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
384 
385  if (!handle) {
386  return false;
387  }
388 
389  return _backend->connected (handle);
390 }
391 
392 int
393 PortManager::connect (const string& source, const string& destination)
394 {
395  int ret;
396 
397  string s = make_port_name_non_relative (source);
398  string d = make_port_name_non_relative (destination);
399 
402 
403  if (src) {
404  ret = src->connect (d);
405  } else if (dst) {
406  ret = dst->connect (s);
407  } else {
408  /* neither port is known to us ...hand-off to the PortEngine
409  */
410  if (_backend) {
411  ret = _backend->connect (s, d);
412  } else {
413  ret = -1;
414  }
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 PortManager::disconnect (const string& source, const string& destination)
430 {
431  int ret;
432 
433  string s = make_port_name_non_relative (source);
434  string d = make_port_name_non_relative (destination);
435 
438 
439  if (src) {
440  ret = src->disconnect (d);
441  } else if (dst) {
442  ret = dst->disconnect (s);
443  } else {
444  /* neither port is known to us ...hand-off to the PortEngine
445  */
446  if (_backend) {
447  ret = _backend->disconnect (s, d);
448  } else {
449  ret = -1;
450  }
451  }
452  return ret;
453 }
454 
455 int
457 {
458  return port->disconnect_all ();
459 }
460 
461 int
463 {
464  Ports::iterator i;
465 
467 
468  DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
469 
470  for (i = p->begin(); i != p->end(); ++i) {
471  if (i->second->reestablish ()) {
472  error << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endmsg;
473  std::cerr << string_compose (_("Re-establising port %1 failed"), i->second->name()) << std::endl;
474  break;
475  }
476  }
477 
478  if (i != p->end()) {
479  /* failed */
480  remove_all_ports ();
481  return -1;
482  }
483 
484  return 0;
485 }
486 
487 int
489 {
491 
492  /* re-establish connections */
493 
494  DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
495 
496  for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
497  i->second->reconnect ();
498  }
499 
500  return 0;
501 }
502 
503 void
504 PortManager::connect_callback (const string& a, const string& b, bool conn)
505 {
508  Ports::iterator x;
510 
511  x = pr->find (make_port_name_relative (a));
512  if (x != pr->end()) {
513  port_a = x->second;
514  }
515 
516  x = pr->find (make_port_name_relative (b));
517  if (x != pr->end()) {
518  port_b = x->second;
519  }
520 
522  port_a, a,
523  port_b, b,
524  conn
525  ); /* EMIT SIGNAL */
526 }
527 
528 void
530 {
532  PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
533  }
534 }
535 
536 bool
538 {
539  if (!_backend) {
540  return false;
541  }
542 
543  return _backend->can_monitor_input ();
544 }
545 
546 void
547 PortManager::request_input_monitoring (const string& name, bool yn) const
548 {
549  if (!_backend) {
550  return;
551  }
552 
553  PortEngine::PortHandle ph = _backend->get_port_by_name (name);
554 
555  if (ph) {
556  _backend->request_input_monitoring (ph, yn);
557  }
558 }
559 
560 void
561 PortManager::ensure_input_monitoring (const string& name, bool yn) const
562 {
563  if (!_backend) {
564  return;
565  }
566 
567  PortEngine::PortHandle ph = _backend->get_port_by_name (name);
568 
569  if (ph) {
570  _backend->ensure_input_monitoring (ph, yn);
571  }
572 }
573 
574 uint32_t
576 {
577  if (!_backend) {
578  return 0;
579  }
580 
581  return _backend->port_name_size ();
582 }
583 
584 string
586 {
587  if (!_backend) {
588  return string();
589  }
590 
591  return _backend->my_name();
592 }
593 
594 int
596 {
598  GraphReordered(); /* EMIT SIGNAL */
599  }
600 
601  return 0;
602 }
603 
604 void
606 {
608  Port::set_cycle_framecnt (nframes);
609 
611 
612  for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
613  p->second->cycle_start (nframes);
614  }
615 }
616 
617 void
619 {
620  for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
621  p->second->cycle_end (nframes);
622  }
623 
624  for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
625  p->second->flush_buffers (nframes);
626  }
627 
628  _cycle_ports.reset ();
629 
630  /* we are done */
631 }
632 
633 void
635 {
636  for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
637  if (i->second->sends_output()) {
638  i->second->get_buffer(nframes).silence(nframes);
639  }
640  }
641 }
642 
643 void
645 {
646  std::vector<std::string> port_names;
647  if (get_ports("", DataType::AUDIO, IsOutput, port_names)) {
648  for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
649  if (!port_is_mine(*p)) {
650  continue;
651  }
652  PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
653  if (!ph) {
654  continue;
655  }
656  void *buf = _backend->get_buffer(ph, nframes);
657  if (!buf) {
658  continue;
659  }
660  memset (buf, 0, sizeof(float) * nframes);
661  }
662  }
663 
664  if (get_ports("", DataType::MIDI, IsOutput, port_names)) {
665  for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
666  if (!port_is_mine(*p)) {
667  continue;
668  }
669  PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
670  if (!ph) {
671  continue;
672  }
673  void *buf = _backend->get_buffer(ph, nframes);
674  if (!buf) {
675  continue;
676  }
677  _backend->midi_clear (buf);
678  }
679  }
680 }
681 
682 void
684 {
685  for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
686 
687  bool x;
688 
689  if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
690  i->second->set_last_monitor (x);
691  /* XXX I think this is dangerous, due to
692  a likely mutex in the signal handlers ...
693  */
694  i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
695  }
696  }
697 }
698 
699 void
700 PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
701 {
702  for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
703 
704  if (i->second->sends_output()) {
705 
707  if (ap) {
709  gain_t g = base_gain;
710 
711  for (pframes_t n = 0; n < nframes; ++n) {
712  *s++ *= g;
713  g -= gain_step;
714  }
715  }
716  }
717  }
718 }
719 
720 PortEngine&
722 {
723  assert (_backend);
724  return *_backend;
725 }
void silence_outputs(pframes_t nframes)
SerializedRCUManager< Ports > ports
Definition: port_manager.h:140
uint32_t port_name_size() const
std::map< std::string, boost::shared_ptr< Port > > Ports
Definition: port_manager.h:46
boost::shared_ptr< Port > register_input_port(DataType, const std::string &portname, bool async=false)
std::string my_name() const
void flush()
Definition: rcu.h:191
int connect(std::string const &)
Definition: port.cc:172
LIBARDOUR_API uint64_t Ports
Definition: debug.cc:64
void port_registration_failure(const std::string &portname)
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
void cycle_end(pframes_t nframes)
boost::shared_ptr< Ports > _cycle_ports
Definition: port_manager.h:148
void connect_callback(const std::string &, const std::string &, bool connection)
bool port_is_physical(const std::string &) const
uint32_t pframes_t
Definition: types.h:61
LIBPBD_API Transmitter error
ChanCount n_physical_outputs() const
void port_renamed(const std::string &, const std::string &)
float gain_t
Definition: types.h:58
std::string name() const
Definition: port.h:54
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
void request_input_monitoring(const std::string &, bool) const
bool connected(const std::string &)
ChanCount n_physical_inputs() const
PortEngine & port_engine()
PBD::Signal0< void > GraphReordered
Definition: port_manager.h:126
void get_physical_outputs(DataType type, std::vector< std::string > &)
#define _(Text)
Definition: i18n.h:11
std::list< boost::shared_ptr< Port > > PortList
Definition: port_manager.h:47
PBD::Signal5< void, boost::weak_ptr< Port >, std::string, boost::weak_ptr< Port >, std::string, bool > PortConnectedOrDisconnected
Definition: port_manager.h:136
int disconnect(const std::string &source, const std::string &destination)
bool can_request_input_monitoring() const
float Sample
Definition: types.h:54
std::string make_port_name_relative(const std::string &name) const
Definition: port_manager.cc:70
Sample * engine_get_whole_audio_buffer()
Definition: audio_port.cc:83
void fade_out(gain_t, gain_t, pframes_t)
Definition: amp.h:29
PortFlags
Definition: types.h:610
boost::shared_ptr< T > get_copy() const
Definition: rcu.h:250
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int connect(const std::string &source, const std::string &destination)
boost::shared_ptr< Port > register_port(DataType type, const std::string &portname, bool input, bool async=false)
boost::shared_ptr< AudioBackend > _backend
Definition: port_manager.h:139
void silence(pframes_t nframes)
const char * name
boost::shared_ptr< Port > get_port_by_name(const std::string &)
Definition: debug.h:30
PBD::Signal0< void > PortRegisteredOrUnregistered
Definition: port_manager.h:129
int disconnect_all()
Definition: port.cc:127
void ensure_input_monitoring(const std::string &, bool) const
std::string get_pretty_name_by_name(const std::string &portname) const
boost::shared_ptr< Port > register_output_port(DataType, const std::string &portname, bool async=false)
void get_physical_inputs(DataType type, std::vector< std::string > &)
Definition: rcu.h:217
boost::shared_ptr< T > reader() const
Definition: rcu.h:58
static void set_global_port_buffer_offset(pframes_t off)
Definition: port.h:132
int unregister_port(boost::shared_ptr< Port >)
void cycle_start(pframes_t nframes)
std::string make_port_name_non_relative(const std::string &name) const
Definition: port_manager.cc:96
static const ChanCount ZERO
Definition: chan_count.h:149
int disconnect(std::string const &)
Definition: port.cc:199
bool port_is_mine(const std::string &fullname) const
int get_ports(const std::string &port_name_pattern, DataType type, PortFlags flags, std::vector< std::string > &)
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
static void set_cycle_framecnt(pframes_t n)
Definition: port.h:128