add tim's jack_utils code to rationalize setup of JACK config
authorPaul Davis <paul@linuxaudiosystems.com>
Sat, 3 Aug 2013 20:37:10 +0000 (16:37 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Sat, 3 Aug 2013 20:37:10 +0000 (16:37 -0400)
13 files changed:
gtk2_ardour/ardour_ui.cc
gtk2_ardour/engine_dialog.cc
gtk2_ardour/startup.cc
libs/ardour/ardour/audioengine.h
libs/ardour/ardour/jack_audiobackend.h
libs/ardour/ardour/jack_utils.h [new file with mode: 0644]
libs/ardour/audioengine.cc
libs/ardour/globals.cc
libs/ardour/jack_audiobackend.cc
libs/ardour/jack_connection.cc
libs/ardour/jack_portengine.cc
libs/ardour/jack_utils.cc [new file with mode: 0644]
libs/ardour/wscript

index e764c50d4e85327dd8187f875594a539d3603eb8..7e6ce10300aa009bd34aac32340143b94b44e9d9 100644 (file)
@@ -400,13 +400,23 @@ ARDOUR_UI::attach_to_engine ()
        engine->BackendAvailable.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::post_engine, this));
 
        ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);
+
+       /* if there is only one audio/midi backend, and it does not require setup, get our use of it underway
+        * right here (we need to know the client name and potential session ID
+        * to do this, which is why this is here, rather than in, say,
+        * ARDOUR::init().
+        */
+
+       if (!AudioEngine::instance()->setup_required()) {
+               const AudioBackendInfo* backend = AudioEngine::instance()->available_backends().front();
+               AudioEngine::instance()->set_backend (backend->name, ARDOUR_COMMAND_LINE::backend_client_name, ARDOUR_COMMAND_LINE::backend_session_uuid);
+               AudioEngine::instance()->start ();
+       }
 }
 
 void
 ARDOUR_UI::post_engine ()
 {
-       cerr << "Backend available!\n";
-
        /* Things to be done once we have a backend running in the AudioEngine
         */
 
@@ -2565,17 +2575,6 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
        int ret = -1;
        bool likely_new = false;
 
-       /* if the audio/midi backend does not require setup, get our use of it underway
-        * right here
-        */
-
-       if (!EngineControl::need_setup()) {
-               vector<const AudioBackendInfo*> backends = AudioEngine::instance()->available_backends();
-               cerr << "Setting up backend " << backends.front()->name;
-               AudioEngine::instance()->set_backend (backends.front()->name, ARDOUR_COMMAND_LINE::backend_client_name, ARDOUR_COMMAND_LINE::backend_session_uuid);
-               AudioEngine::instance()->start ();
-       }
-
        /* deal with any existing DIRTY session now, rather than later. don't
         * treat a non-dirty session this way, so that it stays visible 
         * as we bring up the new session dialog.
index 2db93d75eceb108db908e82d775c91479b394b3e..b52d3a7a156b6386f293169f1461bc4cbfb22735 100644 (file)
@@ -593,18 +593,6 @@ EngineControl::build_command_line (vector<string>& cmd)
        }
 }
 
-bool
-EngineControl::need_setup ()
-{
-       vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
-
-       if (backends.size() == 1 && backends.front()->already_configured()) {
-               return false;
-       }
-
-       return true;
-}
-
 int
 EngineControl::setup_engine ()
 {
index e24e7d6989417e967faa93fad0a99e8b6ace5fb2..8314a0986e6a8aab57db92603079f1db178ed198 100644 (file)
@@ -34,6 +34,7 @@
 #include "pbd/stacktrace.h"
 #include "pbd/openuri.h"
 
+#include "ardour/audioengine.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/recent_sessions.h"
 #include "ardour/session.h"
@@ -91,7 +92,7 @@ ArdourStartup::ArdourStartup (bool require_new, const std::string& session_name,
        , _existing_session_chooser_used (false)
 {
        new_user = !Glib::file_test (been_here_before_path(), Glib::FILE_TEST_EXISTS);
-       need_audio_setup = EngineControl::need_setup ();
+       need_audio_setup = AudioEngine::instance()->setup_required ();
        need_session_info = (session_name.empty() || require_new);
 
        _provided_session_name = session_name;
index 4412faca2212fe1ae95b14b213b1c18e6ba1e954..f06890676dc24950d3fe1e89ecca9e1f04212ffb 100644 (file)
@@ -75,6 +75,7 @@ public:
     std::vector<const AudioBackendInfo*> available_backends() const;
     std::string current_backend_name () const;
     int set_backend (const std::string&, const std::string& arg1, const std::string& arg2);
+    bool setup_required () const;
 
     ProcessThread* main_thread() const { return _main_thread; }
     
index 4f44bdba5f135c0c20117d8b1c75d6a0414a0f08..4fef60211935a8502721f520148251b4d99ba71c 100644 (file)
@@ -147,7 +147,7 @@ class JACKAudioBackend : public AudioBackend {
 
     ChanCount n_physical (unsigned long) const;
     
-    void preset_jack_startup_command ();
+    void setup_jack_startup_command ();
 
     /* pffooo */
 
diff --git a/libs/ardour/ardour/jack_utils.h b/libs/ardour/ardour/jack_utils.h
new file mode 100644 (file)
index 0000000..169dc12
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+    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 JACK midi driver names based on platform
+        */
+       void get_jack_midi_system_names (const std::string& driver, std::vector<std::string>& driver_names);
+
+       /**
+        * Get the default JACK midi driver based on platform
+        */
+       void get_jack_default_midi_system_name (const std::string& driver_name, std::string& midi_system);
+
+       /**
+        * 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_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);
+}
index a7682bac70db523e8ac77369cb75c0e314511a15..2884f410df2f99e6a2988330225aaeb643ad2243 100644 (file)
@@ -194,10 +194,14 @@ AudioEngine::process_callback (pframes_t nframes)
                return 0;
        }
 
+       cerr << "pc, srp = " << session_remove_pending << endl;
+
        if (session_remove_pending) {
 
                /* perform the actual session removal */
 
+               cerr << "\tsrc = " << session_removal_countdown << endl;
+
                if (session_removal_countdown < 0) {
 
                        /* fade out over 1 second */
@@ -226,6 +230,7 @@ AudioEngine::process_callback (pframes_t nframes)
                        _session = 0;
                        session_removal_countdown = -1; // reset to "not in progress"
                        session_remove_pending = false;
+                       cerr << "Send removed signal\n";
                        session_removed.signal(); // wakes up thread that initiated session removal
                }
        }
