13
0
livetrax/libs/backends/jack/jack_audiobackend.cc

1260 lines
29 KiB
C++
Raw Normal View History

/*
* Copyright (C) 2013-2015 Tim Mayberry <mojofunk@gmail.com>
* Copyright (C) 2013-2017 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2014-2018 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>
#include <list>
#include <math.h>
#include <boost/scoped_ptr.hpp>
2013-07-30 17:48:57 -04:00
#include <glibmm/timer.h>
#include <glibmm/spawn.h>
2013-07-30 17:48:57 -04:00
#include "pbd/error.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/session.h"
#include "ardour/types.h"
#include "jack_audiobackend.h"
#include "jack_connection.h"
#include "jack_utils.h"
#include "jack_session.h"
#include "pbd/i18n.h"
2013-07-30 17:48:57 -04:00
using namespace ARDOUR;
2013-07-30 17:48:57 -04:00
using namespace PBD;
using std::string;
2013-07-30 17:48:57 -04:00
using std::vector;
2013-07-30 17:48:57 -04:00
#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: " << DEBUG_THREAD_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; }
JACKAudioBackend::JACKAudioBackend (AudioEngine& e, AudioBackendInfo& info, std::shared_ptr<JackConnection> jc)
: AudioBackend (e, info)
2013-07-31 19:47:20 -04:00
, _jack_connection (jc)
2013-07-30 17:48:57 -04:00
, _running (false)
, _freewheeling (false)
, _target_sample_rate (48000)
, _target_buffer_size (1024)
2015-12-05 07:56:18 -05:00
, _target_num_periods (2)
, _target_interleaved (false)
, _target_input_channels (0)
, _target_output_channels (0)
, _target_systemic_input_latency (0)
, _target_systemic_output_latency (0)
, _current_sample_rate (0)
, _current_buffer_size (0)
, _jack_ports (new JackPorts)
, _session (0)
{
_jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKAudioBackend::when_connected_to_jack, this));
_jack_connection->Disconnected.connect_same_thread (disconnect_connection, boost::bind (&JACKAudioBackend::disconnected, this, _1));
}
JACKAudioBackend::~JACKAudioBackend()
{
{
RCUWriter<JackPorts> writer (_jack_ports);
std::shared_ptr<JackPorts> jp = writer.get_copy ();
jp->clear ();
}
_jack_ports.flush ();
}
string
JACKAudioBackend::name() const
{
return X_("JACK");
}
void*
2013-07-30 17:48:57 -04:00
JACKAudioBackend::private_handle() const
{
2013-07-30 17:48:57 -04:00
return _jack_connection->jack();
}
bool
JACKAudioBackend::available() const
{
return (private_handle() != 0);
}
bool
2013-07-30 17:48:57 -04:00
JACKAudioBackend::is_realtime () const
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false);
return jack_is_realtime (_priv_jack);
}
bool
JACKAudioBackend::requires_driver_selection() const
{
return true;
}
vector<string>
JACKAudioBackend::enumerate_drivers () const
{
vector<string> currently_available;
get_jack_audio_driver_names (currently_available);
return currently_available;
}
int
JACKAudioBackend::set_driver (const std::string& name)
{
_target_driver = name;
return 0;
}
vector<AudioBackend::DeviceStatus>
JACKAudioBackend::enumerate_devices () const
{
vector<string> currently_available = get_jack_device_names_for_audio_driver (_target_driver);
vector<DeviceStatus> statuses;
if (all_devices.find (_target_driver) == all_devices.end()) {
all_devices.insert (make_pair (_target_driver, std::set<string>()));
}
2015-10-05 10:17:49 -04:00
/* store every device we've found, by driver name.
*
* This is so we do not confuse ALSA, FFADO, netjack etc. devices
* with each other.
*/
DeviceList& all (all_devices[_target_driver]);
for (vector<string>::const_iterator d = currently_available.begin(); d != currently_available.end(); ++d) {
all.insert (*d);
}
2015-10-05 10:17:49 -04:00
for (DeviceList::const_iterator d = all.begin(); d != all.end(); ++d) {
if (find (currently_available.begin(), currently_available.end(), *d) == currently_available.end()) {
statuses.push_back (DeviceStatus (*d, false));
} else {
statuses.push_back (DeviceStatus (*d, false));
}
}
2015-10-05 10:17:49 -04:00
return statuses;
}
vector<float>
JACKAudioBackend::available_sample_rates (const string& device) const
{
vector<float> f;
2015-10-05 10:17:49 -04:00
if (device == _target_device && available()) {
2013-07-30 17:48:57 -04:00
f.push_back (sample_rate());
return f;
}
/* if JACK is not already running, just list a bunch of reasonable
values and let the future sort it all out.
*/
f.push_back (8000.0);
f.push_back (16000.0);
f.push_back (24000.0);
f.push_back (32000.0);
f.push_back (44100.0);
f.push_back (48000.0);
f.push_back (88200.0);
f.push_back (96000.0);
f.push_back (192000.0);
f.push_back (384000.0);
2015-10-05 10:17:49 -04:00
return f;
}
vector<uint32_t>
JACKAudioBackend::available_buffer_sizes (const string& device) const
{
vector<uint32_t> s;
2015-10-05 10:17:49 -04:00
if (device == _target_device && available()) {
2013-07-30 17:48:57 -04:00
s.push_back (buffer_size());
return s;
}
s.push_back (8);
s.push_back (16);
s.push_back (32);
s.push_back (64);
s.push_back (128);
s.push_back (256);
s.push_back (512);
s.push_back (1024);
s.push_back (2048);
s.push_back (4096);
s.push_back (8192);
return s;
}
2015-12-05 07:56:18 -05:00
std::vector<uint32_t>
JACKAudioBackend::available_period_sizes (const std::string& driver, const std::string&) const
2015-12-05 07:56:18 -05:00
{
vector<uint32_t> s;
if (ARDOUR::get_jack_audio_driver_supports_setting_period_count (driver)) {
s.push_back (2);
s.push_back (3);
}
return s;
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::available_input_channel_count (const string& /*device*/) const
{
return 128;
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::available_output_channel_count (const string& /*device*/) const
{
return 128;
}
/* -- parameter setting -- */
int
JACKAudioBackend::set_device_name (const string& dev)
{
if (available()) {
/* need to stop and restart JACK for this to work, at present */
return -1;
}
_target_device = dev;
return 0;
}
int
JACKAudioBackend::set_sample_rate (float sr)
{
if (!available()) {
2013-07-30 17:48:57 -04:00
_target_sample_rate = sr;
return 0;
}
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
if (sr == jack_get_sample_rate (_priv_jack)) {
return 0;
}
2013-07-30 17:48:57 -04:00
return -1;
}
2015-12-05 07:56:18 -05:00
int
JACKAudioBackend::set_peridod_size (uint32_t nperiods)
{
if (!available()) {
_target_num_periods = nperiods;
return 0;
}
return -1;
}
int
JACKAudioBackend::set_buffer_size (uint32_t nframes)
{
if (!available()) {
2013-07-30 17:48:57 -04:00
_target_buffer_size = nframes;
return 0;
}
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
if (nframes == jack_get_buffer_size (_priv_jack)) {
return 0;
}
return jack_set_buffer_size (_priv_jack, nframes);
}
int
JACKAudioBackend::set_interleaved (bool yn)
{
/* as far as JACK clients are concerned, the hardware is always
* non-interleaved
*/
if (!yn) {
return 0;
}
return -1;
}
int
JACKAudioBackend::set_input_channels (uint32_t cnt)
{
if (available()) {
if (cnt != 0) {
/* can't set a real value for this while JACK runs */
return -1;
}
}
_target_input_channels = cnt;
2015-10-05 10:17:49 -04:00
2013-07-30 17:48:57 -04:00
return 0;
}
int
JACKAudioBackend::set_output_channels (uint32_t cnt)
{
if (available()) {
if (cnt != 0) {
/* can't set a real value for this while JACK runs */
return -1;
}
}
_target_output_channels = cnt;
2013-07-30 17:48:57 -04:00
return 0;
}
int
JACKAudioBackend::set_systemic_input_latency (uint32_t l)
{
if (available()) {
/* can't do this while JACK runs */
return -1;
}
_target_systemic_input_latency = l;
2013-07-30 17:48:57 -04:00
return 0;
}
int
JACKAudioBackend::set_systemic_output_latency (uint32_t l)
{
if (available()) {
/* can't do this while JACK runs */
return -1;
}
_target_systemic_output_latency = l;
2013-07-30 17:48:57 -04:00
return 0;
}
/* --- Parameter retrieval --- */
std::string
2013-07-30 17:48:57 -04:00
JACKAudioBackend::device_name () const
{
if (!_jack_connection->in_control()) {
return "???"; // JACK has no way (as of fall 2013) to return
// the device name
}
return _target_device;
}
std::string
JACKAudioBackend::driver_name() const
{
if (!_jack_connection->in_control()) {
return "???"; // JACK has no way (as of fall 2013) to return
// the driver name
}
return _target_driver;
}
float
2013-07-30 17:48:57 -04:00
JACKAudioBackend::sample_rate () const
{
if (!_jack_connection->in_control()) {
if (available()) {
return _current_sample_rate;
} else {
return _jack_connection->probed_sample_rate ();
}
}
return _target_sample_rate;
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::buffer_size () const
{
if (!_jack_connection->in_control()) {
if (available()) {
return _current_buffer_size;
} else {
return _jack_connection->probed_buffer_size ();
}
}
return _target_buffer_size;
}
2015-12-05 07:56:18 -05:00
uint32_t
JACKAudioBackend::period_size () const
{
return _target_num_periods;
}
bool
2013-07-30 17:48:57 -04:00
JACKAudioBackend::interleaved () const
{
return false;
}
string
JACKAudioBackend::midi_option () const
{
return _target_midi_option;
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::input_channels () const
{
if (!_jack_connection->in_control()) {
if (available()) {
return n_physical (JackPortIsInput).n_audio();
} else {
return 0;
}
} else {
if (available()) {
return n_physical (JackPortIsInput).n_audio();
} else {
return _target_input_channels;
}
}
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::output_channels () const
{
if (!_jack_connection->in_control()) {
if (available()) {
return n_physical (JackPortIsOutput).n_audio();
} else {
return 0;
}
} else {
if (available()) {
return n_physical (JackPortIsOutput).n_audio();
} else {
return _target_output_channels;
}
}
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::systemic_input_latency () const
{
return _target_systemic_output_latency;
}
uint32_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::systemic_output_latency () const
{
return _target_systemic_output_latency;
}
size_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::raw_buffer_size(DataType t)
{
std::map<DataType,size_t>::const_iterator s = _raw_buffer_sizes.find(t);
return (s != _raw_buffer_sizes.end()) ? s->second : 0;
}
void
JACKAudioBackend::setup_jack_startup_command (bool for_latency_measurement)
{
/* first we map the parameters that have been set onto a
* JackCommandLineOptions object.
*/
JackCommandLineOptions options;
get_jack_default_server_path (options.server_path);
options.driver = _target_driver;
options.samplerate = _target_sample_rate;
options.period_size = _target_buffer_size;
2015-12-05 07:56:18 -05:00
options.num_periods = _target_num_periods;
options.input_device = _target_device;
options.output_device = _target_device;
if (for_latency_measurement) {
options.input_latency = 0;
options.output_latency = 0;
} else {
options.input_latency = _target_systemic_input_latency;
options.output_latency = _target_systemic_output_latency;
}
options.input_channels = _target_input_channels;
options.output_channels = _target_output_channels;
if (_target_sample_format == FormatInt16) {
options.force16_bit = _target_sample_format;
}
options.realtime = true;
options.ports_max = 2048;
2015-10-05 10:17:49 -04:00
ARDOUR::set_midi_option (options, _target_midi_option);
/* this must always be true for any server instance we start ourselves
*/
options.temporary = true;
string cmdline;
if (!get_jack_command_line_string (options, cmdline)) {
2013-09-13 10:19:01 -04:00
/* error, somehow - we will still try to start JACK
* automatically but it will be without our preferred options
*/
std::cerr << "get_jack_command_line_string () failed: using default settings." << std::endl;
return;
}
std::cerr << "JACK command line will be: " << cmdline << std::endl;
write_jack_config_file (get_jack_server_user_config_file_path(), cmdline);
}
/* ---- BASIC STATE CONTROL API: start/stop/pause/freewheel --- */
int
JACKAudioBackend::_start (bool for_latency_measurement)
{
if (!available()) {
if (_jack_connection->in_control()) {
/* we will be starting JACK, so set up the
command that JACK will use when it (auto-)starts
*/
setup_jack_startup_command (for_latency_measurement);
}
if (_jack_connection->open ()) {
return -1;
}
2013-07-30 17:48:57 -04:00
}
2015-10-05 10:17:49 -04:00
2013-07-30 17:48:57 -04:00
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
/* get the buffer size and sample rates established */
jack_sample_rate_callback (jack_get_sample_rate (_priv_jack));
jack_bufsize_callback (jack_get_buffer_size (_priv_jack));
2015-10-05 10:17:49 -04:00
/* Now that we have buffer size and sample rate established, the engine
can go ahead and do its stuff
*/
if (engine.reestablish_ports ()) {
error << _("Could not re-establish ports after connecting to JACK") << endmsg;
return -1;
}
2013-07-30 17:48:57 -04:00
if (!jack_port_type_get_buffer_size) {
warning << _("This version of JACK is old - you should upgrade to a newer version that supports jack_port_type_get_buffer_size()") << endmsg;
}
2015-10-05 10:17:49 -04:00
2013-07-30 17:48:57 -04:00
set_jack_callbacks ();
2015-10-05 10:17:49 -04:00
2013-07-30 17:48:57 -04:00
if (jack_activate (_priv_jack) == 0) {
_running = true;
} else {
// error << _("cannot activate JACK client") << endmsg;
}
2013-07-30 17:48:57 -04:00
engine.reconnect_ports ();
return 0;
}
int
JACKAudioBackend::stop ()
{
_running = false; // no 'engine halted message'.
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
2015-10-05 10:17:49 -04:00
2013-07-30 17:48:57 -04:00
_jack_connection->close ();
2013-07-30 17:48:57 -04:00
_current_buffer_size = 0;
_current_sample_rate = 0;
_raw_buffer_sizes.clear();
return 0;
}
int
JACKAudioBackend::freewheel (bool onoff)
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
if (onoff == _freewheeling) {
/* already doing what has been asked for */
2015-10-05 10:17:49 -04:00
return 0;
}
int ret;
JACK_SERVER_CALL (ret = jack_set_freewheel (_priv_jack, onoff));
if (ret == 0) {
2013-10-12 11:07:57 -04:00
_freewheeling = onoff;
2013-07-30 17:48:57 -04:00
return 0;
}
2013-07-30 17:48:57 -04:00
return -1;
}
/* --- TRANSPORT STATE MANAGEMENT --- */
void
JACKAudioBackend::transport_stop ()
{
GET_PRIVATE_JACK_POINTER (_priv_jack);
jack_transport_stop (_priv_jack);
}
void
JACKAudioBackend::transport_start ()
{
GET_PRIVATE_JACK_POINTER (_priv_jack);
jack_transport_start (_priv_jack);
}
void
JACKAudioBackend::transport_locate (samplepos_t where)
{
GET_PRIVATE_JACK_POINTER (_priv_jack);
jack_transport_locate (_priv_jack, where);
}
samplepos_t
JACKAudioBackend::transport_sample () const
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
return jack_get_current_transport_frame (_priv_jack);
}
2013-07-30 17:48:57 -04:00
TransportState
JACKAudioBackend::transport_state () const
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, ((TransportState) JackTransportStopped));
jack_position_t pos;
return (TransportState) jack_transport_query (_priv_jack, &pos);
}
int
JACKAudioBackend::set_time_master (bool yn)
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
if (yn) {
JACK_SERVER_CALL (return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this));
} else {
JACK_SERVER_CALL (return jack_release_timebase (_priv_jack));
}
}
/* process-time */
bool
JACKAudioBackend::get_sync_offset (pframes_t& offset) const
{
#ifdef HAVE_JACK_VIDEO_SUPPORT
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
jack_position_t pos;
if (_priv_jack) {
(void) jack_transport_query (_priv_jack, &pos);
if (pos.valid & JackVideoFrameOffset) {
offset = pos.video_offset;
return true;
}
}
#else
/* keep gcc happy */
offset = 0;
#endif
return false;
}
samplepos_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::sample_time ()
{
2013-07-30 17:48:57 -04:00
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
return jack_frame_time (_priv_jack);
}
samplepos_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::sample_time_at_cycle_start ()
{
2013-07-30 17:48:57 -04:00
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
return jack_last_frame_time (_priv_jack);
}
pframes_t
2013-07-30 17:48:57 -04:00
JACKAudioBackend::samples_since_cycle_start ()
{
2013-07-30 17:48:57 -04:00
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
return jack_frames_since_cycle_start (_priv_jack);
}
/* JACK Callbacks */
static void
ardour_jack_error (const char* msg)
{
error << "JACK: " << msg << endmsg;
}
void
JACKAudioBackend::set_jack_callbacks ()
{
GET_PRIVATE_JACK_POINTER (_priv_jack);
/* no need to set the thread_init_callback because we use the
* non-callback API, and run the thread init callback in our own code.
*/
2013-07-30 17:48:57 -04:00
JACK_SERVER_CALL (jack_set_process_thread (_priv_jack, _process_thread, this));
JACK_SERVER_CALL (jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this));
JACK_SERVER_CALL (jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this));
JACK_SERVER_CALL (jack_set_xrun_callback (_priv_jack, _xrun_callback, this));
JACK_SERVER_CALL (jack_set_sync_callback (_priv_jack, _jack_sync_callback, this));
JACK_SERVER_CALL (jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this));
#ifdef HAVE_JACK_SESSION
if (jack_set_session_callback) {
JACK_SERVER_CALL (jack_set_session_callback (_priv_jack, _session_callback, this));
}
#endif
if (jack_set_latency_callback) {
JACK_SERVER_CALL (jack_set_latency_callback (_priv_jack, _latency_callback, this));
}
jack_set_error_function (ardour_jack_error);
}
void
JACKAudioBackend::_jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
jack_position_t* pos, int new_position, void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack timebase callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
static_cast<JACKAudioBackend*> (arg)->jack_timebase_callback (state, nframes, pos, new_position);
}
void
JACKAudioBackend::jack_timebase_callback (jack_transport_state_t state, pframes_t nframes,
jack_position_t* pos, int new_position)
{
ARDOUR::Session* session = engine.session();
2013-07-30 17:48:57 -04:00
if (session) {
JACKSession jsession (session);
jsession.timebase_callback (state, nframes, pos, new_position);
}
}
int
JACKAudioBackend::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack sync callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
return static_cast<JACKAudioBackend*> (arg)->jack_sync_callback (state, pos);
}
int
JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
{
2013-07-30 17:48:57 -04:00
TransportState tstate;
bool tstate_valid = true;
2013-07-30 17:48:57 -04:00
switch (state) {
case JackTransportRolling:
tstate = TransportRolling;
break;
case JackTransportLooping:
tstate = TransportLooping;
break;
case JackTransportStarting:
tstate = TransportStarting;
break;
case JackTransportStopped:
tstate = TransportStopped;
break;
default:
// ignore "unofficial" states like JackTransportNetStarting (jackd2)
tstate_valid = false;
break;
}
if (tstate_valid) {
return engine.sync_callback (tstate, pos->frame);
}
2013-07-30 17:48:57 -04:00
return true;
}
int
JACKAudioBackend::_xrun_callback (void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack sync callback\n", DEBUG_THREAD_SELF, pthread_name()));
JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
if (jab->available()) {
jab->engine.Xrun (); /* EMIT SIGNAL */
}
return 0;
}
void
JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack session callback\n", DEBUG_THREAD_SELF, pthread_name()));
JACKAudioBackend* jab = static_cast<JACKAudioBackend*> (arg);
ARDOUR::Session* session = jab->engine.session();
if (session) {
JACKSession jsession (session);
jsession.session_event (event);
}
}
void
JACKAudioBackend::_freewheel_callback (int onoff, void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack freewheel callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
static_cast<JACKAudioBackend*>(arg)->freewheel_callback (onoff);
}
void
JACKAudioBackend::freewheel_callback (int onoff)
{
_freewheeling = onoff;
2013-07-30 17:48:57 -04:00
engine.freewheel_callback (onoff);
}
void
JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack latency callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
return static_cast<JACKAudioBackend*> (arg)->jack_latency_callback (mode);
}
int
JACKAudioBackend::create_process_thread (boost::function<void()> f)
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
jack_native_thread_t thread_id;
ThreadData* td = new ThreadData (this, f, thread_stack_size());
if (jack_client_create_thread (_priv_jack, &thread_id, jack_client_real_time_priority (_priv_jack),
jack_is_realtime (_priv_jack), _start_process_thread, td)) {
return -1;
}
_jack_threads.push_back(thread_id);
return 0;
}
int
JACKAudioBackend::join_process_threads ()
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
int ret = 0;
for (auto & thread : _jack_threads) {
#if defined(USING_JACK2_EXPANSION_OF_JACK_API) || defined __jack_systemdeps_h__
// jack_client is not used by JACK2's implementation
// also jack_client_close() leaves threads active
if (jack_client_stop_thread (_priv_jack, thread) != 0)
#else
void* status;
if (pthread_join (thread, &status) != 0)
#endif
{
error << "AudioEngine: cannot stop process thread" << endmsg;
ret += -1;
}
}
_jack_threads.clear();
return ret;
}
bool
JACKAudioBackend::in_process_thread ()
{
2015-07-04 05:49:44 -04:00
#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__)
if (_main_thread == GetCurrentThread()) {
return true;
}
#else // pthreads
if (pthread_equal (_main_thread, pthread_self()) != 0) {
return true;
}
#endif
for (auto & thread : _jack_threads) {
2015-07-04 05:49:44 -04:00
#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__)
if (thread == GetCurrentThread()) {
return true;
}
#else // pthreads
if (pthread_equal (thread, pthread_self()) != 0) {
return true;
}
#endif
}
return false;
}
uint32_t
JACKAudioBackend::process_thread_count ()
{
return _jack_threads.size();
}
int
JACKAudioBackend::client_real_time_priority ()
{
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
return jack_client_real_time_priority (_priv_jack);
}
void*
JACKAudioBackend::_start_process_thread (void* arg)
{
ThreadData* td = reinterpret_cast<ThreadData*> (arg);
boost::function<void()> f = td->f;
delete td;
f ();
return 0;
}
void*
JACKAudioBackend::_process_thread (void *arg)
{
2013-07-30 17:48:57 -04:00
return static_cast<JACKAudioBackend*> (arg)->process_thread ();
}
void*
JACKAudioBackend::process_thread ()
{
/* JACK doesn't do this for us when we use the wait API
*/
2015-07-04 05:49:44 -04:00
#if defined COMPILER_MINGW && (!defined PTW32_VERSION || defined __jack_systemdeps_h__)
2014-10-22 17:57:55 -04:00
_main_thread = GetCurrentThread();
#else
_main_thread = pthread_self ();
2014-10-22 17:57:55 -04:00
#endif
2013-07-30 17:48:57 -04:00
AudioEngine::thread_init_callback (this);
while (1) {
GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
2021-06-12 11:20:28 -04:00
dsp_stats[AudioBackend::DeviceWait].start ();
pframes_t nframes = jack_cycle_wait (_priv_jack);
2021-06-12 11:20:28 -04:00
dsp_stats[AudioBackend::DeviceWait].update ();
2021-06-13 13:11:03 -04:00
dsp_stats[RunLoop].start();
if (engine.process_callback (nframes)) {
return 0;
}
jack_cycle_signal (_priv_jack, 0);
2021-06-13 13:11:03 -04:00
dsp_stats[AudioBackend::RunLoop].update ();
}
return 0;
}
int
JACKAudioBackend::_sample_rate_callback (pframes_t nframes, void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack sample rate callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
return static_cast<JACKAudioBackend*> (arg)->jack_sample_rate_callback (nframes);
}
int
JACKAudioBackend::jack_sample_rate_callback (pframes_t nframes)
{
2013-07-30 17:48:57 -04:00
_current_sample_rate = nframes;
return engine.sample_rate_change (nframes);
}
void
JACKAudioBackend::jack_latency_callback (jack_latency_callback_mode_t mode)
{
2013-07-30 17:48:57 -04:00
engine.latency_callback (mode == JackPlaybackLatency);
}
int
JACKAudioBackend::_bufsize_callback (pframes_t nframes, void *arg)
{
DEBUG_TRACE (DEBUG::BackendCallbacks, string_compose ("%1/%2 jack buffer size callback\n", DEBUG_THREAD_SELF, pthread_name()));
2013-07-30 17:48:57 -04:00
return static_cast<JACKAudioBackend*> (arg)->jack_bufsize_callback (nframes);
}
int
JACKAudioBackend::jack_bufsize_callback (pframes_t nframes)
{
/* if the size has not changed, this should be a no-op */
if (nframes == _current_buffer_size) {
return 0;
}
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 1);
_current_buffer_size = nframes;
if (jack_port_type_get_buffer_size) {
_raw_buffer_sizes[DataType::AUDIO] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_AUDIO_TYPE);
_raw_buffer_sizes[DataType::MIDI] = jack_port_type_get_buffer_size (_priv_jack, JACK_DEFAULT_MIDI_TYPE);
} else {
/* Old version of JACK.
These crude guesses, see below where we try to get the right answers.
Note that our guess for MIDI deliberatey tries to overestimate
by a little. It would be nicer if we could get the actual
size from a port, but we have to use this estimate in the
event that there are no MIDI ports currently. If there are
the value will be adjusted below.
*/
_raw_buffer_sizes[DataType::AUDIO] = nframes * sizeof (Sample);
_raw_buffer_sizes[DataType::MIDI] = nframes * 4 - (nframes/2);
}
2013-07-30 17:48:57 -04:00
engine.buffer_size_change (nframes);
return 0;
}
void
2013-07-30 17:48:57 -04:00
JACKAudioBackend::disconnected (const char* why)
{
2013-07-30 17:48:57 -04:00
bool was_running = _running;
2013-07-30 17:48:57 -04:00
_running = false;
_current_buffer_size = 0;
_current_sample_rate = 0;
if (was_running) {
2013-07-30 17:48:57 -04:00
engine.halted_callback (why); /* EMIT SIGNAL */
}
}
float
JACKAudioBackend::dsp_load() const
{
2013-07-30 17:48:57 -04:00
GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
return jack_cpu_load (_priv_jack);
}
void
JACKAudioBackend::update_latencies ()
{
GET_PRIVATE_JACK_POINTER (_priv_jack);
JACK_SERVER_CALL (jack_recompute_total_latencies (_priv_jack));
}
ChanCount
JACKAudioBackend::n_physical (unsigned long flags) const
{
ChanCount c;
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, c);
const char ** ports = jack_get_ports (_priv_jack, NULL, NULL, JackPortIsPhysical | flags);
if (ports) {
for (uint32_t i = 0; ports[i]; ++i) {
if (!strstr (ports[i], "Midi-Through")) {
std::shared_ptr<JackPort> jp (new JackPort (jack_port_by_name (_priv_jack, ports[i])));
DataType t = port_data_type (jp);
if (t != DataType::NIL) {
c.set (t, c.get (t) + 1);
}
}
}
2015-10-05 10:17:49 -04:00
jack_free (ports);
}
return c;
}
bool
JACKAudioBackend::can_change_sample_rate_when_running () const
{
return false;
}
bool
JACKAudioBackend::can_change_buffer_size_when_running () const
{
return true;
}
string
JACKAudioBackend::control_app_name () const
{
/* Since JACK/ALSA really don't provide particularly integrated support
for the idea of a control app to be used to control a device,
allow the user to take some control themselves if necessary.
*/
const char* env_value = g_getenv ("ARDOUR_DEVICE_CONTROL_APP");
string appname;
if (!env_value) {
if (_target_driver.empty() || _target_device.empty()) {
return appname;
}
2015-10-05 10:17:49 -04:00
if (_target_driver == "ALSA") {
2015-10-05 10:17:49 -04:00
if (_target_device == "Hammerfall DSP") {
appname = "hdspconf";
} else if (_target_device == "M Audio Delta 1010") {
2013-09-09 21:35:47 -04:00
appname = "mudita24";
} else if (_target_device == "M2496") {
appname = "mudita24";
}
2013-09-09 17:10:33 -04:00
}
} else {
appname = env_value;
}
return appname;
}
void
JACKAudioBackend::launch_control_app ()
{
string appname = control_app_name();
if (appname.empty()) {
error << string_compose (_("There is no control application for the device \"%1\""), _target_device) << endmsg;
return;
}
std::list<string> args;
args.push_back (appname);
Glib::spawn_async ("", args, Glib::SPAWN_SEARCH_PATH);
}
vector<string>
JACKAudioBackend::enumerate_midi_options () const
{
return ARDOUR::enumerate_midi_options ();
}
int
JACKAudioBackend::set_midi_option (const string& opt)
{
_target_midi_option = opt;
return 0;
}
bool
JACKAudioBackend::speed_and_position (double& speed, samplepos_t& position)
{
jack_position_t pos;
jack_transport_state_t state;
bool starting;
/* this won't be called if the port engine in use is not JACK, so we do
not have to worry about the type of PortEngine::private_handle()
*/
speed = 0;
position = 0;
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, true);
state = jack_transport_query (_priv_jack, &pos);
switch (state) {
case JackTransportStopped:
speed = 0;
starting = false;
break;
case JackTransportRolling:
speed = 1.0;
starting = false;
break;
case JackTransportLooping:
speed = 1.0;
starting = false;
break;
case JackTransportStarting:
starting = true;
// don't adjust speed here, just leave it as it was
break;
default:
2014-03-22 21:26:37 -04:00
starting = true; // jack2: JackTransportNetStarting
std::cerr << "WARNING: Unknown JACK transport state: " << state << std::endl;
}
position = pos.frame;
DEBUG_TRACE (DEBUG::Slave, string_compose ("JACK transport: speed %1 position %2 starting %3\n", speed, position, starting));
return starting;
}
int
JACKAudioBackend::reset_device ()
{
/* XXX need to figure out what this means for JACK
*/
return 0;
}