summaryrefslogtreecommitdiff
path: root/libs/backends/jack/jack_audiobackend.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/backends/jack/jack_audiobackend.cc')
-rw-r--r--libs/backends/jack/jack_audiobackend.cc1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/libs/backends/jack/jack_audiobackend.cc b/libs/backends/jack/jack_audiobackend.cc
new file mode 100644
index 0000000000..e004849896
--- /dev/null
+++ b/libs/backends/jack/jack_audiobackend.cc
@@ -0,0 +1,1002 @@
+/*
+ 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 <string>
+#include <list>
+#include <math.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <glibmm/timer.h>
+#include <glibmm/spawn.h>
+
+#include "pbd/error.h"
+
+#include "jack/jack.h"
+#include "jack/thread.h"
+
+#include "ardour/audioengine.h"
+#include "ardour/session.h"
+#include "ardour/types.h"
+
+#include "jack_audiobackend.h"
+#include "jack_connection.h"
+#include "jack_portengine.h"
+#include "jack_utils.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+#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; }
+
+JACKAudioBackend::JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection> jc)
+ : AudioBackend (e)
+ , _jack_connection (jc)
+ , _running (false)
+ , _freewheeling (false)
+ , _target_sample_rate (48000)
+ , _target_buffer_size (1024)
+ , _target_sample_format (FormatFloat)
+ , _target_interleaved (false)
+ , _target_input_channels (-1)
+ , _target_output_channels (-1)
+ , _target_systemic_input_latency (0)
+ , _target_systemic_output_latency (0)
+ , _current_sample_rate (0)
+ , _current_buffer_size (0)
+{
+ _jack_connection->Disconnected.connect_same_thread (disconnect_connection, boost::bind (&JACKAudioBackend::disconnected, this, _1));
+}
+
+JACKAudioBackend::~JACKAudioBackend()
+{
+}
+
+string
+JACKAudioBackend::name() const
+{
+ return X_("JACK");
+}
+
+void*
+JACKAudioBackend::private_handle() const
+{
+ return _jack_connection->jack();
+}
+
+bool
+JACKAudioBackend::connected() const
+{
+ return (private_handle() != 0);
+}
+
+bool
+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>()));
+ }
+
+ /* 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);
+ }
+
+ 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));
+ }
+ }
+
+ return statuses;
+}
+
+vector<float>
+JACKAudioBackend::available_sample_rates (const string& /*device*/) const
+{
+ vector<float> f;
+
+ if (connected()) {
+ 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);
+
+ return f;
+}
+
+vector<uint32_t>
+JACKAudioBackend::available_buffer_sizes (const string& /*device*/) const
+{
+ vector<uint32_t> s;
+
+ if (connected()) {
+ 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;
+}
+
+uint32_t
+JACKAudioBackend::available_input_channel_count (const string& /*device*/) const
+{
+ return 128;
+}
+
+uint32_t
+JACKAudioBackend::available_output_channel_count (const string& /*device*/) const
+{
+ return 128;
+}
+
+/* -- parameter setting -- */
+
+int
+JACKAudioBackend::set_device_name (const string& dev)
+{
+ if (connected()) {
+ /* 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 (!connected()) {
+ _target_sample_rate = sr;
+ return 0;
+ }
+
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (sr == jack_get_sample_rate (_priv_jack)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+JACKAudioBackend::set_buffer_size (uint32_t nframes)
+{
+ if (!connected()) {
+ _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_sample_format (SampleFormat sf)
+{
+ /* as far as JACK clients are concerned, the hardware is always
+ * floating point format.
+ */
+ if (sf == FormatFloat) {
+ return 0;
+ }
+ return -1;
+}
+
+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 (connected()) {
+ return -1;
+ }
+
+ _target_input_channels = cnt;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_output_channels (uint32_t cnt)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_output_channels = cnt;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_systemic_input_latency (uint32_t l)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_systemic_input_latency = l;
+
+ return 0;
+}
+
+int
+JACKAudioBackend::set_systemic_output_latency (uint32_t l)
+{
+ if (connected()) {
+ return -1;
+ }
+
+ _target_systemic_output_latency = l;
+
+ return 0;
+}
+
+/* --- Parameter retrieval --- */
+
+std::string
+JACKAudioBackend::device_name () const
+{
+ if (connected()) {
+ return "???";
+ }
+
+ return _target_device;
+}
+
+float
+JACKAudioBackend::sample_rate () const
+{
+ if (connected()) {
+ return _current_sample_rate;
+ }
+ return _target_sample_rate;
+}
+
+uint32_t
+JACKAudioBackend::buffer_size () const
+{
+ if (connected()) {
+ return _current_buffer_size;
+ }
+ return _target_buffer_size;
+}
+
+SampleFormat
+JACKAudioBackend::sample_format () const
+{
+ return FormatFloat;
+}
+
+bool
+JACKAudioBackend::interleaved () const
+{
+ return false;
+}
+
+uint32_t
+JACKAudioBackend::input_channels () const
+{
+ if (connected()) {
+ return n_physical (JackPortIsInput).n_audio();
+ }
+ return _target_input_channels;
+}
+
+uint32_t
+JACKAudioBackend::output_channels () const
+{
+ if (connected()) {
+ return n_physical (JackPortIsOutput).n_audio();
+ }
+ return _target_output_channels;
+}
+
+uint32_t
+JACKAudioBackend::systemic_input_latency () const
+{
+ return _target_systemic_output_latency;
+}
+
+uint32_t
+JACKAudioBackend::systemic_output_latency () const
+{
+ return _target_systemic_output_latency;
+}
+
+size_t
+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 ()
+{
+ /* 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;
+ options.num_periods = 2;
+ options.input_device = _target_device;
+ options.output_device = _target_device;
+ 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;
+
+ /* 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)) {
+ /* error, somehow */
+ 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 ()
+{
+ if (!connected()) {
+
+ if (!_jack_connection->server_running()) {
+ setup_jack_startup_command ();
+ }
+
+ if (_jack_connection->open ()) {
+ return -1;
+ }
+ }
+
+ 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));
+
+ /* Now that we have buffer size and sample rate established, the engine
+ can go ahead and do its stuff
+ */
+
+ engine.reestablish_ports ();
+
+ 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;
+ }
+
+ set_jack_callbacks ();
+
+ if (jack_activate (_priv_jack) == 0) {
+ _running = true;
+ } else {
+ // error << _("cannot activate JACK client") << endmsg;
+ }
+
+ engine.reconnect_ports ();
+
+ return 0;
+}
+
+int
+JACKAudioBackend::stop ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ _jack_connection->close ();
+
+ _current_buffer_size = 0;
+ _current_sample_rate = 0;
+
+ _raw_buffer_sizes.clear();
+
+ return 0;
+}
+
+int
+JACKAudioBackend::pause ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ if (_priv_jack) {
+ jack_deactivate (_priv_jack);
+ }
+
+ 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 */
+
+ return 0;
+ }
+
+ if (jack_set_freewheel (_priv_jack, onoff) == 0) {
+ _freewheeling = true;
+ return 0;
+ }
+
+ 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 (framepos_t where)
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ jack_transport_locate (_priv_jack, where);
+}
+
+framepos_t
+JACKAudioBackend::transport_frame () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_get_current_transport_frame (_priv_jack);
+}
+
+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) {
+ return jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this);
+ } else {
+ 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;
+}
+
+pframes_t
+JACKAudioBackend::sample_time ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_frame_time (_priv_jack);
+}
+
+pframes_t
+JACKAudioBackend::sample_time_at_cycle_start ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_last_frame_time (_priv_jack);
+}
+
+pframes_t
+JACKAudioBackend::samples_since_cycle_start ()
+{
+ 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);
+
+ jack_set_thread_init_callback (_priv_jack, AudioEngine::thread_init_callback, 0);
+
+ jack_set_process_thread (_priv_jack, _process_thread, this);
+ jack_set_sample_rate_callback (_priv_jack, _sample_rate_callback, this);
+ jack_set_buffer_size_callback (_priv_jack, _bufsize_callback, this);
+ jack_set_xrun_callback (_priv_jack, _xrun_callback, this);
+ jack_set_sync_callback (_priv_jack, _jack_sync_callback, this);
+ jack_set_freewheel_callback (_priv_jack, _freewheel_callback, this);
+
+#ifdef HAVE_JACK_SESSION
+ if( jack_set_session_callback)
+ jack_set_session_callback (_priv_jack, _session_callback, this);
+#endif
+
+ if (jack_set_latency_callback) {
+ 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)
+{
+ 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();
+
+ if (session) {
+ session->jack_timebase_callback (state, nframes, pos, new_position);
+ }
+}
+
+int
+JACKAudioBackend::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_sync_callback (state, pos);
+}
+
+int
+JACKAudioBackend::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
+{
+ TransportState tstate;
+
+ switch (state) {
+ case JackTransportStopped:
+ tstate = TransportStopped;
+ break;
+ case JackTransportRolling:
+ tstate = TransportRolling;
+ break;
+ case JackTransportLooping:
+ tstate = TransportLooping;
+ break;
+ case JackTransportStarting:
+ tstate = TransportStarting;
+ break;
+ }
+
+ return engine.sync_callback (tstate, pos->frame);
+
+ return true;
+}
+
+int
+JACKAudioBackend::_xrun_callback (void *arg)
+{
+ JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
+ if (ae->connected()) {
+ ae->engine.Xrun (); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+#ifdef HAVE_JACK_SESSION
+void
+JACKAudioBackend::_session_callback (jack_session_event_t *event, void *arg)
+{
+ JACKAudioBackend* ae = static_cast<JACKAudioBackend*> (arg);
+ ARDOUR::Session* session = ae->engine.session();
+
+ if (session) {
+ session->jack_session_event (event);
+ }
+}
+#endif
+
+void
+JACKAudioBackend::_freewheel_callback (int onoff, void *arg)
+{
+ static_cast<JACKAudioBackend*>(arg)->freewheel_callback (onoff);
+}
+
+void
+JACKAudioBackend::freewheel_callback (int onoff)
+{
+ _freewheeling = onoff;
+ engine.freewheel_callback (onoff);
+}
+
+void
+JACKAudioBackend::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_latency_callback (mode);
+}
+
+int
+JACKAudioBackend::create_process_thread (boost::function<void()> f, pthread_t* thread, size_t stacksize)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ ThreadData* td = new ThreadData (this, f, stacksize);
+
+ if (jack_client_create_thread (_priv_jack, thread, jack_client_real_time_priority (_priv_jack),
+ jack_is_realtime (_priv_jack), _start_process_thread, td)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+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)
+{
+ return static_cast<JACKAudioBackend*> (arg)->process_thread ();
+}
+
+void*
+JACKAudioBackend::process_thread ()
+{
+ /* JACK doesn't do this for us when we use the wait API
+ */
+
+ AudioEngine::thread_init_callback (this);
+
+ while (1) {
+ GET_PRIVATE_JACK_POINTER_RET(_priv_jack,0);
+
+ pframes_t nframes = jack_cycle_wait (_priv_jack);
+
+ if (engine.process_callback (nframes)) {
+ return 0;
+ }
+
+ jack_cycle_signal (_priv_jack, 0);
+ }
+
+ return 0;
+}
+
+int
+JACKAudioBackend::_sample_rate_callback (pframes_t nframes, void *arg)
+{
+ return static_cast<JACKAudioBackend*> (arg)->jack_sample_rate_callback (nframes);
+}
+
+int
+JACKAudioBackend::jack_sample_rate_callback (pframes_t nframes)
+{
+ _current_sample_rate = nframes;
+ return engine.sample_rate_change (nframes);
+}
+
+void
+JACKAudioBackend::jack_latency_callback (jack_latency_callback_mode_t mode)
+{
+ engine.latency_callback (mode == JackPlaybackLatency);
+}
+
+int
+JACKAudioBackend::_bufsize_callback (pframes_t nframes, void *arg)
+{
+ 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);
+ }
+
+ engine.buffer_size_change (nframes);
+
+ return 0;
+}
+
+void
+JACKAudioBackend::disconnected (const char* why)
+{
+ bool was_running = _running;
+
+ _running = false;
+ _current_buffer_size = 0;
+ _current_sample_rate = 0;
+
+ if (was_running) {
+ engine.halted_callback (why); /* EMIT SIGNAL */
+ }
+}
+float
+JACKAudioBackend::cpu_load() const
+{
+ 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_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")) {
+ DataType t (jack_port_type (jack_port_by_name (_priv_jack, ports[i])));
+ c.set (t, c.get (t) + 1);
+ }
+ }
+
+ 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;
+ }
+
+ if (_target_driver == "ALSA") {
+
+ if (_target_device == "Hammerfall DSP") {
+ appname = "hdspconf";
+ } else if (_target_device == "M Audio Delta 1010") {
+ appname = "mudita24";
+ }
+ }
+ } 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);
+}