@@ -437,6 +442,7 @@ AudioEngine::remove_session ()
 
                if (_session) {
                        session_remove_pending = true;
+                       session_removal_countdown = 0;
                        session_removed.wait(_process_lock);
                }
 
@@ -588,11 +594,8 @@ AudioEngine::set_backend (const std::string& name, const std::string& arg1, cons
                        throw failed_constructor ();
                }
 
-               cerr << "bf\n";
                _backend = b->second->backend_factory (*this);
-               cerr << "pf\n";
                _impl = b->second->portengine_factory (*this);
-               cerr << "done\n";
 
        } catch (exception& e) {
                error << string_compose (_("Could not create backend for %1: %2"), name, e.what()) << endmsg;
@@ -1007,3 +1010,12 @@ AudioEngine::halted_callback (const char* why)
        Halted (why); /* EMIT SIGNAL */
 }
 
+bool
+AudioEngine::setup_required () const
+{
+       if (_backends.size() == 1 && _backends.begin()->second->already_configured()) {
+               return false;
+       }
+
+       return true;
+}
index e890838bfbc08988ce8a4ea5990444b51decdc5b..d744368fe7c9238992f0b28ce614c1d9a09bf881 100644 (file)
@@ -334,12 +334,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
 
        ARDOUR::AudioEngine::create ();
 
-       vector<const AudioBackendInfo*> backends = AudioEngine::instance()->available_backends();
-
-       for (vector<const AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
-               cerr << "BACKEND: [" << (*i)->name << "] already configured " << (*i)->already_configured() << endl;
-       }
-
        return 0;
 }
 
