2008-06-02 17:41:35 -04:00
|
|
|
/*
|
2009-01-20 21:27:21 -05:00
|
|
|
Copyright (C) 2009 Paul Davis
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
#include "ardour/port.h"
|
|
|
|
#include "ardour/audioengine.h"
|
|
|
|
#include "ardour/i18n.h"
|
|
|
|
#include "pbd/failed_constructor.h"
|
|
|
|
#include "pbd/error.h"
|
|
|
|
#include "pbd/compose.h"
|
|
|
|
#include <stdexcept>
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::AudioEngine* ARDOUR::Port::_engine = 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
/** @param n Port short name */
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::Port (std::string const & n, DataType t, Flags f, bool e) : _jack_port (0), _last_monitor (false), _latency (0), _name (n), _flags (f)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
/* Unfortunately we have to pass the DataType into this constructor so that we can
|
|
|
|
create the right kind of JACK port; aside from this we'll use the virtual function type ()
|
|
|
|
to establish type. */
|
2009-01-21 22:13:34 -05:00
|
|
|
|
|
|
|
assert (_name.find_first_of (':') == std::string::npos);
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
if (e) {
|
|
|
|
try {
|
|
|
|
do_make_external (t);
|
|
|
|
}
|
|
|
|
catch (...) {
|
|
|
|
throw failed_constructor ();
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/** Port destructor */
|
|
|
|
ARDOUR::Port::~Port ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port) {
|
|
|
|
jack_port_unregister (_engine->jack (), _jack_port);
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/** Make this port externally visible by setting it up to use a JACK port.
|
|
|
|
* @param t Data type, so that we can call this method from the constructor.
|
|
|
|
*/
|
2008-06-02 17:41:35 -04:00
|
|
|
void
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::do_make_external (DataType t)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port) {
|
|
|
|
/* already external */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_jack_port = jack_port_register (_engine->jack (), _name.c_str (), t.to_jack_type (), _flags, 0);
|
|
|
|
if (_jack_port == 0) {
|
|
|
|
throw std::runtime_error ("Could not register JACK port");
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::make_external ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
do_make_external (type ());
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/** @return true if this port is connected to anything */
|
|
|
|
bool
|
|
|
|
ARDOUR::Port::connected () const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (!_connections.empty ()) {
|
|
|
|
/* connected to a Port* */
|
|
|
|
return true;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port == 0) {
|
|
|
|
/* not using a JACK port, so can't be connected to anything else */
|
|
|
|
return false;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
return (jack_port_connected (_jack_port) != 0);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::disconnect_all ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
/* Disconnect from Port* connections */
|
|
|
|
for (std::set<Port*>::iterator i = _connections.begin (); i != _connections.end (); ++i) {
|
|
|
|
(*i)->_connections.erase (this);
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
|
|
|
_connections.clear ();
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/* And JACK connections */
|
|
|
|
jack_port_disconnect (_engine->jack(), _jack_port);
|
|
|
|
_named_connections.clear ();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/** @param o Port name
|
|
|
|
* @return true if this port is connected to o, otherwise false.
|
|
|
|
*/
|
2008-06-02 17:41:35 -04:00
|
|
|
bool
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::connected_to (std::string const & o) const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-21 22:13:34 -05:00
|
|
|
std::string const full = _engine->make_port_name_non_relative (o);
|
|
|
|
std::string const shrt = _engine->make_port_name_non_relative (o);
|
|
|
|
|
|
|
|
if (_jack_port && jack_port_connected_to (_jack_port, full.c_str ())) {
|
2009-01-20 21:27:21 -05:00
|
|
|
/* connected via JACK */
|
|
|
|
return true;
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
for (std::set<Port*>::iterator i = _connections.begin (); i != _connections.end (); ++i) {
|
2009-01-21 22:13:34 -05:00
|
|
|
if ((*i)->name () == shrt) {
|
2009-01-20 21:27:21 -05:00
|
|
|
/* connected internally */
|
2008-06-02 17:41:35 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
/** @param o Filled in with port full names of ports that we are connected to */
|
2008-06-02 17:41:35 -04:00
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::get_connections (std::vector<std::string> & c) const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
/* JACK connections */
|
|
|
|
if (_jack_port) {
|
|
|
|
const char** jc = jack_port_get_connections (_jack_port);
|
|
|
|
if (jc) {
|
|
|
|
for (int i = 0; jc[i]; ++i) {
|
|
|
|
c.push_back (jc[i]);
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/* Internal connections */
|
|
|
|
for (std::set<Port*>::iterator i = _connections.begin (); i != _connections.end (); ++i) {
|
2009-01-21 22:13:34 -05:00
|
|
|
std::string const full = _engine->make_port_name_non_relative ((*i)->name());
|
|
|
|
c.push_back (full);
|
2009-01-20 21:27:21 -05:00
|
|
|
++n;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return n;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
int
|
|
|
|
ARDOUR::Port::connect (std::string const & other)
|
2009-01-02 14:38:43 -05:00
|
|
|
{
|
|
|
|
/* caller must hold process lock */
|
2009-01-21 22:13:34 -05:00
|
|
|
|
|
|
|
std::string const other_shrt = _engine->make_port_name_non_relative (other);
|
2009-01-20 21:27:21 -05:00
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
Port* p = _engine->get_port_by_name_locked (other_shrt);
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (p && !p->external ()) {
|
|
|
|
/* non-external Ardour port; connect using Port* */
|
|
|
|
r = connect (p);
|
|
|
|
} else {
|
|
|
|
/* connect using name */
|
2009-01-02 14:38:43 -05:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
/* for this to work, we must be an external port */
|
|
|
|
if (!external ()) {
|
|
|
|
make_external ();
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
std::string const this_shrt = _engine->make_port_name_non_relative (_name);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
if (sends_output ()) {
|
2009-01-21 22:13:34 -05:00
|
|
|
r = jack_connect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ());
|
2009-01-20 21:27:21 -05:00
|
|
|
} else {
|
2009-01-21 22:13:34 -05:00
|
|
|
r = jack_connect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str());
|
2009-01-20 21:27:21 -05:00
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
if (r == 0) {
|
|
|
|
_named_connections.insert (other);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return r;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
int
|
|
|
|
ARDOUR::Port::disconnect (std::string const & other)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
/* caller must hold process lock */
|
2009-01-21 22:13:34 -05:00
|
|
|
|
|
|
|
std::string const other_shrt = _engine->make_port_name_non_relative (other);
|
2009-01-20 21:27:21 -05:00
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
Port* p = _engine->get_port_by_name_locked (other_shrt);
|
2009-01-20 21:27:21 -05:00
|
|
|
int r;
|
|
|
|
|
|
|
|
if (p && !p->external ()) {
|
|
|
|
/* non-external Ardour port; disconnect using Port* */
|
|
|
|
r = disconnect (p);
|
2008-06-02 17:41:35 -04:00
|
|
|
} else {
|
2009-01-20 21:27:21 -05:00
|
|
|
/* disconnect using name */
|
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
std::string const this_shrt = _engine->make_port_name_non_relative (_name);
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
if (sends_output ()) {
|
2009-01-21 22:13:34 -05:00
|
|
|
r = jack_disconnect (_engine->jack (), this_shrt.c_str (), other_shrt.c_str ());
|
2009-01-20 21:27:21 -05:00
|
|
|
} else {
|
2009-01-21 22:13:34 -05:00
|
|
|
r = jack_disconnect (_engine->jack (), other_shrt.c_str (), this_shrt.c_str ());
|
2009-01-20 21:27:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (r == 0) {
|
|
|
|
_named_connections.erase (other);
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
return r;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
bool
|
2009-01-21 22:13:34 -05:00
|
|
|
ARDOUR::Port::connected_to (Port* o) const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
return connected_to (o->name ());
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::connect (Port* o)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
/* caller must hold process lock */
|
|
|
|
|
|
|
|
if (external () && o->external ()) {
|
|
|
|
/* we're both external; connect using name */
|
|
|
|
return connect (o->name ());
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
/* otherwise connect by Port* */
|
|
|
|
_connections.insert (o);
|
|
|
|
o->_connections.insert (this);
|
|
|
|
|
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
int
|
|
|
|
ARDOUR::Port::disconnect (Port* o)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (external () && o->external ()) {
|
|
|
|
/* we're both external; try disconnecting using name */
|
|
|
|
int const r = disconnect (o->name ());
|
|
|
|
if (r == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
_connections.erase (o);
|
|
|
|
o->_connections.erase (this);
|
|
|
|
|
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
void
|
|
|
|
ARDOUR::Port::set_engine (AudioEngine* e)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
_engine = e;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
void
|
|
|
|
ARDOUR::Port::ensure_monitor_input (bool yn)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port) {
|
|
|
|
jack_port_ensure_monitor (_jack_port, yn);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::monitoring_input () const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port) {
|
|
|
|
return jack_port_monitoring_input (_jack_port);
|
2008-06-02 17:41:35 -04:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::reset ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
_last_monitor = false;
|
|
|
|
|
|
|
|
// XXX
|
|
|
|
// _metering = 0;
|
|
|
|
// reset_meters ();
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::recompute_total_latency () const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
#ifdef HAVE_JACK_RECOMPUTE_LATENCY
|
|
|
|
if (_jack_port) {
|
|
|
|
jack_recompute_total_latency (_engine->jack (), _jack_port);
|
|
|
|
}
|
|
|
|
#endif
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
nframes_t
|
|
|
|
ARDOUR::Port::total_latency () const
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port) {
|
|
|
|
return jack_port_get_total_latency (_engine->jack (), _jack_port);
|
2008-06-02 17:41:35 -04:00
|
|
|
} else {
|
2009-01-20 21:27:21 -05:00
|
|
|
return _latency;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::reestablish ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
if (!_jack_port) {
|
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
_jack_port = jack_port_register (_engine->jack(), _name.c_str(), type().to_jack_type(), _flags, 0);
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
if (_jack_port == 0) {
|
|
|
|
PBD::error << string_compose (_("could not reregister %1"), _name) << endmsg;
|
|
|
|
return -1;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
reset ();
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::reconnect ()
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
/* caller must hold process lock; intended to be used only after reestablish() */
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
if (!_jack_port) {
|
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
2009-01-20 21:27:21 -05:00
|
|
|
|
|
|
|
for (std::set<string>::iterator i = _named_connections.begin(); i != _named_connections.end(); ++i) {
|
|
|
|
if (connect (*i)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return 0;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-21 22:13:34 -05:00
|
|
|
/** @param n Short name */
|
2008-06-02 17:41:35 -04:00
|
|
|
int
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::set_name (std::string const & n)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-21 22:13:34 -05:00
|
|
|
assert (_name.find_first_of (':') == std::string::npos);
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
if (_jack_port) {
|
|
|
|
r = jack_port_set_name (_jack_port, n.c_str());
|
2009-01-22 08:51:36 -05:00
|
|
|
if (r == 0) {
|
2009-01-20 21:27:21 -05:00
|
|
|
_name = n;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_name = n;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
return r;
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-01-20 21:27:21 -05:00
|
|
|
ARDOUR::Port::set_latency (nframes_t n)
|
2008-06-02 17:41:35 -04:00
|
|
|
{
|
2009-01-20 21:27:21 -05:00
|
|
|
_latency = n;
|
|
|
|
}
|
2008-06-02 17:41:35 -04:00
|
|
|
|
2009-01-20 21:27:21 -05:00
|
|
|
void
|
|
|
|
ARDOUR::Port::request_monitor_input (bool yn)
|
|
|
|
{
|
|
|
|
if (_jack_port) {
|
|
|
|
jack_port_request_monitor (_jack_port, yn);
|
2008-06-02 17:41:35 -04:00
|
|
|
}
|
|
|
|
}
|