2 Copyright (C) 2013 Paul Davis
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.
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.
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.
20 #include "pbd/error.h"
22 #include "ardour/async_midi_port.h"
23 #include "ardour/debug.h"
24 #include "ardour/port_manager.h"
25 #include "ardour/audio_port.h"
26 #include "ardour/midi_port.h"
27 #include "ardour/midiport_manager.h"
31 using namespace ARDOUR;
36 PortManager::PortManager ()
38 , _port_remove_in_progress (false)
43 PortManager::remove_all_ports ()
45 /* make sure that JACK callbacks that will be invoked as we cleanup
46 * ports know that they have nothing to do.
49 _port_remove_in_progress = true;
51 /* process lock MUST be held by caller
55 RCUWriter<Ports> writer (ports);
56 boost::shared_ptr<Ports> ps = writer.get_copy ();
60 /* clear dead wood list in RCU */
64 _port_remove_in_progress = false;
69 PortManager::make_port_name_relative (const string& portname) const
75 string::size_type len;
77 string self = _impl->my_name();
79 len = portname.length();
81 for (n = 0; n < len; ++n) {
82 if (portname[n] == ':') {
87 if ((n != len) && (portname.substr (0, n) == self)) {
88 return portname.substr (n+1);
95 PortManager::make_port_name_non_relative (const string& portname) const
99 if (portname.find_first_of (':') != string::npos) {
103 str = _impl->my_name();
111 PortManager::port_is_mine (const string& portname) const
117 string self = _impl->my_name();
119 if (portname.find_first_of (':') != string::npos) {
120 if (portname.substr (0, self.length ()) != self) {
129 PortManager::port_is_physical (const std::string& portname) const
135 PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
140 return _impl->port_is_physical (ph);
144 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
149 _impl->get_physical_outputs (type, s);
153 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
159 _impl->get_physical_inputs (type, s);
163 PortManager::n_physical_outputs () const
166 return ChanCount::ZERO;
169 return _impl->n_physical_outputs ();
173 PortManager::n_physical_inputs () const
176 return ChanCount::ZERO;
178 return _impl->n_physical_inputs ();
181 /** @param name Full or short name of port
182 * @return Corresponding Port or 0.
185 boost::shared_ptr<Port>
186 PortManager::get_port_by_name (const string& portname)
189 return boost::shared_ptr<Port>();
192 if (!port_is_mine (portname)) {
193 /* not an ardour port */
194 return boost::shared_ptr<Port> ();
197 boost::shared_ptr<Ports> pr = ports.reader();
198 std::string rel = make_port_name_relative (portname);
199 Ports::iterator x = pr->find (rel);
201 if (x != pr->end()) {
202 /* its possible that the port was renamed by some 3rd party and
203 we don't know about it. check for this (the check is quick
204 and cheap), and if so, rename the port (which will alter
205 the port map as a side effect).
207 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
209 x->second->set_name (check);
214 return boost::shared_ptr<Port> ();
218 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
220 RCUWriter<Ports> writer (ports);
221 boost::shared_ptr<Ports> p = writer.get_copy();
222 Ports::iterator x = p->find (old_relative_name);
225 boost::shared_ptr<Port> port = x->second;
227 p->insert (make_pair (new_relative_name, port));
232 PortManager::get_ports (DataType type, PortList& pl)
234 boost::shared_ptr<Ports> plist = ports.reader();
235 for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
236 if (p->second->type() == type) {
237 pl.push_back (p->second);
244 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
250 return _impl->get_ports (port_name_pattern, type, flags, s);
254 PortManager::port_registration_failure (const std::string& portname)
260 string full_portname = _impl->my_name();
261 full_portname += ':';
262 full_portname += portname;
265 PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
269 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
271 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);
274 throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
277 boost::shared_ptr<Port>
278 PortManager::register_port (DataType dtype, const string& portname, bool input, bool async)
280 boost::shared_ptr<Port> newport;
283 if (dtype == DataType::AUDIO) {
284 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
286 newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
287 } else if (dtype == DataType::MIDI) {
289 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
291 newport.reset (new AsyncMIDIPort (portname, (input ? IsInput : IsOutput)));
293 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
295 newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
298 throw PortRegistrationFailure("unable to create port (unknown type)");
301 RCUWriter<Ports> writer (ports);
302 boost::shared_ptr<Ports> ps = writer.get_copy ();
303 ps->insert (make_pair (make_port_name_relative (portname), newport));
305 /* writer goes out of scope, forces update */
309 catch (PortRegistrationFailure& err) {
311 } catch (std::exception& e) {
312 throw PortRegistrationFailure(string_compose(
313 _("unable to create port: %1"), e.what()).c_str());
315 throw PortRegistrationFailure("unable to create port (unknown error)");
318 DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
322 boost::shared_ptr<Port>
323 PortManager::register_input_port (DataType type, const string& portname, bool async)
325 return register_port (type, portname, true, async);
328 boost::shared_ptr<Port>
329 PortManager::register_output_port (DataType type, const string& portname, bool async)
331 return register_port (type, portname, false, async);
335 PortManager::unregister_port (boost::shared_ptr<Port> port)
337 /* caller must hold process lock */
340 RCUWriter<Ports> writer (ports);
341 boost::shared_ptr<Ports> ps = writer.get_copy ();
342 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
344 if (x != ps->end()) {
348 /* writer goes out of scope, forces update */
357 PortManager::connected (const string& port_name)
363 PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
369 return _impl->connected (handle);
373 PortManager::connect (const string& source, const string& destination)
377 string s = make_port_name_non_relative (source);
378 string d = make_port_name_non_relative (destination);
380 boost::shared_ptr<Port> src = get_port_by_name (s);
381 boost::shared_ptr<Port> dst = get_port_by_name (d);
384 ret = src->connect (d);
386 ret = dst->connect (s);
388 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
393 /* already exists - no error, no warning */
394 } else if (ret < 0) {
395 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
396 source, s, destination, d)
404 PortManager::disconnect (const string& source, const string& destination)
408 string s = make_port_name_non_relative (source);
409 string d = make_port_name_non_relative (destination);
411 boost::shared_ptr<Port> src = get_port_by_name (s);
412 boost::shared_ptr<Port> dst = get_port_by_name (d);
415 ret = src->disconnect (d);
417 ret = dst->disconnect (s);
419 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
426 PortManager::disconnect (boost::shared_ptr<Port> port)
428 return port->disconnect_all ();
432 PortManager::reestablish_ports ()
436 boost::shared_ptr<Ports> p = ports.reader ();
438 DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
440 for (i = p->begin(); i != p->end(); ++i) {
441 if (i->second->reestablish ()) {
456 PortManager::reconnect_ports ()
458 boost::shared_ptr<Ports> p = ports.reader ();
460 /* re-establish connections */
462 DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
464 for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
465 i->second->reconnect ();
472 PortManager::connect_callback (const string& a, const string& b, bool conn)
474 boost::shared_ptr<Port> port_a;
475 boost::shared_ptr<Port> port_b;
477 boost::shared_ptr<Ports> pr = ports.reader ();
479 x = pr->find (make_port_name_relative (a));
480 if (x != pr->end()) {
484 x = pr->find (make_port_name_relative (b));
485 if (x != pr->end()) {
489 PortConnectedOrDisconnected (
497 PortManager::registration_callback ()
499 if (!_port_remove_in_progress) {
500 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
505 PortManager::can_request_input_monitoring () const
511 return _impl->can_monitor_input ();
515 PortManager::request_input_monitoring (const string& name, bool yn) const
521 PortEngine::PortHandle ph = _impl->get_port_by_name (name);
524 _impl->request_input_monitoring (ph, yn);
529 PortManager::ensure_input_monitoring (const string& name, bool yn) const
535 PortEngine::PortHandle ph = _impl->get_port_by_name (name);
538 _impl->ensure_input_monitoring (ph, yn);
543 PortManager::port_name_size() const
549 return _impl->port_name_size ();
553 PortManager::my_name() const
559 return _impl->my_name();
563 PortManager::graph_order_callback ()
565 if (!_port_remove_in_progress) {
566 GraphReordered(); /* EMIT SIGNAL */
573 PortManager::cycle_start (pframes_t nframes)
575 Port::set_global_port_buffer_offset (0);
576 Port::set_cycle_framecnt (nframes);
578 _cycle_ports = ports.reader ();
580 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
581 p->second->cycle_start (nframes);
586 PortManager::cycle_end (pframes_t nframes)
588 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
589 p->second->cycle_end (nframes);
592 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
593 p->second->flush_buffers (nframes);
596 _cycle_ports.reset ();
602 PortManager::silence (pframes_t nframes)
604 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
605 if (i->second->sends_output()) {
606 i->second->get_buffer(nframes).silence(nframes);
612 PortManager::check_monitoring ()
614 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
618 if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
619 i->second->set_last_monitor (x);
620 /* XXX I think this is dangerous, due to
621 a likely mutex in the signal handlers ...
623 i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
629 PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
631 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
633 if (i->second->sends_output()) {
635 boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
637 Sample* s = ap->engine_get_whole_audio_buffer ();
638 gain_t g = base_gain;
640 for (pframes_t n = 0; n < nframes; ++n) {