diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2013-07-24 19:30:08 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2013-07-24 19:30:08 -0400 |
commit | 2af6491c1c779cda31b9a9bd8eee2fb47015e91f (patch) | |
tree | bc043d5b3adde246686eebc07efbd5b24a758a43 /libs/ardour/port_manager.cc | |
parent | 9ac6bb9befa047a6c349bed02d40da84600b67cc (diff) |
new file
Diffstat (limited to 'libs/ardour/port_manager.cc')
-rw-r--r-- | libs/ardour/port_manager.cc | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/libs/ardour/port_manager.cc b/libs/ardour/port_manager.cc new file mode 100644 index 0000000000..c279cf6d27 --- /dev/null +++ b/libs/ardour/port_manager.cc @@ -0,0 +1,475 @@ +/* + Copyright (C) 2013 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#include "ardour/port_manager.h" + +using namespace ARDOUR; + +PortManager::PortManager () + , ports (new Ports) +{ +} + +void +AudioEngine::remove_all_ports () +{ + /* make sure that JACK callbacks that will be invoked as we cleanup + * ports know that they have nothing to do. + */ + + port_remove_in_progress = true; + + /* process lock MUST be held by caller + */ + + { + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + ps->clear (); + } + + /* clear dead wood list in RCU */ + + ports.flush (); + + port_remove_in_progress = false; +} + + +string +AudioEngine::make_port_name_relative (const string& portname) const +{ + string::size_type len; + string::size_type n; + + len = portname.length(); + + for (n = 0; n < len; ++n) { + if (portname[n] == ':') { + break; + } + } + + if ((n != len) && (portname.substr (0, n) == jack_client_name)) { + return portname.substr (n+1); + } + + return portname; +} + +string +AudioEngine::make_port_name_non_relative (const string& portname) const +{ + string str; + + if (portname.find_first_of (':') != string::npos) { + return portname; + } + + str = jack_client_name; + str += ':'; + str += portname; + + return str; +} + +bool +AudioEngine::port_is_mine (const string& portname) const +{ + if (portname.find_first_of (':') != string::npos) { + if (portname.substr (0, jack_client_name.length ()) != jack_client_name) { + return false; + } + } + return true; +} + +bool +AudioEngine::port_is_physical (const std::string& portname) const +{ + GET_PRIVATE_JACK_POINTER_RET(_jack, false); + + jack_port_t *port = jack_port_by_name (_priv_jack, portname.c_str()); + + if (!port) { + return false; + } + + return jack_port_flags (port) & JackPortIsPhysical; +} + +ChanCount +AudioEngine::n_physical (unsigned long flags) const +{ + ChanCount c; + + GET_PRIVATE_JACK_POINTER_RET (_jack, c); + + const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags); + if (ports == 0) { + return c; + } + + for (uint32_t i = 0; ports[i]; ++i) { + if (!strstr (ports[i], "Midi-Through")) { + DataType t (jack_port_type (jack_port_by_name (_jack, ports[i]))); + c.set (t, c.get (t) + 1); + } + } + + free (ports); + + return c; +} + +ChanCount +AudioEngine::n_physical_inputs () const +{ + return n_physical (JackPortIsInput); +} + +ChanCount +AudioEngine::n_physical_outputs () const +{ + return n_physical (JackPortIsOutput); +} + +void +AudioEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy) +{ + GET_PRIVATE_JACK_POINTER (_jack); + const char ** ports; + + if ((ports = jack_get_ports (_priv_jack, NULL, type.to_jack_type(), JackPortIsPhysical | flags)) == 0) { + return; + } + + if (ports) { + for (uint32_t i = 0; ports[i]; ++i) { + if (strstr (ports[i], "Midi-Through")) { + continue; + } + phy.push_back (ports[i]); + } + free (ports); + } +} + +/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to + * a physical input connector. + */ +void +AudioEngine::get_physical_inputs (DataType type, vector<string>& ins) +{ + get_physical (type, JackPortIsOutput, ins); +} + +/** Get physical ports for which JackPortIsInput is set; ie those that correspond to + * a physical output connector. + */ +void +AudioEngine::get_physical_outputs (DataType type, vector<string>& outs) +{ + get_physical (type, JackPortIsInput, outs); +} + + +bool +AudioEngine::can_request_hardware_monitoring () +{ + GET_PRIVATE_JACK_POINTER_RET (_jack,false); + const char ** ports; + + if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) { + return false; + } + + free (ports); + + return true; +} + + +/** @param name Full or short name of port + * @return Corresponding Port or 0. + */ + +boost::shared_ptr<Port> +AudioEngine::get_port_by_name (const string& portname) +{ + if (!_running) { + if (!_has_run) { + fatal << _("get_port_by_name() called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + boost::shared_ptr<Port> (); + } + } + + if (!port_is_mine (portname)) { + /* not an ardour port */ + return boost::shared_ptr<Port> (); + } + + boost::shared_ptr<Ports> pr = ports.reader(); + std::string rel = make_port_name_relative (portname); + Ports::iterator x = pr->find (rel); + + if (x != pr->end()) { + /* its possible that the port was renamed by some 3rd party and + we don't know about it. check for this (the check is quick + and cheap), and if so, rename the port (which will alter + the port map as a side effect). + */ + const std::string check = make_port_name_relative (jack_port_name (x->second->jack_port())); + if (check != rel) { + x->second->set_name (check); + } + return x->second; + } + + return boost::shared_ptr<Port> (); +} + +void +AudioEngine::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name) +{ + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> p = writer.get_copy(); + Ports::iterator x = p->find (old_relative_name); + + if (x != p->end()) { + boost::shared_ptr<Port> port = x->second; + p->erase (x); + p->insert (make_pair (new_relative_name, port)); + } +} + +const char ** +AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags) +{ + GET_PRIVATE_JACK_POINTER_RET (_jack,0); + if (!_running) { + if (!_has_run) { + fatal << _("get_ports called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + return 0; + } + } + return jack_get_ports (_priv_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags); +} + +void +AudioEngine::port_registration_failure (const std::string& portname) +{ + GET_PRIVATE_JACK_POINTER (_jack); + string full_portname = jack_client_name; + full_portname += ':'; + full_portname += portname; + + + jack_port_t* p = jack_port_by_name (_priv_jack, full_portname.c_str()); + string reason; + + if (p) { + reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname); + } else { + 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); + } + + throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); +} + +boost::shared_ptr<Port> +AudioEngine::register_port (DataType dtype, const string& portname, bool input) +{ + boost::shared_ptr<Port> newport; + + try { + if (dtype == DataType::AUDIO) { + newport.reset (new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput))); + } else if (dtype == DataType::MIDI) { + newport.reset (new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput))); + } else { + throw PortRegistrationFailure("unable to create port (unknown type)"); + } + + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + ps->insert (make_pair (make_port_name_relative (portname), newport)); + + /* writer goes out of scope, forces update */ + + return newport; + } + + catch (PortRegistrationFailure& err) { + throw err; + } catch (std::exception& e) { + throw PortRegistrationFailure(string_compose( + _("unable to create port: %1"), e.what()).c_str()); + } catch (...) { + throw PortRegistrationFailure("unable to create port (unknown error)"); + } +} + +boost::shared_ptr<Port> +AudioEngine::register_input_port (DataType type, const string& portname) +{ + return register_port (type, portname, true); +} + +boost::shared_ptr<Port> +AudioEngine::register_output_port (DataType type, const string& portname) +{ + return register_port (type, portname, false); +} + +int +AudioEngine::unregister_port (boost::shared_ptr<Port> port) +{ + /* caller must hold process lock */ + + if (!_running) { + /* probably happening when the engine has been halted by JACK, + in which case, there is nothing we can do here. + */ + return 0; + } + + { + RCUWriter<Ports> writer (ports); + boost::shared_ptr<Ports> ps = writer.get_copy (); + Ports::iterator x = ps->find (make_port_name_relative (port->name())); + + if (x != ps->end()) { + ps->erase (x); + } + + /* writer goes out of scope, forces update */ + } + + ports.flush (); + + return 0; +} + +bool +PortManager::connected (const string& port_name) +{ + PortEngine::PortHandle handle = _impl->get_port_by_name (port_name); + + if (!handle) { + return false; + } + + return _impl->connected (handle); +} + +int +AudioEngine::connect (const string& source, const string& destination) +{ + int ret; + + if (!_running) { + if (!_has_run) { + fatal << _("connect called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + return -1; + } + } + + string s = make_port_name_non_relative (source); + string d = make_port_name_non_relative (destination); + + + boost::shared_ptr<Port> src = get_port_by_name (s); + boost::shared_ptr<Port> dst = get_port_by_name (d); + + if (src) { + ret = src->connect (d); + } else if (dst) { + ret = dst->connect (s); + } else { + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + ret = -1; + } + + if (ret > 0) { + /* already exists - no error, no warning */ + } else if (ret < 0) { + error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"), + source, s, destination, d) + << endmsg; + } + + return ret; +} + +int +AudioEngine::disconnect (const string& source, const string& destination) +{ + int ret; + + if (!_running) { + if (!_has_run) { + fatal << _("disconnect called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + return -1; + } + } + + string s = make_port_name_non_relative (source); + string d = make_port_name_non_relative (destination); + + boost::shared_ptr<Port> src = get_port_by_name (s); + boost::shared_ptr<Port> dst = get_port_by_name (d); + + if (src) { + ret = src->disconnect (d); + } else if (dst) { + ret = dst->disconnect (s); + } else { + /* neither port is known to us, and this API isn't intended for use as a general patch bay */ + ret = -1; + } + return ret; +} + +int +AudioEngine::disconnect (boost::shared_ptr<Port> port) +{ + GET_PRIVATE_JACK_POINTER_RET (_jack,-1); + + if (!_running) { + if (!_has_run) { + fatal << _("disconnect called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + return -1; + } + } + + return port->disconnect_all (); +} + |