add tim's jack_utils code to rationalize setup of JACK config

This commit is contained in:
Paul Davis 2013-08-03 16:37:10 -04:00
parent 1c4d00e8b7
commit c2e7c32c53
13 changed files with 1217 additions and 43 deletions

View 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.

View 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 ()
{

View 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;

View 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; }

View 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 */

View File

@ -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);
}

View 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;
}

View 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;
}

View 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 ();
}

View File

@ -23,12 +23,15 @@
#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;
}

View 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)
{

896
libs/ardour/jack_utils.cc Normal file
View File

@ -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;
}

View 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'