enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / backends / jack / jack_utils.cc
index 0a5b3eb44d552acf6bfe052a1957303d87da52bf..b26cb4036797194112457aba6ea1c4a53396773a 100644 (file)
@@ -19,7 +19,7 @@
 */
 
 #ifdef HAVE_ALSA
-#include <alsa/asoundlib.h>
+#include "ardouralsautil/devicelist.h"
 #endif
 
 #ifdef __APPLE__
 #include <mach-o/dyld.h>
 #endif
 
-#ifdef HAVE_PORTAUDIO
-#include <portaudio.h>
+#ifdef PLATFORM_WINDOWS
+#include <shobjidl.h>  //  Needed for
+#include <shlguid.h>   // 'IShellLink'
 #endif
 
-#include <jack/jack.h>
-
-#include <fstream>
+#if (defined PLATFORM_WINDOWS && defined HAVE_PORTAUDIO)
+#include <portaudio.h>
+#endif
 
 #include <boost/scoped_ptr.hpp>
 
+#include "pbd/gstdio_compat.h"
 #include <glibmm/miscutils.h>
 
 #include "pbd/epa.h"
@@ -53,7 +55,7 @@
 #include <CFBundle.h>
 #endif
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace PBD;
@@ -83,6 +85,8 @@ namespace {
        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");
@@ -103,7 +107,7 @@ get_none_string ()
 void
 ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
 {
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        audio_driver_names.push_back (portaudio_driver_name);
 #elif __APPLE__
        audio_driver_names.push_back (coreaudio_driver_name);
@@ -269,60 +273,7 @@ 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);
-               }
-       }
+       get_alsa_audio_device_names(devices);
 #else
        /* silence a compiler unused variable warning */
        (void) devices;
@@ -421,7 +372,7 @@ ARDOUR::get_jack_coreaudio_device_names (device_map_t& devices)
 void
 ARDOUR::get_jack_portaudio_device_names (device_map_t& devices)
 {
-#ifdef HAVE_PORTAUDIO
+#if (defined PLATFORM_WINDOWS && defined HAVE_PORTAUDIO)
        if (Pa_Initialize() != paNoError) {
                return;
        }
@@ -541,7 +492,7 @@ ARDOUR::get_jack_audio_driver_supports_setting_period_count (const string& drive
 bool
 ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names)
 {
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        server_names.push_back ("jackd.exe");
 #else
        server_names.push_back ("jackd");
@@ -556,7 +507,7 @@ 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);
+       setenv ("PATH", Searchpath(dirs).to_string().c_str(), 1);
 #else
        /* silence a compiler unused variable warning */
        (void) dirs;
@@ -579,9 +530,63 @@ ARDOUR::get_jack_server_dir_paths (vector<std::string>& server_dir_paths)
        server_dir_paths.push_back (Glib::path_get_dirname (execpath));
 #endif
 
-       SearchPath sp(string(g_getenv("PATH")));
+       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
 
-#ifdef WIN32
        gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL);
        if (install_dir) {
                sp.push_back (install_dir);
@@ -608,8 +613,7 @@ ARDOUR::get_jack_server_paths (const vector<std::string>& server_dir_paths,
                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);
+               find_files_matching_pattern (server_paths, server_dir_paths, *i);
        }
        return !server_paths.empty();
 }
@@ -690,11 +694,13 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
 
        args.push_back (options.server_path);
 
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
        // must use sync mode on windows
        args.push_back ("-S");
+#endif
 
-       // this needs to be added now on windows
+#if (defined PLATFORM_WINDOWS || defined __APPLE__)
+       // midi systems needs to be added before the audio driver for jack2
        if (!options.midi_driver.empty () && options.midi_driver != get_none_string ()) {
                args.push_back ("-X");
                args.push_back (options.midi_driver);
@@ -736,14 +742,25 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
                args.push_back ("-v");
        }
 
-#ifndef WIN32
        if (options.temporary) {
                args.push_back ("-T");
        }
-#endif
+
+       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;
        }
@@ -751,60 +768,71 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
        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 (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.output_device, command_line_output_device_name)) {
-               return false;
-       }
 
-       if (options.input_device.empty()) {
-               // playback only
-               if (options.output_device.empty()) {
+               if (!get_jack_command_line_audio_device_name (options.driver,
+                                       options.input_device, command_line_input_device_name)) {
                        return false;
                }
-               args.push_back ("-P");
-       } else if (options.output_device.empty()) {
-               // capture only
-               if (options.input_device.empty()) {
+
+               if (!get_jack_command_line_audio_device_name (options.driver,
+                                       options.output_device, command_line_output_device_name)) {
                        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);
+
+               if (options.input_device.empty()) {
+                       // playback only
+                       if (options.output_device.empty()) {
+                               return false;
+                       }
                        args.push_back ("-P");
-                       args.push_back (command_line_output_device_name);
-               } else {
-                       return false;
+               } 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.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 (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));
+               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");
@@ -824,9 +852,11 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
                }
        }
 
-       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 != 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) {
@@ -850,22 +880,24 @@ ARDOUR::get_jack_command_line_string (JackCommandLineOptions& options, string& c
                }
        }
 
-       if (options.driver == alsa_driver_name || options.driver == coreaudio_driver_name) {
+       if (options.driver == alsa_driver_name) {
 
-               if (!options.midi_driver.empty() && options.midi_driver != get_none_string ()) {
-                       args.push_back ("-X");
-                       args.push_back (options.midi_driver);
+               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();) {
-#ifdef WIN32
-               oss << quote_string (*i);
-#else
-               oss << *i;
-#endif
+               if (i->find_first_of(' ') != string::npos) {
+                       oss << "\"" << *i << "\"";
+               } else {
+                       oss << *i;
+               }
                if (++i != args.end()) oss << ' ';
        }
 
@@ -894,31 +926,28 @@ ARDOUR::get_jack_server_user_config_file_path ()
 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) {
+       if (!g_file_set_contents (config_file_path.c_str(), command_line.c_str(), -1, NULL)) {
                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 () 
+ARDOUR::enumerate_midi_options ()
 {
        if (midi_options.empty()) {
 #ifdef HAVE_ALSA
-               midi_options.push_back (make_pair (_("ALSA raw devices"), alsaraw_midi_driver_name));
-               midi_options.push_back (make_pair (_("ALSA sequencer"), alsaseq_midi_driver_name));
+               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
+#if (defined PLATFORM_WINDOWS && defined 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));
+               midi_options.push_back (make_pair (_("System MIDI (MME)"), winmme_midi_driver_name));
 #endif
 #ifdef __APPLE__
                midi_options.push_back (make_pair (_("CoreMIDI"), coremidi_midi_driver_name));
@@ -927,12 +956,12 @@ ARDOUR::enumerate_midi_options ()
 
        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);
        }
 
+       v.push_back (get_none_string());
+
        return v;
 }