Paul Davis
3c857a78c6
As was noted in 88ee3af3ea
it is unsafe/undefined behavior if two threads
sleep on the JACK request file descriptor, since there is no way to control
which one will wake and process the request. Since each thread may have
sent a different request, this can lead to a thread misinterpreting the
response because it is reading the wrong response.
This may (or may not) solve some subtle problems with JACK, but was
revealed by having a control surface (LaunchPad Pro) that registers
three ports from the butler thread at about the same as the GUI
thread is registering the auditioner. One thread read the wrong
response, and because of some slightly weird code/design, it attempts
to rename the port from within the response handler, which in JACK1
leads to deadlock (and later, zombification).
750 lines
20 KiB
C++
750 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2013-2018 Paul Davis <paul@linuxaudiosystems.com>
|
|
* Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
|
|
#include "pbd/error.h"
|
|
|
|
#include "jack_audiobackend.h"
|
|
#include "jack_connection.h"
|
|
|
|
#include "ardour/debug.h"
|
|
#include "ardour/port_manager.h"
|
|
|
|
#include "pbd/i18n.h"
|
|
|
|
using namespace ARDOUR;
|
|
using namespace PBD;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
#define GET_PRIVATE_JACK_POINTER(localvar) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return; }
|
|
#define GET_PRIVATE_JACK_POINTER_RET(localvar,r) jack_client_t* localvar = _jack_connection->jack(); if (!(localvar)) { return r; }
|
|
// #define JACK_SERVER_CALL(expr) { std::cerr << "JACK SERVER CALL: " << pthread_self() << '/' << pthread_name() << ' ' << #expr << std::endl; Glib::Threads::Mutex::Lock lm (server_call_mutex); expr; }
|
|
#define JACK_SERVER_CALL(expr) { Glib::Threads::Mutex::Lock lm (server_call_mutex); expr; }
|
|
|
|
static uint32_t
|
|
ardour_port_flags_to_jack_flags (PortFlags flags)
|
|
{
|
|
uint32_t jack_flags = 0;
|
|
|
|
if (flags & IsInput) {
|
|
jack_flags |= JackPortIsInput;
|
|
}
|
|
if (flags & IsOutput) {
|
|
jack_flags |= JackPortIsOutput;
|
|
}
|
|
if (flags & IsTerminal) {
|
|
jack_flags |= JackPortIsTerminal;
|
|
}
|
|
if (flags & IsPhysical) {
|
|
jack_flags |= JackPortIsPhysical;
|
|
}
|
|
if (flags & CanMonitor) {
|
|
jack_flags |= JackPortCanMonitor;
|
|
}
|
|
|
|
return jack_flags;
|
|
}
|
|
|
|
static DataType
|
|
jack_port_type_to_ardour_data_type (const char* jack_type)
|
|
{
|
|
if (strcmp (jack_type, JACK_DEFAULT_AUDIO_TYPE) == 0) {
|
|
return DataType::AUDIO;
|
|
} else if (strcmp (jack_type, JACK_DEFAULT_MIDI_TYPE) == 0) {
|
|
return DataType::MIDI;
|
|
}
|
|
return DataType::NIL;
|
|
}
|
|
|
|
static const char*
|
|
ardour_data_type_to_jack_port_type (DataType d)
|
|
{
|
|
switch (d) {
|
|
case DataType::AUDIO:
|
|
return JACK_DEFAULT_AUDIO_TYPE;
|
|
case DataType::MIDI:
|
|
return JACK_DEFAULT_MIDI_TYPE;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::when_connected_to_jack ()
|
|
{
|
|
/* register callbacks for stuff that is our responsibility */
|
|
|
|
jack_client_t* client = _jack_connection->jack();
|
|
|
|
if (!client) {
|
|
/* how could this happen? it could ... */
|
|
error << _("Already disconnected from JACK before PortEngine could register callbacks") << endmsg;
|
|
return;
|
|
}
|
|
|
|
JACK_SERVER_CALL (jack_set_port_registration_callback (client, _registration_callback, this));
|
|
JACK_SERVER_CALL (jack_set_port_connect_callback (client, _connect_callback, this));
|
|
JACK_SERVER_CALL (jack_set_graph_order_callback (client, _graph_order_callback, this));
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::set_port_name (PortHandle port, const std::string& name)
|
|
{
|
|
#if HAVE_JACK_PORT_RENAME
|
|
jack_client_t* client = _jack_connection->jack();
|
|
if (client) {
|
|
JACK_SERVER_CALL (return jack_port_rename (client, std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, name.c_str()));
|
|
} else {
|
|
return -1;
|
|
}
|
|
#else
|
|
JACK_SERVER_CALL (return jack_port_set_name (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, name.c_str()));
|
|
#endif
|
|
}
|
|
|
|
string
|
|
JACKAudioBackend::get_port_name (PortHandle port) const
|
|
{
|
|
if (!port) {
|
|
error << _("Fetching port name for non-existent port!") << endmsg;
|
|
return string();
|
|
}
|
|
|
|
jack_port_t* jp = std::dynamic_pointer_cast<JackPort>(port)->jack_ptr;
|
|
|
|
if (!jp) {
|
|
error << _("Fetching port name for non-existent JACK port!") << endmsg;
|
|
return string();
|
|
}
|
|
|
|
return jack_port_name (jp);
|
|
}
|
|
|
|
PortFlags
|
|
JACKAudioBackend::get_port_flags (PortHandle port) const
|
|
{
|
|
return PortFlags (jack_port_flags (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr));
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::get_port_property (PortHandle port, const std::string& key, std::string& value, std::string& type) const
|
|
{
|
|
#ifdef HAVE_JACK_METADATA // really everyone ought to have this by now.
|
|
int rv = -1;
|
|
char *cvalue = NULL;
|
|
char *ctype = NULL;
|
|
|
|
jack_uuid_t uuid = jack_port_uuid (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr);
|
|
rv = jack_get_property (uuid, key.c_str(), &cvalue, &ctype);
|
|
|
|
if (0 == rv && cvalue) {
|
|
value = cvalue;
|
|
if (ctype) {
|
|
type = ctype;
|
|
}
|
|
} else {
|
|
rv = -1;
|
|
}
|
|
|
|
jack_free(cvalue);
|
|
jack_free(ctype);
|
|
return rv;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::set_port_property (PortHandle port, const std::string& key, const std::string& value, const std::string& type)
|
|
{
|
|
#ifdef HAVE_JACK_METADATA // really everyone ought to have this by now.
|
|
int rv = -1;
|
|
jack_client_t* client = _jack_connection->jack();
|
|
jack_uuid_t uuid = jack_port_uuid (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr);
|
|
return jack_set_property(client, uuid, key.c_str(), value.c_str(), type.c_str());
|
|
return rv;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
PortEngine::PortPtr
|
|
JACKAudioBackend::get_port_by_name (const std::string& name) const
|
|
{
|
|
{
|
|
std::shared_ptr<JackPorts const> ports = _jack_ports.reader ();
|
|
JackPorts::const_iterator p = ports->find (name);
|
|
|
|
if (p != ports->end()) {
|
|
return p->second;
|
|
}
|
|
}
|
|
|
|
/* Port not known to us yet, so look it up via JACK (slow) */
|
|
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, PortEngine::PortPtr());
|
|
jack_port_t * jack_port = jack_port_by_name (_priv_jack, name.c_str());
|
|
|
|
if (!jack_port) {
|
|
/* No such port ... return nothering */
|
|
return PortEngine::PortPtr();
|
|
}
|
|
|
|
std::shared_ptr<JackPort> jp;
|
|
|
|
{
|
|
RCUWriter<JackPorts> writer (_jack_ports);
|
|
std::shared_ptr<JackPorts> ports = writer.get_copy();
|
|
jp.reset (new JackPort (jack_port));
|
|
ports->insert (std::make_pair (name, jp));
|
|
}
|
|
|
|
_jack_ports.flush ();
|
|
|
|
return jp;
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::_registration_callback (jack_port_id_t id, int reg, void* arg)
|
|
{
|
|
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack port registration callback\n", pthread_self(), pthread_name()));
|
|
|
|
/* we don't use a virtual method for the registration callback, because
|
|
JACK is the only backend that delivers the arguments shown above. So
|
|
call our own JACK-centric registration callback, then the generic
|
|
one.
|
|
*/
|
|
static_cast<JACKAudioBackend*> (arg)->jack_registration_callback (id, reg);
|
|
static_cast<JACKAudioBackend*> (arg)->manager.registration_callback ();
|
|
static_cast<JACKAudioBackend*> (arg)->engine.latency_callback (false);
|
|
static_cast<JACKAudioBackend*> (arg)->engine.latency_callback (true);
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::jack_registration_callback (jack_port_id_t id, int reg)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER (_priv_jack);
|
|
jack_port_t* jack_port = jack_port_by_id (_priv_jack, id);
|
|
|
|
if (!jack_port) {
|
|
return;
|
|
}
|
|
|
|
/* We only need to care about ports that we do not register/unregister
|
|
* ourselves. Our own ports will added/removed from _jack_ports at the
|
|
* appropriate time.
|
|
*
|
|
* But for the input meters, we'll be looking up ports not created by
|
|
* us, and they may also go away at arbitrary times too. We want to
|
|
* make sure we can look up these ports by name only (in _jack_ports)
|
|
* because jack_port_by_name() is unacceptably slow for RT contexts
|
|
* (like run_input_meters()). So we catch these ports at registration
|
|
* time, and put a suitable entry in _jack_ports.
|
|
*
|
|
* It isn't critical that we keep _jack_ports current if any of these
|
|
* ports goes away, but since we get told about that here, we do that
|
|
* just to keep things clean. This will happen if someone disconnects a
|
|
* USB MIDI device, for example.
|
|
*/
|
|
|
|
if (!jack_port_is_mine (_priv_jack, jack_port)) {
|
|
|
|
const char* name = jack_port_name (jack_port);
|
|
|
|
std::shared_ptr<JackPorts> ports = _jack_ports.write_copy();
|
|
|
|
if (!reg) {
|
|
if (ports->erase (name)) {
|
|
_jack_ports.update (ports);
|
|
} else {
|
|
_jack_ports.no_update();
|
|
}
|
|
} else {
|
|
if (ports->find (name) != ports->end()) {
|
|
/* hmmm, we already have this port */
|
|
std::cout << "re-registration of JACK port named " << name << std::endl;
|
|
ports->erase (name);
|
|
}
|
|
|
|
std::shared_ptr<JackPort> jp (new JackPort (jack_port));
|
|
ports->insert (std::make_pair (name, jp));
|
|
|
|
_jack_ports.update (ports);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::_graph_order_callback (void *arg)
|
|
{
|
|
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack graph order callback\n", pthread_self(), pthread_name()));
|
|
return static_cast<JACKAudioBackend*> (arg)->manager.graph_order_callback ();
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg)
|
|
{
|
|
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack connect/disconnect callback\n", pthread_self(), pthread_name()));
|
|
static_cast<JACKAudioBackend*> (arg)->connect_callback (id_a, id_b, conn);
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn)
|
|
{
|
|
if (manager.port_remove_in_progress()) {
|
|
return;
|
|
}
|
|
|
|
GET_PRIVATE_JACK_POINTER (_priv_jack);
|
|
|
|
jack_port_t* a = jack_port_by_id (_priv_jack, id_a);
|
|
jack_port_t* b = jack_port_by_id (_priv_jack, id_b);
|
|
|
|
manager.connect_callback (jack_port_name (a), jack_port_name (b), conn == 0 ? false : true);
|
|
}
|
|
|
|
bool
|
|
JACKAudioBackend::connected (PortHandle p, bool process_callback_safe)
|
|
{
|
|
bool ret = false;
|
|
jack_port_t* port = std::dynamic_pointer_cast<JackPort>(p)->jack_ptr;
|
|
const char** ports;
|
|
|
|
if (process_callback_safe) {
|
|
ports = jack_port_get_connections (port);
|
|
} else {
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
JACK_SERVER_CALL (ports = jack_port_get_all_connections (_priv_jack, port));
|
|
}
|
|
|
|
if (ports) {
|
|
ret = true;
|
|
}
|
|
|
|
jack_free (ports);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
JACKAudioBackend::connected_to (PortHandle p, const std::string& other, bool process_callback_safe)
|
|
{
|
|
bool ret = false;
|
|
const char** ports;
|
|
jack_port_t* port = std::dynamic_pointer_cast<JackPort>(p)->jack_ptr;
|
|
|
|
if (process_callback_safe) {
|
|
ports = jack_port_get_connections (port);
|
|
} else {
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
JACK_SERVER_CALL (ports = jack_port_get_all_connections (_priv_jack, port));
|
|
}
|
|
|
|
if (ports) {
|
|
for (int i = 0; ports[i]; ++i) {
|
|
if (other == ports[i]) {
|
|
ret = true;
|
|
}
|
|
}
|
|
jack_free (ports);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
JACKAudioBackend::physically_connected (PortHandle p, bool process_callback_safe)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
jack_port_t* port = std::dynamic_pointer_cast<JackPort>(p)->jack_ptr;
|
|
|
|
const char** ports;
|
|
|
|
if (process_callback_safe) {
|
|
ports = jack_port_get_connections ((jack_port_t*)port);
|
|
} else {
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
JACK_SERVER_CALL (ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port));
|
|
}
|
|
|
|
if (ports) {
|
|
for (int i = 0; ports[i]; ++i) {
|
|
|
|
jack_port_t* other = jack_port_by_name (_priv_jack, ports[i]);
|
|
|
|
if (other && (jack_port_flags (other) & JackPortIsPhysical)) {
|
|
jack_free (ports);
|
|
return true;
|
|
}
|
|
}
|
|
jack_free (ports);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
JACKAudioBackend::externally_connected (PortHandle p, bool process_callback_safe)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
jack_port_t* port = std::dynamic_pointer_cast<JackPort>(p)->jack_ptr;
|
|
|
|
const char** ports;
|
|
|
|
if (process_callback_safe) {
|
|
ports = jack_port_get_connections ((jack_port_t*)port);
|
|
} else {
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
|
|
JACK_SERVER_CALL (ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port));
|
|
}
|
|
|
|
if (ports) {
|
|
for (int i = 0; ports[i]; ++i) {
|
|
jack_port_t* other = jack_port_by_name (_priv_jack, ports[i]);
|
|
|
|
if (other && (jack_port_flags (other) & JackPortIsPhysical)) {
|
|
jack_free (ports);
|
|
return true;
|
|
}
|
|
if (other && !jack_port_is_mine (_priv_jack, other)) {
|
|
jack_free (ports);
|
|
return true;
|
|
}
|
|
}
|
|
jack_free (ports);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::get_connections (PortHandle p, vector<string>& s, bool process_callback_safe)
|
|
{
|
|
const char** ports;
|
|
jack_port_t* port = std::dynamic_pointer_cast<JackPort>(p)->jack_ptr;
|
|
|
|
if (process_callback_safe) {
|
|
ports = jack_port_get_connections (port);
|
|
} else {
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
|
|
JACK_SERVER_CALL (ports = jack_port_get_all_connections (_priv_jack, port));
|
|
}
|
|
|
|
if (ports) {
|
|
for (int i = 0; ports[i]; ++i) {
|
|
s.push_back (ports[i]);
|
|
}
|
|
jack_free (ports);
|
|
}
|
|
|
|
return s.size();
|
|
}
|
|
|
|
DataType
|
|
JACKAudioBackend::port_data_type (PortHandle port) const
|
|
{
|
|
return jack_port_type_to_ardour_data_type (jack_port_type (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr));
|
|
}
|
|
|
|
const string&
|
|
JACKAudioBackend::my_name() const
|
|
{
|
|
return _jack_connection->client_name();
|
|
}
|
|
|
|
bool
|
|
JACKAudioBackend::port_is_physical (PortHandle port) const
|
|
{
|
|
if (!port) {
|
|
return false;
|
|
}
|
|
|
|
return jack_port_flags (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr) & JackPortIsPhysical;
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s) const
|
|
{
|
|
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack,0);
|
|
|
|
const char** ports = jack_get_ports (_priv_jack, port_name_pattern.c_str(), ardour_data_type_to_jack_port_type (type), ardour_port_flags_to_jack_flags (flags));
|
|
|
|
if (ports == 0) {
|
|
return 0;
|
|
}
|
|
|
|
for (uint32_t i = 0; ports[i]; ++i) {
|
|
s.push_back (ports[i]);
|
|
}
|
|
|
|
jack_free (ports);
|
|
|
|
return s.size();
|
|
}
|
|
|
|
ChanCount
|
|
JACKAudioBackend::n_physical_inputs () const
|
|
{
|
|
return n_physical (JackPortIsInput);
|
|
}
|
|
|
|
ChanCount
|
|
JACKAudioBackend::n_physical_outputs () const
|
|
{
|
|
return n_physical (JackPortIsOutput);
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::get_physical (DataType type, unsigned long flags, vector<string>& phy) const
|
|
{
|
|
GET_PRIVATE_JACK_POINTER (_priv_jack);
|
|
const char ** ports = jack_get_ports (_priv_jack, NULL, ardour_data_type_to_jack_port_type (type), JackPortIsPhysical | flags);
|
|
|
|
if (!ports) {
|
|
return;
|
|
}
|
|
|
|
for (uint32_t i = 0; ports[i]; ++i) {
|
|
if (strstr (ports[i], "Midi-Through")) {
|
|
continue;
|
|
}
|
|
phy.push_back (ports[i]);
|
|
}
|
|
jack_free (ports);
|
|
}
|
|
|
|
/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
|
|
* a physical input connector.
|
|
*/
|
|
void
|
|
JACKAudioBackend::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
|
|
JACKAudioBackend::get_physical_outputs (DataType type, vector<string>& outs)
|
|
{
|
|
get_physical (type, JackPortIsInput, outs);
|
|
}
|
|
|
|
|
|
bool
|
|
JACKAudioBackend::can_monitor_input () const
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false);
|
|
const char ** ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor);
|
|
|
|
if (ports) {
|
|
return false;
|
|
}
|
|
|
|
jack_free (ports);
|
|
|
|
return true;
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::request_input_monitoring (PortHandle port, bool yn)
|
|
{
|
|
JACK_SERVER_CALL (return jack_port_request_monitor (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, yn));
|
|
}
|
|
int
|
|
JACKAudioBackend::ensure_input_monitoring (PortHandle port, bool yn)
|
|
{
|
|
JACK_SERVER_CALL (return jack_port_ensure_monitor (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, yn));
|
|
}
|
|
bool
|
|
JACKAudioBackend::monitoring_input (PortHandle port)
|
|
{
|
|
return jack_port_monitoring_input (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr);
|
|
}
|
|
|
|
PortEngine::PortPtr
|
|
JACKAudioBackend::register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags)
|
|
{
|
|
jack_port_t* jack_port;
|
|
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, PortEngine::PortPtr());
|
|
JACK_SERVER_CALL (jack_port = jack_port_register (_priv_jack, shortname.c_str(), ardour_data_type_to_jack_port_type (type), ardour_port_flags_to_jack_flags (flags), 0));
|
|
|
|
if (!jack_port) {
|
|
return PortEngine::PortPtr();
|
|
}
|
|
|
|
std::shared_ptr<JackPort> jp;
|
|
|
|
{
|
|
RCUWriter<JackPorts> writer (_jack_ports);
|
|
std::shared_ptr<JackPorts> ports = writer.get_copy();
|
|
|
|
jp.reset (new JackPort (jack_port));
|
|
|
|
ports->insert (std::make_pair (jack_port_name (jack_port), jp));
|
|
}
|
|
|
|
_jack_ports.flush();
|
|
|
|
return jp;
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::unregister_port (PortHandle port)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER (_priv_jack);
|
|
std::shared_ptr<JackPort> jp = std::dynamic_pointer_cast<JackPort>(port);
|
|
const std::string name = jack_port_name (jp->jack_ptr);
|
|
|
|
{
|
|
RCUWriter<JackPorts> writer (_jack_ports);
|
|
std::shared_ptr<JackPorts> ports = writer.get_copy();
|
|
ports->erase (name);
|
|
}
|
|
|
|
_jack_ports.flush ();
|
|
|
|
(void) jack_port_unregister (_priv_jack, jp->jack_ptr);
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::connect (PortHandle port, const std::string& other)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
|
|
int r;
|
|
|
|
JACK_SERVER_CALL (r = jack_connect (_priv_jack, jack_port_name (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr), other.c_str()));
|
|
|
|
if (r == 0 || r == EEXIST) {
|
|
return 0;
|
|
}
|
|
return r;
|
|
}
|
|
int
|
|
JACKAudioBackend::connect (const std::string& src, const std::string& dst)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
|
|
int r;
|
|
|
|
JACK_SERVER_CALL (r = jack_connect (_priv_jack, src.c_str(), dst.c_str()));
|
|
|
|
if (r == 0 || r == EEXIST) {
|
|
return 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::disconnect (PortHandle port, const std::string& other)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
|
|
JACK_SERVER_CALL (return jack_disconnect (_priv_jack, jack_port_name (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr), other.c_str()));
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::disconnect (const std::string& src, const std::string& dst)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
|
|
JACK_SERVER_CALL (return jack_disconnect (_priv_jack, src.c_str(), dst.c_str()));
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::disconnect_all (PortHandle port)
|
|
{
|
|
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
|
|
JACK_SERVER_CALL (return jack_port_disconnect (_priv_jack, std::dynamic_pointer_cast<JackPort>(port)->jack_ptr));
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index)
|
|
{
|
|
jack_midi_event_t ev;
|
|
int ret;
|
|
|
|
if ((ret = jack_midi_event_get (&ev, port_buffer, event_index)) == 0) {
|
|
timestamp = ev.time;
|
|
size = ev.size;
|
|
*buf = ev.buffer;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
JACKAudioBackend::midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size)
|
|
{
|
|
return jack_midi_event_write (port_buffer, timestamp, buffer, size);
|
|
}
|
|
|
|
uint32_t
|
|
JACKAudioBackend::get_midi_event_count (void* port_buffer)
|
|
{
|
|
return jack_midi_get_event_count (port_buffer);
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::midi_clear (void* port_buffer)
|
|
{
|
|
jack_midi_clear_buffer (port_buffer);
|
|
}
|
|
|
|
void
|
|
JACKAudioBackend::set_latency_range (PortHandle port, bool for_playback, LatencyRange r)
|
|
{
|
|
jack_latency_range_t range;
|
|
|
|
range.min = r.min;
|
|
range.max = r.max;
|
|
|
|
jack_port_set_latency_range (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
|
|
}
|
|
|
|
LatencyRange
|
|
JACKAudioBackend::get_latency_range (PortHandle port, bool for_playback)
|
|
{
|
|
jack_latency_range_t range;
|
|
LatencyRange ret;
|
|
|
|
jack_port_get_latency_range (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
|
|
|
|
ret.min = range.min;
|
|
ret.max = range.max;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void*
|
|
JACKAudioBackend::get_buffer (PortHandle port, pframes_t nframes)
|
|
{
|
|
return jack_port_get_buffer (std::dynamic_pointer_cast<JackPort>(port)->jack_ptr, nframes);
|
|
}
|
|
|
|
uint32_t
|
|
JACKAudioBackend::port_name_size() const
|
|
{
|
|
return jack_port_name_size ();
|
|
}
|