summaryrefslogtreecommitdiff
path: root/libs/backends
diff options
context:
space:
mode:
Diffstat (limited to 'libs/backends')
-rw-r--r--libs/backends/jack/jack_api.cc102
-rw-r--r--libs/backends/jack/jack_audiobackend.cc1002
-rw-r--r--libs/backends/jack/jack_audiobackend.h190
-rw-r--r--libs/backends/jack/jack_connection.cc164
-rw-r--r--libs/backends/jack/jack_connection.h40
-rw-r--r--libs/backends/jack/jack_portengine.cc569
-rw-r--r--libs/backends/jack/jack_portengine.h132
-rw-r--r--libs/backends/jack/jack_utils.cc897
-rw-r--r--libs/backends/jack/jack_utils.h235
-rw-r--r--libs/backends/jack/wscript51
-rw-r--r--libs/backends/wscript27
11 files changed, 3409 insertions, 0 deletions
diff --git a/libs/backends/jack/jack_api.cc b/libs/backends/jack/jack_api.cc
new file mode 100644
index 0000000000..0136161181
--- /dev/null
+++ b/libs/backends/jack/jack_api.cc
@@ -0,0 +1,102 @@
+/*
+ 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 "jack_connection.h"
+#include "jack_audiobackend.h"
+#include "jack_portengine.h"
+
+using namespace ARDOUR;
+
+static boost::shared_ptr<JACKAudioBackend> backend;
+static boost::shared_ptr<JACKPortEngine> port_engine;
+static boost::shared_ptr<JackConnection> jack_connection;
+
+static boost::shared_ptr<AudioBackend>
+backend_factory (AudioEngine& ae)
+{
+ if (!jack_connection) {
+ return boost::shared_ptr<AudioBackend>();
+ }
+
+ if (!backend) {
+ backend.reset (new JACKAudioBackend (ae, jack_connection));
+ }
+
+ return backend;
+}
+
+static boost::shared_ptr<PortEngine>
+portengine_factory (PortManager& pm)
+{
+ if (!jack_connection) {
+ return boost::shared_ptr<PortEngine>();
+ }
+
+ if (!port_engine) {
+ port_engine.reset (new JACKPortEngine (pm, jack_connection));
+ }
+
+ return port_engine;
+}
+
+static int
+instantiate (const std::string& arg1, const std::string& arg2)
+{
+ try {
+ jack_connection.reset (new JackConnection (arg1, arg2));
+ } catch (...) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+deinstantiate ()
+{
+ port_engine.reset ();
+ backend.reset ();
+ jack_connection.reset ();
+
+ return 0;
+}
+
+static bool
+already_configured ()
+{
+ return JackConnection::server_running ();
+}
+
+extern "C" {
+
+
+ /* functions looked up using dlopen-and-cousins, and so naming scope
+ * must be non-mangled.
+ */
+
+ ARDOUR::AudioBackendInfo descriptor = {
+ "JACK",
+ instantiate,
+ deinstantiate,
+ backend_factory,
+ portengine_factory,
+ already_configured,
+ };
+}
+
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);
+}
diff --git a/libs/backends/jack/jack_audiobackend.h b/libs/backends/jack/jack_audiobackend.h
new file mode 100644
index 0000000000..1389e15c4a
--- /dev/null
+++ b/libs/backends/jack/jack_audiobackend.h
@@ -0,0 +1,190 @@
+/*
+ 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.
+
+*/
+
+#ifndef __libardour_jack_audiobackend_h__
+#define __libardour_jack_audiobackend_h__
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+#include <stdint.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <jack/jack.h>
+#ifdef HAVE_JACK_SESSION
+#include <jack/session.h>
+#endif
+
+#include "ardour/audio_backend.h"
+
+namespace ARDOUR {
+
+class JackConnection;
+
+class JACKAudioBackend : public AudioBackend {
+ public:
+ JACKAudioBackend (AudioEngine& e, boost::shared_ptr<JackConnection>);
+ ~JACKAudioBackend ();
+
+ std::string name() const;
+ void* private_handle() const;
+ bool connected() const;
+ bool is_realtime () const;
+
+ bool requires_driver_selection() const;
+ std::vector<std::string> enumerate_drivers () const;
+ int set_driver (const std::string&);
+
+ std::vector<DeviceStatus> enumerate_devices () const;
+
+ std::vector<float> available_sample_rates (const std::string& device) const;
+ std::vector<uint32_t> available_buffer_sizes (const std::string& device) const;
+ uint32_t available_input_channel_count (const std::string& device) const;
+ uint32_t available_output_channel_count (const std::string& device) const;
+
+ bool can_change_sample_rate_when_running() const;
+ bool can_change_buffer_size_when_running() const;
+
+ int set_device_name (const std::string&);
+ int set_sample_rate (float);
+ int set_buffer_size (uint32_t);
+ int set_sample_format (SampleFormat);
+ int set_interleaved (bool yn);
+ int set_input_channels (uint32_t);
+ int set_output_channels (uint32_t);
+ int set_systemic_input_latency (uint32_t);
+ int set_systemic_output_latency (uint32_t);
+
+ std::string device_name () const;
+ float sample_rate () const;
+ uint32_t buffer_size () const;
+ SampleFormat sample_format () const;
+ bool interleaved () const;
+ uint32_t input_channels () const;
+ uint32_t output_channels () const;
+ uint32_t systemic_input_latency () const;
+ uint32_t systemic_output_latency () const;
+
+ std::string control_app_name () const;
+ void launch_control_app ();
+
+ int start ();
+ int stop ();
+ int pause ();
+ int freewheel (bool);
+
+ float cpu_load() const;
+
+ pframes_t sample_time ();
+ pframes_t sample_time_at_cycle_start ();
+ pframes_t samples_since_cycle_start ();
+
+ size_t raw_buffer_size (DataType t);
+
+ int create_process_thread (boost::function<void()> func, pthread_t*, size_t stacksize);
+
+ void transport_start ();
+ void transport_stop ();
+ void transport_locate (framepos_t /*pos*/);
+ TransportState transport_state () const;
+ framepos_t transport_frame() const;
+
+ int set_time_master (bool /*yn*/);
+ bool get_sync_offset (pframes_t& /*offset*/) const;
+
+ void update_latencies ();
+
+ static bool already_configured();
+
+ private:
+ boost::shared_ptr<JackConnection> _jack_connection; //< shared with JACKPortEngine
+ bool _running;
+ bool _freewheeling;
+ std::map<DataType,size_t> _raw_buffer_sizes;
+
+ static int _xrun_callback (void *arg);
+ static void* _process_thread (void *arg);
+ static int _sample_rate_callback (pframes_t nframes, void *arg);
+ static int _bufsize_callback (pframes_t nframes, void *arg);
+ static void _jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int, void*);
+ static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg);
+ static void _freewheel_callback (int , void *arg);
+ static void _latency_callback (jack_latency_callback_mode_t, void*);
+#ifdef HAVE_JACK_SESSION
+ static void _session_callback (jack_session_event_t *event, void *arg);
+#endif
+
+ void jack_timebase_callback (jack_transport_state_t, pframes_t, jack_position_t*, int);
+ int jack_sync_callback (jack_transport_state_t, jack_position_t*);
+ int jack_bufsize_callback (pframes_t);
+ int jack_sample_rate_callback (pframes_t);
+ void freewheel_callback (int);
+ int process_callback (pframes_t nframes);
+ void jack_latency_callback (jack_latency_callback_mode_t);
+ void disconnected (const char*);
+
+ void set_jack_callbacks ();
+ int reconnect_to_jack ();
+
+ struct ThreadData {
+ JACKAudioBackend* engine;
+ boost::function<void()> f;
+ size_t stacksize;
+
+ ThreadData (JACKAudioBackend* e, boost::function<void()> fp, size_t stacksz)
+ : engine (e) , f (fp) , stacksize (stacksz) {}
+ };
+
+ void* process_thread ();
+ static void* _start_process_thread (void*);
+
+ ChanCount n_physical (unsigned long) const;
+
+ void setup_jack_startup_command ();
+
+ /* pffooo */
+
+ std::string _target_driver;
+ std::string _target_device;
+ float _target_sample_rate;
+ uint32_t _target_buffer_size;
+ SampleFormat _target_sample_format;
+ bool _target_interleaved;
+ uint32_t _target_input_channels;
+ uint32_t _target_output_channels;
+ uint32_t _target_systemic_input_latency;
+ uint32_t _target_systemic_output_latency;
+ uint32_t _current_sample_rate;
+ uint32_t _current_buffer_size;
+
+ typedef std::set<std::string> DeviceList;
+ typedef std::map<std::string,DeviceList> DriverDeviceMap;
+
+ mutable DriverDeviceMap all_devices;
+
+ PBD::ScopedConnection disconnect_connection;
+};
+
+} // namespace
+
+#endif /* __ardour_audiobackend_h__ */
+
diff --git a/libs/backends/jack/jack_connection.cc b/libs/backends/jack/jack_connection.cc
new file mode 100644
index 0000000000..da0127c584
--- /dev/null
+++ b/libs/backends/jack/jack_connection.cc
@@ -0,0 +1,164 @@
+/*
+ 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 <boost/scoped_ptr.hpp>
+#include <jack/session.h>
+
+#include "pbd/epa.h"
+
+#include "jack_connection.h"
+#include "jack_utils.h"
+
+#define GET_PRIVATE_JACK_POINTER(j) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return; }
+#define GET_PRIVATE_JACK_POINTER_RET(j,r) jack_client_t* _priv_jack = (jack_client_t*) (j); if (!_priv_jack) { return r; }
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::string;
+using std::vector;
+
+static void jack_halted_callback (void* arg)
+{
+ JackConnection* jc = static_cast<JackConnection*> (arg);
+ jc->halted_callback ();
+}
+
+static void jack_halted_info_callback (jack_status_t code, const char* reason, void* arg)
+{
+ JackConnection* jc = static_cast<JackConnection*> (arg);
+ jc->halted_info_callback (code, reason);
+}
+
+
+JackConnection::JackConnection (const std::string& arg1, const std::string& arg2)
+ : _jack (0)
+ , _client_name (arg1)
+ , session_uuid (arg2)
+{
+}
+
+JackConnection::~JackConnection ()
+{
+ close ();
+}
+
+bool
+JackConnection::server_running ()
+{
+ EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
+ boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
+
+ /* revert all environment settings back to whatever they were when
+ * ardour started, because ardour's startup script may have reset
+ * something in ways that interfere with finding/starting JACK.
+ */
+
+ if (global_epa) {
+ current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
+ global_epa->restore ();
+ }
+
+ jack_status_t status;
+ jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
+
+ if (status == 0) {
+ jack_client_close (c);
+ return true;
+ }
+
+ return false;
+}
+
+int
+JackConnection::open ()
+{
+ EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
+ boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
+ jack_status_t status;
+
+ close ();
+
+ /* revert all environment settings back to whatever they were when ardour started
+ */
+
+ if (global_epa) {
+ current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
+ global_epa->restore ();
+ }
+
+ /* ensure that PATH or equivalent includes likely locations of the JACK
+ * server, in case the user's default does not.
+ */
+
+ vector<string> dirs;
+ get_jack_server_dir_paths (dirs);
+ set_path_env_for_jack_autostart (dirs);
+
+ if ((_jack = jack_client_open (_client_name.c_str(), JackSessionID, &status, session_uuid.c_str())) == 0) {
+ return -1;
+ }
+
+ if (status & JackNameNotUnique) {
+ _client_name = jack_get_client_name (_jack);
+ }
+
+ /* attach halted handler */
+
+ if (jack_on_info_shutdown) {
+ jack_on_info_shutdown (_jack, jack_halted_info_callback, this);
+ } else {
+ jack_on_shutdown (_jack, jack_halted_callback, this);
+ }
+
+
+ Connected(); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+JackConnection::close ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_jack, -1);
+
+ if (_priv_jack) {
+ int ret = jack_client_close (_priv_jack);
+ _jack = 0;
+ Disconnected (""); /* EMIT SIGNAL */
+ return ret;
+ }
+
+ return 0;
+}
+
+void
+JackConnection::halted_callback ()
+{
+ _jack = 0;
+ Disconnected ("");
+}
+
+void
+JackConnection::halted_info_callback (jack_status_t /*status*/, const char* reason)
+{
+ _jack = 0;
+ Disconnected (reason);
+}
+
+
diff --git a/libs/backends/jack/jack_connection.h b/libs/backends/jack/jack_connection.h
new file mode 100644
index 0000000000..cd45f3b9ba
--- /dev/null
+++ b/libs/backends/jack/jack_connection.h
@@ -0,0 +1,40 @@
+#ifndef __libardour_jack_connection_h__
+#define __libardour_jack_connection_h__
+
+#include <string>
+#include <jack/jack.h>
+
+#include "pbd/signals.h"
+
+namespace ARDOUR {
+
+class JackConnection {
+ public:
+ JackConnection (const std::string& client_name, const std::string& session_uuid);
+ ~JackConnection ();
+
+ const std::string& client_name() const { return _client_name; }
+
+ int open ();
+ int close ();
+ bool connected () const { return _jack != 0; }
+
+ jack_client_t* jack() const { return _jack; }
+
+ PBD::Signal0<void> Connected;
+ PBD::Signal1<void,const char*> Disconnected;
+
+ void halted_callback ();
+ void halted_info_callback (jack_status_t, const char*);
+
+ static bool server_running();
+
+ private:
+ jack_client_t* volatile _jack;
+ std::string _client_name;
+ std::string session_uuid;
+};
+
+} // namespace
+
+#endif /* __libardour_jack_connection_h__ */
diff --git a/libs/backends/jack/jack_portengine.cc b/libs/backends/jack/jack_portengine.cc
new file mode 100644
index 0000000000..bd352a2f11
--- /dev/null
+++ b/libs/backends/jack/jack_portengine.cc
@@ -0,0 +1,569 @@
+/*
+ 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.h>
+#include <stdint.h>
+
+#include "pbd/error.h"
+
+#include "jack_portengine.h"
+#include "jack_connection.h"
+
+#include "ardour/port_manager.h"
+
+#include "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; }
+
+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 "";
+}
+
+JACKPortEngine::JACKPortEngine (PortManager& pm, boost::shared_ptr<JackConnection> jc)
+ : PortEngine (pm)
+ , _jack_connection (jc)
+{
+ _jack_connection->Connected.connect_same_thread (jack_connection_connection, boost::bind (&JACKPortEngine::connected_to_jack, this));
+}
+
+JACKPortEngine::~JACKPortEngine ()
+{
+ /* a default destructor would do this, and so would this one,
+ but we'll make it explicit in case we ever need to debug
+ the lifetime of the JACKConnection
+ */
+ _jack_connection.reset ();
+}
+
+void
+JACKPortEngine::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_set_port_registration_callback (client, _registration_callback, this);
+ jack_set_port_connect_callback (client, _connect_callback, this);
+ jack_set_graph_order_callback (client, _graph_order_callback, this);
+}
+
+void*
+JACKPortEngine::private_handle() const
+{
+ return _jack_connection->jack();
+}
+
+bool
+JACKPortEngine::connected() const
+{
+ return _jack_connection->connected();
+}
+
+int
+JACKPortEngine::set_port_name (PortHandle port, const std::string& name)
+{
+ return jack_port_set_name ((jack_port_t*) port, name.c_str());
+}
+
+string
+JACKPortEngine::get_port_name (PortHandle port) const
+{
+ return jack_port_name ((jack_port_t*) port);
+}
+
+PortEngine::PortHandle*
+JACKPortEngine:: get_port_by_name (const std::string& name) const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return (PortHandle*) jack_port_by_name (_priv_jack, name.c_str());
+}
+
+void
+JACKPortEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* arg)
+{
+ static_cast<JACKPortEngine*> (arg)->manager.registration_callback ();
+}
+
+int
+JACKPortEngine::_graph_order_callback (void *arg)
+{
+ return static_cast<JACKPortEngine*> (arg)->manager.graph_order_callback ();
+}
+
+void
+JACKPortEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg)
+{
+ static_cast<JACKPortEngine*> (arg)->connect_callback (id_a, id_b, conn);
+}
+
+void
+JACKPortEngine::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
+JACKPortEngine::connected (PortHandle port, bool process_callback_safe)
+{
+ bool ret = false;
+
+ 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);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ ret = true;
+ }
+
+ jack_free (ports);
+
+ return ret;
+}
+
+bool
+JACKPortEngine::connected_to (PortHandle port, const std::string& other, bool process_callback_safe)
+{
+ bool ret = false;
+ 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);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ for (int i = 0; ports[i]; ++i) {
+ if (other == ports[i]) {
+ ret = true;
+ }
+ }
+ jack_free (ports);
+ }
+
+ return ret;
+}
+
+bool
+JACKPortEngine::physically_connected (PortHandle p, bool process_callback_safe)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, false);
+ jack_port_t* port = (jack_port_t*) p;
+
+ 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);
+ 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)) {
+ return true;
+ }
+ }
+ jack_free (ports);
+ }
+
+ return false;
+}
+
+int
+JACKPortEngine::get_connections (PortHandle port, vector<string>& s, bool process_callback_safe)
+{
+ const char** ports;
+
+ if (process_callback_safe) {
+ ports = jack_port_get_connections ((jack_port_t*)port);
+ } else {
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ ports = jack_port_get_all_connections (_priv_jack, (jack_port_t*)port);
+ }
+
+ if (ports) {
+ for (int i = 0; ports[i]; ++i) {
+ s.push_back (ports[i]);
+ }
+ jack_free (ports);
+ }
+
+ return s.size();
+}
+
+DataType
+JACKPortEngine::port_data_type (PortHandle p) const
+{
+ return jack_port_type_to_ardour_data_type (jack_port_type ((jack_port_t*) p));
+}
+
+const string&
+JACKPortEngine::my_name() const
+{
+ return _jack_connection->client_name();
+}
+
+bool
+JACKPortEngine::port_is_physical (PortHandle ph) const
+{
+ if (!ph) {
+ return false;
+ }
+
+ return jack_port_flags ((jack_port_t*) ph) & JackPortIsPhysical;
+}
+
+int
+JACKPortEngine::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
+JACKPortEngine::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 = port_data_type (jack_port_by_name (_priv_jack, ports[i]));
+ if (t != DataType::NIL) {
+ c.set (t, c.get (t) + 1);
+ }
+ }
+ }
+
+ jack_free (ports);
+ }
+
+ return c;
+}
+
+ChanCount
+JACKPortEngine::n_physical_inputs () const
+{
+ return n_physical (JackPortIsInput);
+}
+
+ChanCount
+JACKPortEngine::n_physical_outputs () const
+{
+ return n_physical (JackPortIsOutput);
+}
+
+void
+JACKPortEngine::get_physical (DataType type, unsigned long flags, vector<string>& phy) const
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ const char ** ports;
+
+ if ((ports = jack_get_ports (_priv_jack, NULL, ardour_data_type_to_jack_port_type (type), JackPortIsPhysical | flags)) == 0) {
+ return;
+ }
+
+ if (ports) {
+ for (uint32_t i = 0; ports[i]; ++i) {
+ if (strstr (ports[i], "Midi-Through")) {
+ continue;
+ }
+ phy.push_back (ports[i]);
+ }
+ jack_free (ports);
+ }
+}
+
+/** Get physical ports for which JackPortIsOutput is set; ie those that correspond to
+ * a physical input connector.
+ */
+void
+JACKPortEngine::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
+JACKPortEngine::get_physical_outputs (DataType type, vector<string>& outs)
+{
+ get_physical (type, JackPortIsInput, outs);
+}
+
+
+bool
+JACKPortEngine::can_monitor_input () const
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack,false);
+ const char ** ports;
+
+ if ((ports = jack_get_ports (_priv_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+ return false;
+ }
+
+ jack_free (ports);
+
+ return true;
+}
+
+int
+JACKPortEngine::request_input_monitoring (PortHandle port, bool yn)
+{
+ return jack_port_request_monitor ((jack_port_t*) port, yn);
+}
+int
+JACKPortEngine::ensure_input_monitoring (PortHandle port, bool yn)
+{
+ return jack_port_ensure_monitor ((jack_port_t*) port, yn);
+}
+bool
+JACKPortEngine::monitoring_input (PortHandle port)
+{
+ return jack_port_monitoring_input ((jack_port_t*) port);
+}
+
+
+pframes_t
+JACKPortEngine::sample_time_at_cycle_start ()
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_last_frame_time (_priv_jack);
+}
+
+
+PortEngine::PortHandle
+JACKPortEngine::register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, 0);
+ return jack_port_register (_priv_jack, shortname.c_str(),
+ ardour_data_type_to_jack_port_type (type),
+ ardour_port_flags_to_jack_flags (flags),
+ 0);
+}
+
+void
+JACKPortEngine::unregister_port (PortHandle port)
+{
+ GET_PRIVATE_JACK_POINTER (_priv_jack);
+ (void) jack_port_unregister (_priv_jack, (jack_port_t*) port);
+}
+
+int
+JACKPortEngine::connect (PortHandle port, const std::string& other)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_connect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str());
+}
+int
+JACKPortEngine::connect (const std::string& src, const std::string& dst)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+
+ int r = jack_connect (_priv_jack, src.c_str(), dst.c_str());
+ return r;
+}
+
+int
+JACKPortEngine::disconnect (PortHandle port, const std::string& other)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_disconnect (_priv_jack, jack_port_name ((jack_port_t*) port), other.c_str());
+}
+
+int
+JACKPortEngine::disconnect (const std::string& src, const std::string& dst)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_disconnect (_priv_jack, src.c_str(), dst.c_str());
+}
+
+int
+JACKPortEngine::disconnect_all (PortHandle port)
+{
+ GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
+ return jack_port_disconnect (_priv_jack, (jack_port_t*) port);
+}
+
+int
+JACKPortEngine::midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** 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
+JACKPortEngine::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
+JACKPortEngine::get_midi_event_count (void* port_buffer)
+{
+ return jack_midi_get_event_count (port_buffer);
+}
+
+void
+JACKPortEngine::midi_clear (void* port_buffer)
+{
+ jack_midi_clear_buffer (port_buffer);
+}
+
+void
+JACKPortEngine::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 ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
+}
+
+LatencyRange
+JACKPortEngine::get_latency_range (PortHandle port, bool for_playback)
+{
+ jack_latency_range_t range;
+ LatencyRange ret;
+
+ jack_port_get_latency_range ((jack_port_t*) port, for_playback ? JackPlaybackLatency : JackCaptureLatency, &range);
+
+ ret.min = range.min;
+ ret.max = range.max;
+
+ return ret;
+}
+
+void*
+JACKPortEngine::get_buffer (PortHandle port, pframes_t nframes)
+{
+ return jack_port_get_buffer ((jack_port_t*) port, nframes);
+}
+
+uint32_t
+JACKPortEngine::port_name_size() const
+{
+ return jack_port_name_size ();
+}
diff --git a/libs/backends/jack/jack_portengine.h b/libs/backends/jack/jack_portengine.h
new file mode 100644
index 0000000000..0e1eb48c5c
--- /dev/null
+++ b/libs/backends/jack/jack_portengine.h
@@ -0,0 +1,132 @@
+/*
+ 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.
+
+*/
+
+#ifndef __libardour_jack_portengine_h__
+#define __libardour_jack_portengine_h__
+
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+#include <jack/types.h>
+#include <jack/midiport.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include "pbd/signals.h"
+
+#include "ardour/port_engine.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class JackConnection;
+
+class JACKPortEngine : public PortEngine
+{
+ public:
+ JACKPortEngine (PortManager&, boost::shared_ptr<JackConnection>);
+ ~JACKPortEngine();
+
+ void* private_handle() const;
+ bool connected() const;
+
+ const std::string& my_name() const;
+
+ uint32_t port_name_size() const;
+
+ int set_port_name (PortHandle, const std::string&);
+ std::string get_port_name (PortHandle) const;
+ PortHandle* get_port_by_name (const std::string&) const;
+
+ int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&) const;
+
+ DataType port_data_type (PortHandle) const;
+
+ PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+ void unregister_port (PortHandle);
+
+ bool connected (PortHandle, bool process_callback_safe);
+ bool connected_to (PortHandle, const std::string&, bool process_callback_safe);
+ bool physically_connected (PortHandle, bool process_callback_safe);
+ int get_connections (PortHandle, std::vector<std::string>&, bool process_callback_safe);
+ int connect (PortHandle, const std::string&);
+
+ int disconnect (PortHandle, const std::string&);
+ int disconnect_all (PortHandle);
+ int connect (const std::string& src, const std::string& dst);
+ int disconnect (const std::string& src, const std::string& dst);
+
+ /* MIDI */
+
+ int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
+ int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
+ uint32_t get_midi_event_count (void* port_buffer);
+ void midi_clear (void* port_buffer);
+
+ /* Monitoring */
+
+ bool can_monitor_input() const;
+ int request_input_monitoring (PortHandle, bool);
+ int ensure_input_monitoring (PortHandle, bool);
+ bool monitoring_input (PortHandle);
+
+ /* Latency management
+ */
+
+ void set_latency_range (PortHandle, bool for_playback, LatencyRange);
+ LatencyRange get_latency_range (PortHandle, bool for_playback);
+
+ /* Physical ports */
+
+ bool port_is_physical (PortHandle) const;
+ void get_physical_outputs (DataType type, std::vector<std::string>&);
+ void get_physical_inputs (DataType type, std::vector<std::string>&);
+ ChanCount n_physical_outputs () const;
+ ChanCount n_physical_inputs () const;
+
+ /* Getting access to the data buffer for a port */
+
+ void* get_buffer (PortHandle, pframes_t);
+
+ /* Miscellany */
+
+ pframes_t sample_time_at_cycle_start ();
+
+ private:
+ boost::shared_ptr<JackConnection> _jack_connection;
+
+ static int _graph_order_callback (void *arg);
+ static void _registration_callback (jack_port_id_t, int, void *);
+ static void _connect_callback (jack_port_id_t, jack_port_id_t, int, void *);
+
+ void connect_callback (jack_port_id_t, jack_port_id_t, int);
+
+ ChanCount n_physical (unsigned long flags) const;
+ void get_physical (DataType type, unsigned long flags, std::vector<std::string>& phy) const;
+
+ PBD::ScopedConnection jack_connection_connection;
+ void connected_to_jack ();
+
+};
+
+} // namespace
+
+#endif /* __libardour_jack_portengine_h__ */
diff --git a/libs/backends/jack/jack_utils.cc b/libs/backends/jack/jack_utils.cc
new file mode 100644
index 0000000000..77f3d95aa1
--- /dev/null
+++ b/libs/backends/jack/jack_utils.cc
@@ -0,0 +1,897 @@
+/*
+ Copyright (C) 2010 Paul Davis
+ Copyright (C) 2011 Tim Mayberry
+
+ 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.
+
+*/
+
+#ifdef HAVE_ALSA
+#include <alsa/asoundlib.h>
+#endif
+
+#ifdef __APPLE__
+#include <CoreAudio/CoreAudio.h>
+#include <CoreFoundation/CFString.h>
+#include <sys/param.h>
+#include <mach-o/dyld.h>
+#endif
+
+#ifdef HAVE_PORTAUDIO
+#include <portaudio.h>
+#endif
+
+#include <jack/jack.h>
+
+#include <fstream>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <glibmm/miscutils.h>
+
+#include "pbd/epa.h"
+#include "pbd/error.h"
+#include "pbd/convert.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+
+#include "jack_utils.h"
+
+#ifdef __APPLE
+#include <CFBundle.h>
+#endif
+
+#include "i18n.h"
+
+using namespace std;
+using namespace PBD;
+
+namespace ARDOUR {
+ // The pretty driver names
+ const char * const portaudio_driver_name = X_("Portaudio");
+ const char * const coreaudio_driver_name = X_("CoreAudio");
+ const char * const alsa_driver_name = X_("ALSA");
+ const char * const oss_driver_name = X_("OSS");
+ const char * const freebob_driver_name = X_("FreeBoB");
+ const char * const ffado_driver_name = X_("FFADO");
+ const char * const netjack_driver_name = X_("NetJACK");
+ const char * const dummy_driver_name = X_("Dummy");
+}
+
+namespace {
+
+ // The real driver names
+ const char * const portaudio_driver_command_line_name = X_("portaudio");
+ const char * const coreaudio_driver_command_line_name = X_("coreaudio");
+ const char * const alsa_driver_command_line_name = X_("alsa");
+ const char * const oss_driver_command_line_name = X_("oss");
+ const char * const freebob_driver_command_line_name = X_("freebob");
+ const char * const ffado_driver_command_line_name = X_("firewire");
+ const char * const netjack_driver_command_line_name = X_("netjack");
+ const char * const dummy_driver_command_line_name = X_("dummy");
+
+ // should we provide more "pretty" names like above?
+ const char * const alsaseq_midi_driver_name = X_("seq");
+ const char * const alsaraw_midi_driver_name = X_("raw");
+ const char * const winmme_midi_driver_name = X_("winmme");
+ const char * const coremidi_midi_driver_name = X_("coremidi");
+
+ // this should probably be translated
+ const char * const default_device_name = X_("Default");
+}
+
+std::string
+get_none_string ()
+{
+ return _("None");
+}
+
+void
+ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
+{
+#ifdef WIN32
+ audio_driver_names.push_back (portaudio_driver_name);
+#elif __APPLE__
+ audio_driver_names.push_back (coreaudio_driver_name);
+#else
+#ifdef HAVE_ALSA
+ audio_driver_names.push_back (alsa_driver_name);
+#endif
+ audio_driver_names.push_back (oss_driver_name);
+ audio_driver_names.push_back (freebob_driver_name);
+ audio_driver_names.push_back (ffado_driver_name);
+#endif
+ audio_driver_names.push_back (netjack_driver_name);
+ audio_driver_names.push_back (dummy_driver_name);
+}
+
+void
+ARDOUR::get_jack_default_audio_driver_name (string& audio_driver_name)
+{
+ vector<string> drivers;
+ get_jack_audio_driver_names (drivers);
+ audio_driver_name = drivers.front ();
+}
+
+void
+ARDOUR::get_jack_sample_rate_strings (vector<string>& samplerates)
+{
+ // do these really need to be translated?
+ samplerates.push_back (_("8000Hz"));
+ samplerates.push_back (_("22050Hz"));
+ samplerates.push_back (_("44100Hz"));
+ samplerates.push_back (_("48000Hz"));
+ samplerates.push_back (_("88200Hz"));
+ samplerates.push_back (_("96000Hz"));
+ samplerates.push_back (_("192000Hz"));
+}
+
+string
+ARDOUR::get_jack_default_sample_rate ()
+{
+ return _("48000Hz");
+}
+
+void
+ARDOUR::get_jack_period_size_strings (std::vector<std::string>& period_sizes)
+{
+ period_sizes.push_back ("32");
+ period_sizes.push_back ("64");
+ period_sizes.push_back ("128");
+ period_sizes.push_back ("256");
+ period_sizes.push_back ("512");
+ period_sizes.push_back ("1024");
+ period_sizes.push_back ("2048");
+ period_sizes.push_back ("4096");
+ period_sizes.push_back ("8192");
+}
+
+string
+ARDOUR::get_jack_default_period_size ()
+{
+ return "1024";
+}
+
+void
+ARDOUR::get_jack_dither_mode_strings (const string& driver, vector<string>& dither_modes)
+{
+ dither_modes.push_back (get_none_string ());
+
+ if (driver == alsa_driver_name ) {
+ dither_modes.push_back (_("Triangular"));
+ dither_modes.push_back (_("Rectangular"));
+ dither_modes.push_back (_("Shaped"));
+ }
+}
+
+string
+ARDOUR::get_jack_default_dither_mode (const string& /*driver*/)
+{
+ return get_none_string ();
+}
+
+string
+ARDOUR::get_jack_latency_string (string samplerate, float periods, string period_size)
+{
+ uint32_t rate = atoi (samplerate);
+ float psize = atof (period_size);
+
+ char buf[32];
+ snprintf (buf, sizeof(buf), "%.1fmsec", (periods * psize) / (rate/1000.0));
+
+ return buf;
+}
+
+bool
+get_jack_command_line_audio_driver_name (const string& driver_name, string& command_line_name)
+{
+ using namespace ARDOUR;
+ if (driver_name == portaudio_driver_name) {
+ command_line_name = portaudio_driver_command_line_name;
+ return true;
+ } else if (driver_name == coreaudio_driver_name) {
+ command_line_name = coreaudio_driver_command_line_name;
+ return true;
+ } else if (driver_name == alsa_driver_name) {
+ command_line_name = alsa_driver_command_line_name;
+ return true;
+ } else if (driver_name == oss_driver_name) {
+ command_line_name = oss_driver_command_line_name;
+ return true;
+ } else if (driver_name == freebob_driver_name) {
+ command_line_name = freebob_driver_command_line_name;
+ return true;
+ } else if (driver_name == ffado_driver_name) {
+ command_line_name = ffado_driver_command_line_name;
+ return true;
+ } else if (driver_name == netjack_driver_name) {
+ command_line_name = netjack_driver_command_line_name;
+ return true;
+ } else if (driver_name == dummy_driver_name) {
+ command_line_name = dummy_driver_command_line_name;
+ return true;
+ }
+ return false;
+}
+
+bool
+get_jack_command_line_audio_device_name (const string& driver_name,
+ const string& device_name, string& command_line_device_name)
+{
+ using namespace ARDOUR;
+ device_map_t devices;
+
+ get_jack_device_names_for_audio_driver (driver_name, devices);
+
+ for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ if (i->first == device_name) {
+ command_line_device_name = i->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+get_jack_command_line_dither_mode (const string& dither_mode, string& command_line_dither_mode)
+{
+ using namespace ARDOUR;
+
+ if (dither_mode == _("Triangular")) {
+ command_line_dither_mode = "triangular";
+ return true;
+ } else if (dither_mode == _("Rectangular")) {
+ command_line_dither_mode = "rectangular";
+ return true;
+ } else if (dither_mode == _("Shaped")) {
+ command_line_dither_mode = "shaped";
+ return true;
+ }
+
+ return false;
+}
+
+void
+ARDOUR::get_jack_alsa_device_names (device_map_t& devices)
+{
+#ifdef HAVE_ALSA
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_pcm_info_t *pcminfo;
+ snd_ctl_card_info_alloca(&info);
+ snd_pcm_info_alloca(&pcminfo);
+ string devname;
+ int cardnum = -1;
+ int device = -1;
+
+ while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+
+ devname = "hw:";
+ devname += PBD::to_string (cardnum, std::dec);
+
+ if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
+
+ if (snd_ctl_card_info (handle, info) < 0) {
+ continue;
+ }
+
+ string card_name = snd_ctl_card_info_get_name (info);
+
+ /* change devname to use ID, not number */
+
+ devname = "hw:";
+ devname += snd_ctl_card_info_get_id (info);
+
+ while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
+
+ /* only detect duplex devices here. more
+ * complex arrangements are beyond our scope
+ */
+
+ snd_pcm_info_set_device (pcminfo, device);
+ snd_pcm_info_set_subdevice (pcminfo, 0);
+ snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
+
+ if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
+
+ snd_pcm_info_set_device (pcminfo, device);
+ snd_pcm_info_set_subdevice (pcminfo, 0);
+ snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
+
+ if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
+ devname += ',';
+ devname += PBD::to_string (device, std::dec);
+ devices.insert (std::make_pair (card_name, devname));
+ }
+ }
+ }
+
+ snd_ctl_close(handle);
+ }
+ }
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+#ifdef __APPLE__
+static OSStatus
+getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
+{
+ UInt32 size = sizeof(CFStringRef);
+ CFStringRef UI;
+ OSStatus res = AudioDeviceGetProperty(id, 0, false,
+ kAudioDevicePropertyDeviceUID, &size, &UI);
+ if (res == noErr)
+ CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
+ CFRelease(UI);
+ return res;
+}
+#endif
+
+void
+ARDOUR::get_jack_coreaudio_device_names (device_map_t& devices)
+{
+#ifdef __APPLE__
+ // Find out how many Core Audio devices are there, if any...
+ // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
+ OSStatus err;
+ Boolean isWritable;
+ UInt32 outSize = sizeof(isWritable);
+
+ err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &outSize, &isWritable);
+ if (err == noErr) {
+ // Calculate the number of device available...
+ int numCoreDevices = outSize / sizeof(AudioDeviceID);
+ // Make space for the devices we are about to get...
+ AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &outSize, (void *) coreDeviceIDs);
+ if (err == noErr) {
+ // Look for the CoreAudio device name...
+ char coreDeviceName[256];
+ UInt32 nameSize;
+
+ for (int i = 0; i < numCoreDevices; i++) {
+
+ nameSize = sizeof (coreDeviceName);
+
+ /* enforce duplex devices only */
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyStreams,
+ &outSize, &isWritable);
+
+ if (err != noErr || outSize == 0) {
+ continue;
+ }
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, false, kAudioDevicePropertyStreams,
+ &outSize, &isWritable);
+
+ if (err != noErr || outSize == 0) {
+ continue;
+ }
+
+ err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyDeviceName,
+ &outSize, &isWritable);
+ if (err == noErr) {
+ err = AudioDeviceGetProperty(coreDeviceIDs[i],
+ 0, true, kAudioDevicePropertyDeviceName,
+ &nameSize, (void *) coreDeviceName);
+ if (err == noErr) {
+ char drivername[128];
+
+ // this returns the unique id for the device
+ // that must be used on the commandline for jack
+
+ if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
+ devices.insert (make_pair (coreDeviceName, drivername));
+ }
+ }
+ }
+ }
+ }
+ delete [] coreDeviceIDs;
+ }
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+void
+ARDOUR::get_jack_portaudio_device_names (device_map_t& devices)
+{
+#ifdef HAVE_PORTAUDIO
+ if (Pa_Initialize() != paNoError) {
+ return;
+ }
+
+ for (PaDeviceIndex i = 0; i < Pa_GetDeviceCount (); ++i) {
+ string api_name;
+ string readable_name;
+ string jack_device_name;
+ const PaDeviceInfo* device_info = Pa_GetDeviceInfo(i);
+
+ if (device_info != NULL) { // it should never be ?
+ api_name = Pa_GetHostApiInfo (device_info->hostApi)->name;
+ readable_name = api_name + " " + device_info->name;
+ jack_device_name = api_name + "::" + device_info->name;
+ devices.insert (make_pair (readable_name, jack_device_name));
+ }
+ }
+ Pa_Terminate();
+#else
+ /* silence a compiler unused variable warning */
+ (void) devices;
+#endif
+}
+
+void
+ARDOUR::get_jack_oss_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_freebob_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_ffado_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_netjack_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+void
+ARDOUR::get_jack_dummy_device_names (device_map_t& devices)
+{
+ devices.insert (make_pair (default_device_name, default_device_name));
+}
+
+bool
+ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name, device_map_t& devices)
+{
+ devices.clear();
+
+ if (driver_name == portaudio_driver_name) {
+ get_jack_portaudio_device_names (devices);
+ } else if (driver_name == coreaudio_driver_name) {
+ get_jack_coreaudio_device_names (devices);
+ } else if (driver_name == alsa_driver_name) {
+ get_jack_alsa_device_names (devices);
+ } else if (driver_name == oss_driver_name) {
+ get_jack_oss_device_names (devices);
+ } else if (driver_name == freebob_driver_name) {
+ get_jack_freebob_device_names (devices);
+ } else if (driver_name == ffado_driver_name) {
+ get_jack_ffado_device_names (devices);
+ } else if (driver_name == netjack_driver_name) {
+ get_jack_netjack_device_names (devices);
+ } else if (driver_name == dummy_driver_name) {
+ get_jack_dummy_device_names (devices);
+ }
+
+ return !devices.empty();
+}
+
+
+std::vector<std::string>
+ARDOUR::get_jack_device_names_for_audio_driver (const string& driver_name)
+{
+ std::vector<std::string> readable_names;
+ device_map_t devices;
+
+ get_jack_device_names_for_audio_driver (driver_name, devices);
+
+ for (device_map_t::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ readable_names.push_back (i->first);
+ }
+
+ return readable_names;
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_two_devices (const string& driver)
+{
+ return (driver == alsa_driver_name || driver == oss_driver_name);
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_latency_adjustment (const string& driver)
+{
+ return (driver == alsa_driver_name || driver == coreaudio_driver_name ||
+ driver == ffado_driver_name || driver == portaudio_driver_name);
+}
+
+bool
+ARDOUR::get_jack_audio_driver_supports_setting_period_count (const string& driver)
+{
+ return !(driver == dummy_driver_name || driver == coreaudio_driver_name ||
+ driver == portaudio_driver_name);
+}
+
+bool
+ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names)
+{
+#ifdef WIN32
+ server_names.push_back ("jackd.exe");
+#else
+ server_names.push_back ("jackd");
+ server_names.push_back ("jackdmp");
+#endif
+ return !server_names.empty();
+}
+
+void
+ARDOUR::set_path_env_for_jack_autostart (const vector<std::string>& dirs)
+{
+#ifdef __APPLE__
+ // push it back into the environment so that auto-started JACK can find it.
+ // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
+ setenv ("PATH", SearchPath(dirs).to_string().c_str(), 1);
+#else
+ /* silence a compiler unused variable warning */
+ (void) dirs;
+#endif
+}
+
+bool
+ARDOUR::get_jack_server_dir_paths (vector<std::string>& server_dir_paths)
+{
+#ifdef __APPLE__
+ /* this magic lets us finds the path to the OSX bundle, and then
+ we infer JACK's location from there
+ */
+
+ char execpath[MAXPATHLEN+1];
+ uint32_t pathsz = sizeof (execpath);
+
+ _NSGetExecutablePath (execpath, &pathsz);
+
+ server_dir_paths.push_back (Glib::path_get_dirname (execpath));
+#endif
+
+ SearchPath sp(string(g_getenv("PATH")));
+
+#ifdef WIN32
+ gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL);
+ if (install_dir) {
+ sp.push_back (install_dir);
+ g_free (install_dir);
+ }
+ // don't try and use a system wide JACK install yet.
+#else
+ if (sp.empty()) {
+ sp.push_back ("/usr/bin");
+ sp.push_back ("/bin");
+ sp.push_back ("/usr/local/bin");
+ sp.push_back ("/opt/local/bin");
+ }
+#endif
+
+ std::copy (sp.begin(), sp.end(), std::back_inserter(server_dir_paths));
+
+ return !server_dir_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_server_paths (const vector<std::string>& server_dir_paths,
+ const vector<string>& server_names,
+ vector<std::string>& server_paths)
+{
+ for (vector<string>::const_iterator i = server_names.begin(); i != server_names.end(); ++i) {
+ Glib::PatternSpec ps (*i);
+ find_matching_files_in_directories (server_dir_paths, ps, server_paths);
+ }
+ return !server_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_server_paths (vector<std::string>& server_paths)
+{
+ vector<std::string> server_dirs;
+
+ if (!get_jack_server_dir_paths (server_dirs)) {
+ return false;
+ }
+
+ vector<string> server_names;
+
+ if (!get_jack_server_application_names (server_names)) {
+ return false;
+ }
+
+ if (!get_jack_server_paths (server_dirs, server_names, server_paths)) {
+ return false;
+ }
+
+ return !server_paths.empty();
+}
+
+bool
+ARDOUR::get_jack_default_server_path (std::string& server_path)
+{
+ vector<std::string> server_paths;
+
+ if (!get_jack_server_paths (server_paths)) {
+ return false;
+ }
+
+ server_path = server_paths.front ();
+ return true;
+}
+
+string
+quote_string (const string& str)
+{
+ return "\"" + str + "\"";
+}
+
+ARDOUR::JackCommandLineOptions::JackCommandLineOptions ()
+ : server_path ()
+ , timeout(0)
+ , no_mlock(false)
+ , ports_max(128)
+ , realtime(true)
+ , priority(0)
+ , unlock_gui_libs(false)
+ , verbose(false)
+ , temporary(true)
+ , driver()
+ , input_device()
+ , output_device()
+ , num_periods(2)
+ , period_size(1024)
+ , samplerate(48000)
+ , input_latency(0)
+ , output_latency(0)
+ , hardware_metering(false)
+ , hardware_monitoring(false)
+ , dither_mode()
+ , force16_bit(false)
+ , soft_mode(false)
+ , midi_driver()
+{
+
+}
+
+bool
+ARDOUR::get_jack_command_line_string (const JackCommandLineOptions& options, string& command_line)
+{
+ vector<string> args;
+
+ args.push_back (options.server_path);
+
+#ifdef WIN32
+ // must use sync mode on windows
+ args.push_back ("-S");
+
+ // this needs to be added now on windows
+ if (!options.midi_driver.empty () && options.midi_driver != get_none_string ()) {
+ args.push_back ("-X");
+ args.push_back (options.midi_driver);
+ }
+#endif
+
+ if (options.timeout) {
+ args.push_back ("-t");
+ args.push_back (to_string (options.timeout, std::dec));
+ }
+
+ if (options.no_mlock) {
+ args.push_back ("-m");
+ }
+
+ args.push_back ("-p");
+ args.push_back (to_string(options.ports_max, std::dec));
+
+ if (options.realtime) {
+ args.push_back ("-R");
+ if (options.priority != 0) {
+ args.push_back ("-P");
+ args.push_back (to_string(options.priority, std::dec));
+ }
+ } else {
+ args.push_back ("-r");
+ }
+
+ if (options.unlock_gui_libs) {
+ args.push_back ("-u");
+ }
+
+ if (options.verbose) {
+ args.push_back ("-v");
+ }
+
+#ifndef WIN32
+ if (options.temporary) {
+ args.push_back ("-T");
+ }
+#endif
+
+ string command_line_driver_name;
+
+ if (!get_jack_command_line_audio_driver_name (options.driver, command_line_driver_name)) {
+ return false;
+ }
+
+ args.push_back ("-d");
+ args.push_back (command_line_driver_name);
+
+ if (options.output_device.empty() && options.input_device.empty()) {
+ return false;
+ }
+
+ string command_line_input_device_name;
+ string command_line_output_device_name;
+
+ if (!get_jack_command_line_audio_device_name (options.driver,
+ options.input_device, command_line_input_device_name)) {
+ return false;
+ }
+
+ if (!get_jack_command_line_audio_device_name (options.driver,
+ options.output_device, command_line_output_device_name)) {
+ return false;
+ }
+
+ if (options.input_device.empty()) {
+ // playback only
+ if (options.output_device.empty()) {
+ return false;
+ }
+ args.push_back ("-P");
+ } else if (options.output_device.empty()) {
+ // capture only
+ if (options.input_device.empty()) {
+ return false;
+ }
+ args.push_back ("-C");
+ } else if (options.input_device != options.output_device) {
+ // capture and playback on two devices if supported
+ if (get_jack_audio_driver_supports_two_devices (options.driver)) {
+ args.push_back ("-C");
+ args.push_back (command_line_input_device_name);
+ args.push_back ("-P");
+ args.push_back (command_line_output_device_name);
+ } else {
+ return false;
+ }
+ }
+
+ if (options.input_channels) {
+ args.push_back ("-i");
+ args.push_back (to_string (options.input_channels, std::dec));
+ }
+
+ if (options.output_channels) {
+ args.push_back ("-o");
+ args.push_back (to_string (options.output_channels, std::dec));
+ }
+
+ if (get_jack_audio_driver_supports_setting_period_count (options.driver)) {
+ args.push_back ("-n");
+ args.push_back (to_string (options.num_periods, std::dec));
+ }
+
+ args.push_back ("-r");
+ args.push_back (to_string (options.samplerate, std::dec));
+
+ args.push_back ("-p");
+ args.push_back (to_string (options.period_size, std::dec));
+
+ if (get_jack_audio_driver_supports_latency_adjustment (options.driver)) {
+ if (options.input_latency) {
+ args.push_back ("-I");
+ args.push_back (to_string (options.input_latency, std::dec));
+ }
+ if (options.output_latency) {
+ args.push_back ("-O");
+ args.push_back (to_string (options.output_latency, std::dec));
+ }
+ }
+
+ if (options.input_device == options.output_device && options.input_device != default_device_name) {
+ args.push_back ("-d");
+ args.push_back (command_line_input_device_name);
+ }
+
+ if (options.driver == alsa_driver_name) {
+ if (options.hardware_metering) {
+ args.push_back ("-M");
+ }
+ if (options.hardware_monitoring) {
+ args.push_back ("-H");
+ }
+
+ string command_line_dither_mode;
+ if (get_jack_command_line_dither_mode (options.dither_mode, command_line_dither_mode)) {
+ args.push_back ("-z");
+ args.push_back (command_line_dither_mode);
+ }
+ if (options.force16_bit) {
+ args.push_back ("-S");
+ }
+ if (options.soft_mode) {
+ args.push_back ("-s");
+ }
+
+ if (!options.midi_driver.empty() && options.midi_driver != get_none_string ()) {
+ args.push_back ("-X");
+ args.push_back (options.midi_driver);
+ }
+ }
+
+ ostringstream oss;
+
+ for (vector<string>::const_iterator i = args.begin(); i != args.end();) {
+#ifdef WIN32
+ oss << quote_string (*i);
+#else
+ oss << *i;
+#endif
+ if (++i != args.end()) oss << ' ';
+ }
+
+ command_line = oss.str();
+ return true;
+}
+
+string
+ARDOUR::get_jack_server_config_file_name ()
+{
+ return ".jackdrc";
+}
+
+std::string
+ARDOUR::get_jack_server_user_config_dir_path ()
+{
+ return Glib::get_home_dir ();
+}
+
+std::string
+ARDOUR::get_jack_server_user_config_file_path ()
+{
+ return Glib::build_filename (get_jack_server_user_config_dir_path (), get_jack_server_config_file_name ());
+}
+
+bool
+ARDOUR::write_jack_config_file (const std::string& config_file_path, const string& command_line)
+{
+ ofstream jackdrc (config_file_path.c_str());
+
+ if (!jackdrc) {
+ error << string_compose (_("cannot open JACK rc file %1 to store parameters"), config_file_path) << endmsg;
+ return false;
+ }
+
+ jackdrc << command_line << endl;
+ jackdrc.close ();
+ return true;
+}
diff --git a/libs/backends/jack/jack_utils.h b/libs/backends/jack/jack_utils.h
new file mode 100644
index 0000000000..7565353198
--- /dev/null
+++ b/libs/backends/jack/jack_utils.h
@@ -0,0 +1,235 @@
+/*
+ Copyright (C) 2011 Tim Mayberry
+
+ 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 <stdint.h>
+
+#include <vector>
+#include <map>
+#include <string>
+
+namespace ARDOUR {
+
+ // Names for the drivers on all possible systems
+ extern const char * const portaudio_driver_name;
+ extern const char * const coreaudio_driver_name;
+ extern const char * const alsa_driver_name;
+ extern const char * const oss_driver_name;
+ extern const char * const freebob_driver_name;
+ extern const char * const ffado_driver_name;
+ extern const char * const netjack_driver_name;
+ extern const char * const dummy_driver_name;
+
+ /**
+ * Get a list of possible JACK audio driver names based on platform
+ */
+ void get_jack_audio_driver_names (std::vector<std::string>& driver_names);
+
+ /**
+ * Get the default JACK audio driver based on platform
+ */
+ void get_jack_default_audio_driver_name (std::string& driver_name);
+
+ /**
+ * Get a list of possible samplerates supported be JACK
+ */
+ void get_jack_sample_rate_strings (std::vector<std::string>& sample_rates);
+
+ /**
+ * @return The default samplerate
+ */
+ std::string get_jack_default_sample_rate ();
+
+ /**
+ * @return true if sample rate string was able to be converted
+ */
+ bool get_jack_sample_rate_value_from_string (const std::string& srs, uint32_t& srv);
+
+ /**
+ * Get a list of possible period sizes supported be JACK
+ */
+ void get_jack_period_size_strings (std::vector<std::string>& samplerates);
+
+ /**
+ * @return The default period size
+ */
+ std::string get_jack_default_period_size ();
+
+ /**
+ * @return true if period size string was able to be converted
+ */
+ bool get_jack_period_size_value_from_string (const std::string& pss, uint32_t& psv);
+
+ /**
+ * These are driver specific I think, so it may require a driver arg
+ * in future
+ */
+ void get_jack_dither_mode_strings (const std::string& driver, std::vector<std::string>& dither_modes);
+
+ /**
+ * @return The default dither mode
+ */
+ std::string get_jack_default_dither_mode (const std::string& driver);
+
+ /**
+ * @return Estimate of latency
+ *
+ * API matches current use in GUI
+ */
+ std::string get_jack_latency_string (std::string samplerate, float periods, std::string period_size);
+
+ /**
+ * Key being a readable name to display in a GUI
+ * Value being name used in a jack commandline
+ */
+ typedef std::map<std::string, std::string> device_map_t;
+
+ /**
+ * Use library specific code to find out what what devices exist for a given
+ * driver that might work in JACK. There is no easy way to find out what
+ * modules the JACK server supports so guess based on platform. For instance
+ * portaudio is cross-platform but we only return devices if built for
+ * windows etc
+ */
+ void get_jack_alsa_device_names (device_map_t& devices);
+ void get_jack_portaudio_device_names (device_map_t& devices);
+ void get_jack_coreaudio_device_names (device_map_t& devices);
+ void get_jack_oss_device_names (device_map_t& devices);
+ void get_jack_freebob_device_names (device_map_t& devices);
+ void get_jack_ffado_device_names (device_map_t& devices);
+ void get_jack_netjack_device_names (device_map_t& devices);
+ void get_jack_dummy_device_names (device_map_t& devices);
+
+ /*
+ * @return true if there were devices found for the driver
+ *
+ * @param driver The driver name returned by get_jack_audio_driver_names
+ * @param devices The map used to insert the drivers into, devices will be cleared before
+ * adding the available drivers
+ */
+ bool get_jack_device_names_for_audio_driver (const std::string& driver, device_map_t& devices);
+
+ /*
+ * @return a list of readable device names for a specific driver.
+ */
+ std::vector<std::string> get_jack_device_names_for_audio_driver (const std::string& driver);
+
+ /**
+ * @return true if the driver supports playback and recording
+ * on separate devices
+ */
+ bool get_jack_audio_driver_supports_two_devices (const std::string& driver);
+
+ bool get_jack_audio_driver_supports_latency_adjustment (const std::string& driver);
+
+ bool get_jack_audio_driver_supports_setting_period_count (const std::string& driver);
+
+ /**
+ * The possible names to use to try and find servers, this includes
+ * any file extensions like .exe on Windows
+ *
+ * @return true if the JACK application names for this platform could be guessed
+ */
+ bool get_jack_server_application_names (std::vector<std::string>& server_names);
+
+ /**
+ * Sets the PATH environment variable to contain directories likely to contain
+ * JACK servers so that if the JACK server is auto-started it can find the server
+ * executable.
+ *
+ * This is only modifies PATH on the mac at the moment.
+ */
+ void set_path_env_for_jack_autostart (const std::vector<std::string>&);
+
+ /**
+ * Get absolute paths to directories that might contain JACK servers on the system
+ *
+ * @return true if !server_paths.empty()
+ */
+ bool get_jack_server_dir_paths (std::vector<std::string>& server_dir_paths);
+
+ /**
+ * Get absolute paths to JACK servers on the system
+ *
+ * @return true if a server was found
+ */
+ bool get_jack_server_paths (const std::vector<std::string>& server_dir_paths,
+ const std::vector<std::string>& server_names,
+ std::vector<std::string>& server_paths);
+
+
+ bool get_jack_server_paths (std::vector<std::string>& server_paths);
+
+ /**
+ * Get absolute path to default JACK server
+ */
+ bool get_jack_default_server_path (std::string& server_path);
+
+ /**
+ * @return The name of the jack server config file
+ */
+ std::string get_jack_server_config_file_name ();
+
+ std::string get_jack_server_user_config_dir_path ();
+
+ std::string get_jack_server_user_config_file_path ();
+
+ bool write_jack_config_file (const std::string& config_file_path, const std::string& command_line);
+
+ struct JackCommandLineOptions {
+
+ // see implementation for defaults
+ JackCommandLineOptions ();
+
+ //operator bool
+ //operator ostream
+
+ std::string server_path;
+ uint32_t timeout;
+ bool no_mlock;
+ uint32_t ports_max;
+ bool realtime;
+ uint32_t priority;
+ bool unlock_gui_libs;
+ bool verbose;
+ bool temporary;
+ bool playback_only;
+ bool capture_only;
+ std::string driver;
+ std::string input_device;
+ std::string output_device;
+ uint32_t num_periods;
+ uint32_t period_size;
+ uint32_t samplerate;
+ uint32_t input_channels;
+ uint32_t output_channels;
+ uint32_t input_latency;
+ uint32_t output_latency;
+ bool hardware_metering;
+ bool hardware_monitoring;
+ std::string dither_mode;
+ bool force16_bit;
+ bool soft_mode;
+ std::string midi_driver;
+ };
+
+ /**
+ * @return true if able to build a valid command line based on options
+ */
+ bool get_jack_command_line_string (const JackCommandLineOptions& options, std::string& command_line);
+}
diff --git a/libs/backends/jack/wscript b/libs/backends/jack/wscript
new file mode 100644
index 0000000000..3c47f3a652
--- /dev/null
+++ b/libs/backends/jack/wscript
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+import sys
+import re
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+JACKBACKEND_VERSION = '1.0.0'
+I18N_PACKAGE = 'jack-backend'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = [
+ 'jack_api.cc',
+ 'jack_connection.cc',
+ 'jack_audiobackend.cc',
+ 'jack_portengine.cc',
+ 'jack_utils.cc'
+ ]
+ obj.includes = ['.']
+ obj.cxxflags = [ '-fPIC' ]
+ obj.name = 'jack_audiobackend'
+ obj.target = 'jack_audiobackend'
+ obj.uselib = [ 'JACK' ]
+ obj.use = 'ardour libpbd'
+ obj.vnum = JACKBACKEND_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'backends')
+ obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"']
+
+
+ #
+ # device discovery code in the jack backend needs ALSA
+ # on Linux.
+ #
+
+ if re.search ("linux", sys.platform) != None:
+ obj.uselib += [ 'ALSA' ]
+
diff --git a/libs/backends/wscript b/libs/backends/wscript
new file mode 100644
index 0000000000..b8779b7713
--- /dev/null
+++ b/libs/backends/wscript
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+backends = [ 'jack' ]
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def sub_config_and_use(conf, name, has_objects = True):
+ conf.recurse(name)
+ autowaf.set_local_lib(conf, name, has_objects)
+
+def configure(conf):
+ autowaf.set_recursive()
+ autowaf.configure(conf)
+
+ for i in backends:
+ sub_config_and_use(conf, i)
+
+def build(bld):
+ for i in backends:
+ bld.recurse(i)