John Emmas
458d4dc6de
A few things need to be tested / completed:- 1) The code is currently guarded by #ifdef COMPILER_MSVC. This is just precautionary. If it builds okay with MinGW ,the guard can be removed. 2) Windows Playback and Capture devices almost always have different names. This needs to get accommodated in our Backend dialog (as in Mixbus) 3) Windows Playback and Capture devices will almost always contain spaces. We need to accommodate this when writing to .jackdrc (surround them in quote marks)
1042 lines
27 KiB
C++
1042 lines
27 KiB
C++
/*
|
|
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 PLATFORM_WINDOWS
|
|
#include <shobjidl.h> // Needed for
|
|
#include <shlguid.h> // 'IShellLink'
|
|
#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 alsa_seq_midi_driver_name = X_("alsa");
|
|
const char * const alsa_raw_midi_driver_name = X_("alsarawmidi");
|
|
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");
|
|
}
|
|
|
|
static ARDOUR::MidiOptions midi_options;
|
|
|
|
std::string
|
|
get_none_string ()
|
|
{
|
|
return _("None");
|
|
}
|
|
|
|
void
|
|
ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
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 PLATFORM_WINDOWS
|
|
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 PLATFORM_WINDOWS
|
|
// N.B. The #define (immediately below) can be safely removed once we know that this code builds okay with mingw
|
|
#ifdef COMPILER_MSVC
|
|
IShellLinkA *pISL = NULL;
|
|
IPersistFile *ppf = NULL;
|
|
|
|
// Mixbus creates a Windows shortcut giving the location of its
|
|
// own (bundled) version of Jack. Let's see if that shortcut exists
|
|
if (SUCCEEDED (CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pISL)))
|
|
{
|
|
if (SUCCEEDED (pISL->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
|
|
{
|
|
char target_path[MAX_PATH];
|
|
char shortcut_pathA[MAX_PATH];
|
|
WCHAR shortcut_pathW[MAX_PATH];
|
|
|
|
// Our Windows installer should have created a shortcut to the Jack
|
|
// server so let's start by finding out what drive it got installed on
|
|
if (char *env_path = getenv ("windir"))
|
|
{
|
|
strcpy (shortcut_pathA, env_path);
|
|
shortcut_pathA[2] = '\0'; // Gives us just the drive letter and colon
|
|
}
|
|
else // Assume 'C:'
|
|
strcpy (shortcut_pathA, "C:");
|
|
|
|
strcat (shortcut_pathA, "\\Program Files (x86)\\Jack\\Start Jack.lnk");
|
|
|
|
MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, shortcut_pathA, -1, shortcut_pathW, MAX_PATH);
|
|
|
|
// If it did, load the shortcut into our persistent file
|
|
if (SUCCEEDED (ppf->Load(shortcut_pathW, 0)))
|
|
{
|
|
// Read the target information from the shortcut object
|
|
if (S_OK == (pISL->GetPath (target_path, MAX_PATH, NULL, SLGP_UNCPRIORITY)))
|
|
{
|
|
char *p = strrchr (target_path, '\\');
|
|
|
|
if (p)
|
|
{
|
|
*p = NULL;
|
|
sp.push_back (target_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppf)
|
|
ppf->Release();
|
|
|
|
if (pISL)
|
|
pISL->Release();
|
|
#endif
|
|
|
|
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 (JackCommandLineOptions& options, string& command_line, bool for_latency_measurement)
|
|
{
|
|
vector<string> args;
|
|
|
|
args.push_back (options.server_path);
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
// 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
|
|
|
|
/* XXX hack to enforce qjackctl-like behaviour */
|
|
if (options.timeout == 0) {
|
|
options.timeout = 200;
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
if (options.temporary) {
|
|
args.push_back ("-T");
|
|
}
|
|
|
|
if (options.driver == alsa_driver_name) {
|
|
if (options.midi_driver == alsa_seq_midi_driver_name) {
|
|
args.push_back ("-X");
|
|
args.push_back ("alsa_midi");
|
|
} else if (options.midi_driver == alsa_raw_midi_driver_name) {
|
|
args.push_back ("-X");
|
|
args.push_back ("alsarawmidi");
|
|
}
|
|
}
|
|
|
|
string command_line_driver_name;
|
|
|
|
string command_line_input_device_name;
|
|
string command_line_output_device_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.driver != dummy_driver_name) {
|
|
if (options.output_device.empty() && options.input_device.empty()) {
|
|
return false;
|
|
}
|
|
|
|
|
|
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));
|
|
}
|
|
} else {
|
|
// jackd dummy backend
|
|
if (options.input_channels) {
|
|
args.push_back ("-C");
|
|
args.push_back (to_string (options.input_channels, std::dec));
|
|
}
|
|
|
|
if (options.output_channels) {
|
|
args.push_back ("-P");
|
|
args.push_back (to_string (options.output_channels, 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 (!for_latency_measurement && 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.driver != dummy_driver_name) {
|
|
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.driver == alsa_driver_name || options.driver == coreaudio_driver_name) {
|
|
|
|
if (options.midi_driver != alsa_seq_midi_driver_name) {
|
|
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();) {
|
|
oss << *i;
|
|
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;
|
|
}
|
|
|
|
vector<string>
|
|
ARDOUR::enumerate_midi_options ()
|
|
{
|
|
if (midi_options.empty()) {
|
|
#ifdef HAVE_ALSA
|
|
midi_options.push_back (make_pair (_("(legacy) ALSA raw devices"), alsaraw_midi_driver_name));
|
|
midi_options.push_back (make_pair (_("(legacy) ALSA sequencer"), alsaseq_midi_driver_name));
|
|
midi_options.push_back (make_pair (_("ALSA (JACK1, 0.124 and later)"), alsa_seq_midi_driver_name));
|
|
midi_options.push_back (make_pair (_("ALSA (JACK2, 1.9.8 and later)"), alsa_raw_midi_driver_name));
|
|
#endif
|
|
#ifdef HAVE_PORTAUDIO
|
|
/* Windows folks: what name makes sense here? Are there other
|
|
choices as well ?
|
|
*/
|
|
midi_options.push_back (make_pair (_("Multimedia Extension"), winmme_midi_driver_name));
|
|
#endif
|
|
#ifdef __APPLE__
|
|
midi_options.push_back (make_pair (_("CoreMIDI"), coremidi_midi_driver_name));
|
|
#endif
|
|
}
|
|
|
|
vector<string> v;
|
|
|
|
v.push_back (get_none_string());
|
|
|
|
for (MidiOptions::const_iterator i = midi_options.begin(); i != midi_options.end(); ++i) {
|
|
v.push_back (i->first);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
int
|
|
ARDOUR::set_midi_option (ARDOUR::JackCommandLineOptions& options, const string& opt)
|
|
{
|
|
if (opt.empty() || opt == get_none_string()) {
|
|
options.midi_driver = "";
|
|
return 0;
|
|
}
|
|
|
|
for (MidiOptions::const_iterator i = midi_options.begin(); i != midi_options.end(); ++i) {
|
|
if (i->first == opt) {
|
|
options.midi_driver = i->second;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|