index 1bdd70c0f6c220eb00d0507e50b0c23a2c4456da..ed412f3f5a07f987fe61ecc30e6438d415c34dfe 100644 (file)
@@ -31,6 +31,7 @@
 #include "ardour/jack_audiobackend.h"
 #include "ardour/jack_connection.h"
 #include "ardour/jack_portengine.h"
+#include "ardour/jack_utils.h"
 
 #include "i18n.h"
 
@@ -355,12 +356,39 @@ JACKAudioBackend::raw_buffer_size(DataType t)
 }
 
 void
-JACKAudioBackend::preset_jack_startup_command ()
+JACKAudioBackend::setup_jack_startup_command ()
 {
-       /* write parameter settings to ~/.jackdrc file so that next invocation
-        * of JACK (presumably from a call to jack_client_open() from this
-        * process) will do the right thing.
+       /* first we map the parameters that have been set onto a
+        * JackCommandLineOptions object.
         */
+
+       JackCommandLineOptions options;
+
+       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;
+       if (_target_sample_format == FormatInt16) {
+               options.force16_bit = _target_sample_format;
+       }
+
+       /* 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 */
+       }
+
+       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 --- */
@@ -371,8 +399,9 @@ JACKAudioBackend::start ()
        if (!connected()) {
 
                if (!_jack_connection->server_running()) {
-                       preset_jack_startup_command ();
+                       setup_jack_startup_command ();
                }
+
                std::cerr << "Open JACK connection\n";
                _jack_connection->open ();
        }
index cbd9b3fbccc968107afd028b32c568802ccc2a4c..57950b0e17086004dfcdd8cd6453e3ab5ae4b918 100644 (file)
 #include "pbd/epa.h"
 
 #include "ardour/jack_connection.h"
+#include "ardour/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)
 {
@@ -99,6 +102,14 @@ JackConnection::open ()
                 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;
        }
index d0716e555f70ef37367f23847085f25fde980952..82b4502cb6306bca97416d5167e7ca25eeb6ac94 100644 (file)
@@ -461,7 +461,6 @@ JACKPortEngine::disconnect_all (PortHandle port)
        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)
 {
diff --git a/libs/ardour/jack_utils.cc b/libs/ardour/jack_utils.cc
new file mode 100644 (file)
index 0000000..606b716
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+    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 WAF_BUILD
+#include "libardour-config.h"
+#endif
+
+#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 "ardour/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_midi_system_names (const string& driver, vector<string>& midi_system_names)
+{
+       midi_system_names.push_back (get_none_string ());
+#ifdef WIN32
+       midi_system_names.push_back (winmme_midi_driver_name);
+#elif __APPLE__
+       midi_system_names.push_back (coreaudio_midi_driver_name);
+#else
+#ifdef HAVE_ALSA
+       if (driver == alsa_driver_name) {
+               midi_system_names.push_back (alsaseq_midi_driver_name);
+               midi_system_names.push_back (alsaraw_midi_driver_name);
+       }
+#endif
+#endif
+}
+
+void
+ARDOUR::get_jack_default_midi_system_name (const string& driver, string& midi_system_name)
+{
+       vector<string> drivers;
+       get_jack_midi_system_names (driver, drivers);
+       midi_system_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) {
+
+                       while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 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 (snd_pcm_info_get_name (pcminfo), 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(), 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) {
+               find_matching_files_in_directories (server_dir_paths, Glib::PatternSpec(*i), 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(true)
+       , 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 (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 ("-0");
+                       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;
+}
index 07bb10f08d37b9831a5d86ce29f315634aebe925..d48a4d6d85d42f4be51f5a8252a222835df7a428 100644 (file)
@@ -441,7 +441,8 @@ def build(bld):
             'jack_api.cc',
             'jack_connection.cc',
             'jack_audiobackend.cc',
-            'jack_portengine.cc'
+            'jack_portengine.cc',
+            'jack_utils.cc'
             ])
     obj.cxxflags = [ '-fPIC' ]
     obj.name     = 'jack_audiobackend'