Marginal cleanup.
[ardour.git] / gtk2_ardour / engine_dialog.cc
index c1ffcefb2b9306f1f0bc8da92fe67bb6495af9cc..6fe514b99198db87f9d79a16bf02eb3a14082099 100644 (file)
@@ -1,11 +1,34 @@
+/*
+    Copyright (C) 2010 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #include <vector>
 #include <cmath>
 #include <fstream>
 #include <map>
 
+#include <boost/scoped_ptr.hpp>
+
 #include <glibmm.h>
 #include <gtkmm/messagedialog.h>
-#include <pbd/xml++.h>
+
+#include "pbd/epa.h"
+#include "pbd/xml++.h"
 
 #ifdef __APPLE__
 #include <CoreAudio/CoreAudio.h>
 #include <alsa/asoundlib.h>
 #endif
 
-#include <ardour/profile.h>
 #include <jack/jack.h>
 
 #include <gtkmm/stock.h>
 #include <gtkmm2ext/utils.h>
 
-#include <pbd/convert.h>
-#include <pbd/error.h>
-#include <pbd/pathscanner.h>
+#include "pbd/convert.h"
+#include "pbd/error.h"
+#include "pbd/pathscanner.h"
 
 #ifdef __APPLE
 #include <CFBundle.h>
@@ -46,6 +68,10 @@ EngineControl::EngineControl ()
          priority_spinner (priority_adjustment),
          ports_adjustment (128, 8, 1024, 1, 16),
          ports_spinner (ports_adjustment),
+         input_latency_adjustment (0, 0, 99999, 1),
+         input_latency (input_latency_adjustment),
+         output_latency_adjustment (0, 0, 99999, 1),
+         output_latency (output_latency_adjustment),
          realtime_button (_("Realtime")),
          no_memory_lock_button (_("Do not lock memory")),
          unlock_memory_button (_("Unlock memory")),
@@ -65,7 +91,7 @@ EngineControl::EngineControl ()
          basic_packer (8, 2),
          options_packer (14, 2),
          device_packer (6, 2)
-#endif   
+#endif
 {
        using namespace Notebook_Helpers;
        Label* label;
@@ -115,6 +141,7 @@ EngineControl::EngineControl ()
 #else
        strings.push_back (X_("ALSA"));
        strings.push_back (X_("OSS"));
+       strings.push_back (X_("FreeBoB"));
        strings.push_back (X_("FFADO"));
 #endif
        strings.push_back (X_("NetJACK"));
@@ -122,76 +149,79 @@ EngineControl::EngineControl ()
        set_popdown_strings (driver_combo, strings);
        driver_combo.set_active_text (strings.front());
 
-       /* figure out available devices and set up interface_combo */
-
-       enumerate_devices ();
-       driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
+       driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
        driver_changed ();
 
        strings.clear ();
-       strings.push_back (_("Playback/Recording on 1 Device"));
-       strings.push_back (_("Playback/Recording on 2 Devices"));
+       strings.push_back (_("Playback/recording on 1 device"));
+       strings.push_back (_("Playback/recording on 2 devices"));
        strings.push_back (_("Playback only"));
        strings.push_back (_("Recording only"));
        set_popdown_strings (audio_mode_combo, strings);
        audio_mode_combo.set_active_text (strings.front());
 
-       audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
+       audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
        audio_mode_changed ();
 
+       strings.clear ();
+       strings.push_back (_("None"));
+       strings.push_back (_("seq"));
+       strings.push_back (_("raw"));
+       set_popdown_strings (midi_driver_combo, strings);
+       midi_driver_combo.set_active_text (strings.front ());
+
        row = 0;
 
-       label = manage (new Label (_("Driver")));
+       label = manage (left_aligned_label (_("Driver:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 
-       label = manage (new Label (_("Interface")));
+       label = manage (left_aligned_label (_("Audio Interface:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 
-       label = manage (new Label (_("Sample Rate")));
+       label = manage (left_aligned_label (_("Sample rate:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 
-       label = manage (new Label (_("Buffer size")));
+       label = manage (left_aligned_label (_("Buffer size:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 
 #ifndef __APPLE__
-       label = manage (new Label (_("Number of buffers")));
+       label = manage (left_aligned_label (_("Number of buffers:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        periods_spinner.set_value (2);
        row++;
 #endif
 
-       label = manage (new Label (_("Approximate latency")));
-       label->set_alignment (0.0, 0.5);
+       label = manage (left_aligned_label (_("Approximate latency:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 
-       sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
-       periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
-       period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
+       sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
+       periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
+       period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
        redisplay_latency();
        row++;
        /* no audio mode with CoreAudio, its duplex or nuthin' */
 
 #ifndef __APPLE__
-       label = manage (new Label (_("Audio Mode")));
+       label = manage (left_aligned_label (_("Audio mode:")));
        basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        row++;
 #endif
 
-       interface_combo.set_size_request (125, -1);
-       input_device_combo.set_size_request (125, -1);
-       output_device_combo.set_size_request (125, -1);
+       interface_combo.set_size_request (250, -1);
+       input_device_combo.set_size_request (250, -1);
+       output_device_combo.set_size_request (250, -1);
 
        /*
 
@@ -201,8 +231,8 @@ EngineControl::EngineControl ()
                stop_button.set_sensitive (false);
        }
 
-       start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
-       stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
+       start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
+       stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
        */
 
        button_box.pack_start (start_button, false, false);
@@ -218,9 +248,12 @@ EngineControl::EngineControl ()
        options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 
-       realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
+       realtime_button.set_active (true);
+       realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
        realtime_changed ();
 
+#if PROVIDE_TOO_MANY_OPTIONS
+
 #ifndef __APPLE__
        label = manage (new Label (_("Realtime Priority")));
        label->set_alignment (1.0, 0.5);
@@ -245,7 +278,7 @@ EngineControl::EngineControl ()
        ++row;
        options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
-#else 
+#else
        options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 #endif
@@ -265,15 +298,19 @@ EngineControl::EngineControl ()
        options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 
-       label = manage (new Label (_("Number of ports")));
-       label->set_alignment (1.0, 0.5);
+#endif /* PROVIDE_TOO_MANY_OPTIONS */
+       label = manage (left_aligned_label (_("Number of ports:")));
        options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
        options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 
+       label = manage (left_aligned_label (_("MIDI driver:")));
+       options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
+       options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
+       ++row;
+
 #ifndef __APPLE__
-       label = manage (new Label (_("Dither")));       
-       label->set_alignment (1.0, 0.5);
+       label = manage (left_aligned_label (_("Dither:")));
        options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
        options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
@@ -285,14 +322,13 @@ EngineControl::EngineControl ()
                fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
                /*NOTREACHED*/
        }
-       
+
        set_popdown_strings (serverpath_combo, server_strings);
        serverpath_combo.set_active_text (server_strings.front());
 
        if (server_strings.size() > 1) {
-               label = manage (new Label (_("Server:")));
+               label = manage (left_aligned_label (_("Server:")));
                options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
-               label->set_alignment (0.0, 0.5);
                options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
                ++row;
        }
@@ -303,36 +339,26 @@ EngineControl::EngineControl ()
        row = 0;
 
 #ifndef __APPLE__
-       label = manage (new Label (_("Input device")));
-       label->set_alignment (1.0, 0.5);
+       label = manage (left_aligned_label (_("Input device:")));
        device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
-       label = manage (new Label (_("Output device")));
-       label->set_alignment (1.0, 0.5);
+       label = manage (left_aligned_label (_("Output device:")));
        device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
-       device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);   
+       device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 #endif
-       label = manage (new Label (_("Input channels")));
-       label->set_alignment (1.0, 0.5);
-       device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
-       device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
-       ++row;
-       label = manage (new Label (_("Output channels")));
-       label->set_alignment (1.0, 0.5);
-       device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
-       device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
-       ++row;
-       label = manage (new Label (_("Hardware input latency (samples)")));
-       label->set_alignment (1.0, 0.5);
+       label = manage (left_aligned_label (_("Hardware input latency:")));
        device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
+       label = manage (left_aligned_label (_("samples")));
+       device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
-       label = manage (new Label (_("Hardware output latency (samples)")));
-       label->set_alignment (1.0, 0.5);
+       label = manage (left_aligned_label (_("Hardware output latency:")));
        device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
+       label = manage (left_aligned_label (_("samples")));
+       device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
 
        basic_hbox.pack_start (basic_packer, false, false);
@@ -361,33 +387,36 @@ EngineControl::build_command_line (vector<string>& cmd)
 {
        string str;
        string driver;
-       bool using_oss = false;
        bool using_alsa = false;
        bool using_coreaudio = false;
-       bool using_netjack = false;
-       bool using_ffado = false;
        bool using_dummy = false;
+       bool using_ffado = false;
 
        /* first, path to jackd */
 
        cmd.push_back (serverpath_combo.get_active_text ());
-       
+
        /* now jackd arguments */
 
        str = timeout_combo.get_active_text ();
+
        if (str != _("Ignore")) {
-               double secs;
+
+               double secs = 0;
                uint32_t msecs;
-               atof (str);
+               secs = atof (str);
                msecs = (uint32_t) floor (secs * 1000.0);
-               cmd.push_back ("-t");
-               cmd.push_back (to_string (msecs, std::dec));
+
+               if (msecs > 0) {
+                       cmd.push_back ("-t");
+                       cmd.push_back (to_string (msecs, std::dec));
+               }
        }
 
        if (no_memory_lock_button.get_active()) {
                cmd.push_back ("-m"); /* no munlock */
        }
-       
+
        cmd.push_back ("-p"); /* port max */
        cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
 
@@ -395,6 +424,8 @@ EngineControl::build_command_line (vector<string>& cmd)
                cmd.push_back ("-R");
                cmd.push_back ("-P");
                cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
+       } else {
+               cmd.push_back ("-r"); /* override jackd's default --realtime */
        }
 
        if (unlock_memory_button.get_active()) {
@@ -404,7 +435,7 @@ EngineControl::build_command_line (vector<string>& cmd)
        if (verbose_output_button.get_active()) {
                cmd.push_back ("-v");
        }
-       
+
        /* now add fixed arguments (not user-selectable) */
 
        cmd.push_back ("-T"); // temporary */
@@ -414,71 +445,105 @@ EngineControl::build_command_line (vector<string>& cmd)
        cmd.push_back ("-d");
 
        driver = driver_combo.get_active_text ();
+
        if (driver == X_("ALSA")) {
                using_alsa = true;
                cmd.push_back ("alsa");
        } else if (driver == X_("OSS")) {
-               using_oss = true;
                cmd.push_back ("oss");
        } else if (driver == X_("CoreAudio")) {
                using_coreaudio = true;
                cmd.push_back ("coreaudio");
        } else if (driver == X_("NetJACK")) {
-               using_netjack = true;
                cmd.push_back ("netjack");
+       } else if (driver == X_("FreeBoB")) {
+               cmd.push_back ("freebob");
        } else if (driver == X_("FFADO")) {
                using_ffado = true;
-               cmd.push_back ("ffado");
+               cmd.push_back ("firewire");
        } else if ( driver == X_("Dummy")) {
-      using_dummy = true;
-      cmd.push_back ("dummy");
+               using_dummy = true;
+               cmd.push_back ("dummy");
        }
 
        /* driver arguments */
 
        if (!using_coreaudio) {
                str = audio_mode_combo.get_active_text();
-               
+
                if (str == _("Playback/Recording on 1 Device")) {
-                       
+
                        /* relax */
-                       
+
                } else if (str == _("Playback/Recording on 2 Devices")) {
-                       
+
+                       string input_device = get_device_name (driver, input_device_combo.get_active_text());
+                       string output_device = get_device_name (driver, output_device_combo.get_active_text());
+
+                       if (input_device.empty() || output_device.empty()) {
+                               cmd.clear ();
+                               return;
+                       }
+
                        cmd.push_back ("-C");
-                       cmd.push_back (get_device_name (driver, input_device_combo.get_active_text()));
+                       cmd.push_back (input_device);
+
                        cmd.push_back ("-P");
-                       cmd.push_back (get_device_name (driver, output_device_combo.get_active_text()));
-                       
+                       cmd.push_back (output_device);
+
                } else if (str == _("Playback only")) {
                        cmd.push_back ("-P");
                } else if (str == _("Recording only")) {
                        cmd.push_back ("-C");
                }
 
-      if (! using_dummy ) {
-                  cmd.push_back ("-n");
-                  cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
-      }
+               if (!using_dummy) {
+                       cmd.push_back ("-n");
+                       cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
+               }
        }
 
        cmd.push_back ("-r");
        cmd.push_back (to_string (get_rate(), std::dec));
-       
+
        cmd.push_back ("-p");
        cmd.push_back (period_size_combo.get_active_text());
 
+       if (using_alsa || using_ffado || using_coreaudio) {
+
+               double val = input_latency_adjustment.get_value();
+
+                if (val) {
+                        cmd.push_back ("-I");
+                        cmd.push_back (to_string ((uint32_t) val, std::dec));
+                }
+
+                val = output_latency_adjustment.get_value();
+
+               if (val) {
+                        cmd.push_back ("-O");
+                        cmd.push_back (to_string ((uint32_t) val, std::dec));
+                }
+       }
+
        if (using_alsa) {
-               
+
                if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
+
+                       string device = get_device_name (driver, interface_combo.get_active_text());
+                       if (device.empty()) {
+                               cmd.clear ();
+                               return;
+                       }
+
                        cmd.push_back ("-d");
-                       cmd.push_back (get_device_name (driver, interface_combo.get_active_text()));
-               } 
+                       cmd.push_back (device);
+               }
 
                if (hw_meter_button.get_active()) {
                        cmd.push_back ("-M");
                }
-               
+
                if (hw_monitor_button.get_active()) {
                        cmd.push_back ("-H");
                }
@@ -497,22 +562,32 @@ EngineControl::build_command_line (vector<string>& cmd)
                if (force16bit_button.get_active()) {
                        cmd.push_back ("-S");
                }
-               
+
                if (soft_mode_button.get_active()) {
                        cmd.push_back ("-s");
                }
 
+               str = midi_driver_combo.get_active_text ();
+
+               if (str == _("seq")) {
+                       cmd.push_back ("-X seq");
+               } else if (str == _("raw")) {
+                       cmd.push_back ("-X raw");
+               }
        } else if (using_coreaudio) {
 
 #ifdef __APPLE__
                // note: older versions of the CoreAudio JACK backend use -n instead of -d here
-               cmd.push_back ("-d");
-               cmd.push_back (get_device_name (driver, interface_combo.get_active_text()));
-#endif
 
-       } else if (using_oss) {
+               string device = get_device_name (driver, interface_combo.get_active_text());
+               if (device.empty()) {
+                       cmd.clear ();
+                       return;
+               }
 
-       } else if (using_netjack) {
+               cmd.push_back ("-d");
+               cmd.push_back (device);
+#endif
 
        }
 }
@@ -520,6 +595,17 @@ EngineControl::build_command_line (vector<string>& cmd)
 bool
 EngineControl::engine_running ()
 {
+        EnvironmentalProtectionAgency* global_epa = EnvironmentalProtectionAgency::get_global_epa ();
+        boost::scoped_ptr<EnvironmentalProtectionAgency> current_epa;
+
+        /* revert all environment settings back to whatever they were when ardour started
+         */
+
+        if (global_epa) {
+                current_epa.reset (new EnvironmentalProtectionAgency(true)); /* will restore settings when we leave scope */
+                global_epa->restore ();
+        }
+
        jack_status_t status;
        jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
 
@@ -538,7 +624,11 @@ EngineControl::setup_engine ()
 
        build_command_line (args);
 
-       Glib::ustring jackdrc_path = Glib::get_home_dir();
+       if (args.empty()) {
+               return 1; // try again
+       }
+
+       std::string jackdrc_path = Glib::get_home_dir();
        jackdrc_path += "/.jackdrc";
 
        ofstream jackdrc (jackdrc_path.c_str());
@@ -546,10 +636,12 @@ EngineControl::setup_engine ()
                error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
                return -1;
        }
-
+       cerr << "JACK COMMAND: ";
        for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
+               cerr << (*i) << ' ';
                jackdrc << (*i) << ' ';
        }
+       cerr << endl;
        jackdrc << endl;
        jackdrc.close ();
 
@@ -567,30 +659,43 @@ EngineControl::realtime_changed ()
 }
 
 void
-EngineControl::enumerate_devices ()
+EngineControl::enumerate_devices (const string& driver)
 {
        /* note: case matters for the map keys */
 
+       if (driver == "CoreAudio") {
 #ifdef __APPLE__
-       devices["CoreAudio"] = enumerate_coreaudio_devices ();
+               devices[driver] = enumerate_coreaudio_devices ();
+#endif
+
+#ifndef __APPLE__
+       } else if (driver == "ALSA") {
+               devices[driver] = enumerate_alsa_devices ();
+       } else if (driver == "FreeBOB") {
+               devices[driver] = enumerate_freebob_devices ();
+       } else if (driver == "FFADO") {
+               devices[driver] = enumerate_ffado_devices ();
+       } else if (driver == "OSS") {
+               devices[driver] = enumerate_oss_devices ();
+       } else if (driver == "Dummy") {
+               devices[driver] = enumerate_dummy_devices ();
+       } else if (driver == "NetJACK") {
+               devices[driver] = enumerate_netjack_devices ();
+       }
 #else
-       devices["ALSA"] = enumerate_alsa_devices ();
-       devices["FFADO"] = enumerate_ffado_devices ();
-       devices["OSS"] = enumerate_oss_devices ();
-       devices["Dummy"] = enumerate_dummy_devices ();
-       devices["NetJACK"] = enumerate_netjack_devices ();
+        }
 #endif
 }
 
 #ifdef __APPLE__
-static OSStatus 
+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) 
+       if (res == noErr)
                CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
        CFRelease(UI);
        return res;
@@ -600,12 +705,12 @@ vector<string>
 EngineControl::enumerate_coreaudio_devices ()
 {
        vector<string> devs;
-       
+
        // Find out how many Core Audio devices are there, if any...
        // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
        OSStatus err;
        Boolean isWritable;
-       size_t outSize = sizeof(isWritable);
+       UInt32 outSize = sizeof(isWritable);
 
        backend_devs.clear ();
 
@@ -621,7 +726,7 @@ EngineControl::enumerate_coreaudio_devices ()
                if (err == noErr) {
                        // Look for the CoreAudio device name...
                        char coreDeviceName[256];
-                       size_t nameSize;
+                       UInt32 nameSize;
 
                        for (int i = 0; i < numCoreDevices; i++) {
 
@@ -657,11 +762,11 @@ EngineControl::enumerate_coreaudio_devices ()
 
                                                // 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) {
                                                        devs.push_back (coreDeviceName);
                                                        backend_devs.push_back (drivername);
-                                               } 
+                                               }
                                        }
                                }
                        }
@@ -669,6 +774,7 @@ EngineControl::enumerate_coreaudio_devices ()
                delete [] coreDeviceIDs;
        }
 
+
        if (devs.size() == 0) {
                MessageDialog msg (_("\
 You do not have any audio devices capable of\n\
@@ -681,7 +787,7 @@ have no duplex audio device.\n\n\
 Alternatively, if you really want just playback\n\
 or recording but not both, start JACK before running\n\
 Ardour and choose the relevant device then."
-                                          ), 
+                                          ),
                                   true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
                msg.set_title (_("No suitable audio devices"));
                msg.set_position (Gtk::WIN_POS_MOUSE);
@@ -689,6 +795,7 @@ Ardour and choose the relevant device then."
                exit (1);
        }
 
+
        return devs;
 }
 #else
@@ -717,28 +824,11 @@ EngineControl::enumerate_alsa_devices ()
 
                        while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
 
-                               bool have_playback = false;
-                               bool have_capture = false;
-
-                               /* find duplex devices only */
-
-                               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) {
-                                       have_capture = true;
-                               }
-
                                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) {
-                                       have_playback = true;
-                               }
-
-                               if (have_capture && have_playback) {
                                        devs.push_back (snd_pcm_info_get_name (pcminfo));
                                        devname += ',';
                                        devname += to_string (device, std::dec);
@@ -757,14 +847,17 @@ vector<string>
 EngineControl::enumerate_ffado_devices ()
 {
        vector<string> devs;
+       backend_devs.clear ();
        return devs;
 }
+
 vector<string>
 EngineControl::enumerate_freebob_devices ()
 {
        vector<string> devs;
        return devs;
 }
+
 vector<string>
 EngineControl::enumerate_oss_devices ()
 {
@@ -789,15 +882,20 @@ void
 EngineControl::driver_changed ()
 {
        string driver = driver_combo.get_active_text();
-       vector<string>& strings = devices[driver];
        string::size_type maxlen = 0;
-       int maxindex = -1;
        int n = 0;
 
+       enumerate_devices (driver);
+
+       vector<string>& strings = devices[driver];
+
+       if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
+               return;
+       }
+
        for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
                if ((*i).length() > maxlen) {
                        maxlen = (*i).length();
-                       maxindex = n;
                }
        }
 
@@ -810,7 +908,7 @@ EngineControl::driver_changed ()
                input_device_combo.set_active_text (strings.front());
                output_device_combo.set_active_text (strings.front());
        }
-       
+
        if (driver == "ALSA") {
                soft_mode_button.set_sensitive (true);
                force16bit_button.set_sensitive (true);
@@ -829,7 +927,14 @@ EngineControl::driver_changed ()
 uint32_t
 EngineControl::get_rate ()
 {
-       return atoi (sample_rate_combo.get_active_text ());
+       double r = atof (sample_rate_combo.get_active_text ());
+       /* the string may have been translated with an abbreviation for
+        * thousands, so use a crude heuristic to fix this.
+        */
+       if (r < 1000.0) {
+               r *= 1000.0;
+       }
+       return lrint (r);
 }
 
 void
@@ -847,12 +952,13 @@ EngineControl::redisplay_latency ()
        snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
 
        latency_label.set_text (buf);
+       latency_label.set_alignment (0, 0.5);
 }
 
 void
 EngineControl::audio_mode_changed ()
 {
-       Glib::ustring str = audio_mode_combo.get_active_text();
+       std::string str = audio_mode_combo.get_active_text();
 
        if (str == _("Playback/Recording on 1 Device")) {
                input_device_combo.set_sensitive (false);
@@ -862,12 +968,14 @@ EngineControl::audio_mode_changed ()
                output_device_combo.set_sensitive (true);
        } else if (str == _("Playback only")) {
                output_device_combo.set_sensitive (true);
+               input_device_combo.set_sensitive (false);
        } else if (str == _("Recording only")) {
                input_device_combo.set_sensitive (true);
+               output_device_combo.set_sensitive (false);
        }
 }
 
-static bool jack_server_filter(const string& str, void *arg)
+static bool jack_server_filter(const string& str, void */*arg*/)
 {
    return str == "jackd" || str == "jackdmp";
 }
@@ -879,23 +987,23 @@ EngineControl::find_jack_servers (vector<string>& strings)
        /* 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);
-       
-       Glib::ustring path (Glib::path_get_dirname (execpath));
+
+       string path (Glib::path_get_dirname (execpath));
        path += "/jackd";
 
        if (Glib::file_test (path, FILE_TEST_EXISTS)) {
                strings.push_back (path);
-       } 
+       }
 
        if (getenv ("ARDOUR_WITH_JACK")) {
                /* no other options - only use the JACK we supply */
                if (strings.empty()) {
-                       fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
+                       fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
                        /*NOTREACHED*/
                }
                return;
@@ -903,20 +1011,51 @@ EngineControl::find_jack_servers (vector<string>& strings)
 #else
        string path;
 #endif
-       
+
        PathScanner scanner;
        vector<string *> *jack_servers;
        std::map<string,int> un;
-       
-       path = getenv ("PATH");
+       char *p;
+       bool need_minimal_path = false;
+
+       p = getenv ("PATH");
+
+       if (p && *p) {
+               path = p;
+       } else {
+               need_minimal_path = true;
+       }
+
+#ifdef __APPLE__
+       // many mac users don't have PATH set up to include
+       // likely installed locations of JACK
+       need_minimal_path = true;
+#endif
+
+       if (need_minimal_path) {
+               if (path.empty()) {
+                       path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
+               } else {
+                       path += ":/usr/local/bin:/opt/local/bin";
+               }
+       }
+
+#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", path.c_str(), 1);
+#endif
 
        jack_servers = scanner (path, jack_server_filter, 0, false, true);
-       
+       if (!jack_servers) {
+               return;
+       }
+
        vector<string *>::iterator iter;
-       
+
        for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
                string p = **iter;
-               
+
                if (un[p]++ == 0) {
                        strings.push_back(p);
                }
@@ -930,23 +1069,29 @@ EngineControl::get_device_name (const string& driver, const string& human_readab
        vector<string>::iterator n;
        vector<string>::iterator i;
 
+       if (human_readable.empty()) {
+               /* this can happen if the user's .ardourrc file has a device name from
+                  another computer system in it
+               */
+               MessageDialog msg (_("You need to choose an audio device first."));
+               msg.run ();
+               return string();
+       }
+
        if (backend_devs.empty()) {
                return human_readable;
        }
-       
+
        for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
                if (human_readable == (*i)) {
                        return (*n);
                }
        }
-       
+
        if (i == devices[driver].end()) {
-               fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg;
-               /*NOTREACHED*/
+               warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
        }
 
-       /* keep gcc happy */
-
        return string();
 }
 
@@ -955,7 +1100,7 @@ EngineControl::get_state ()
 {
        XMLNode* root = new XMLNode ("AudioSetup");
        XMLNode* child;
-       Glib::ustring path;
+       std::string path;
 
        child = new XMLNode ("periods");
        child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
@@ -969,14 +1114,6 @@ EngineControl::get_state ()
        child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
        root->add_child_nocopy (*child);
 
-       child = new XMLNode ("inchannels");
-       child->add_property ("val", to_string (input_channels.get_value(), std::dec));
-       root->add_child_nocopy (*child);
-
-       child = new XMLNode ("outchannels");
-       child->add_property ("val", to_string (output_channels.get_value(), std::dec));
-       root->add_child_nocopy (*child);
-
        child = new XMLNode ("inlatency");
        child->add_property ("val", to_string (input_latency.get_value(), std::dec));
        root->add_child_nocopy (*child);
@@ -1056,7 +1193,11 @@ EngineControl::get_state ()
        child = new XMLNode ("outputdevice");
        child->add_property ("val", output_device_combo.get_active_text());
        root->add_child_nocopy (*child);
-       
+
+       child = new XMLNode ("mididriver");
+       child->add_property ("val", midi_driver_combo.get_active_text());
+       root->add_child_nocopy (*child);
+
        return *root;
 }
 
@@ -1066,19 +1207,24 @@ EngineControl::set_state (const XMLNode& root)
        XMLNodeList          clist;
        XMLNodeConstIterator citer;
        XMLNode* child;
-       XMLProperty* prop;
-
-   bool using_dummy = false;
+       XMLProperty* prop = NULL;
+       bool using_dummy = false;
+       bool using_ffado = false;
 
        int val;
        string strval;
 
-   if ( (child = root.child("driver"))){
-      prop = child->property("val");
-      if (prop && (prop->value() == "Dummy") ) {
-         using_dummy = true;
-      }
-   }
+       if ( (child = root.child ("driver"))){
+               prop = child->property("val");
+
+               if (prop && (prop->value() == "Dummy") ) {
+                       using_dummy = true;
+               }
+               if (prop && (prop->value() == "FFADO") ) {
+                       using_ffado = true;
+               }
+
+       }
 
        clist = root.children();
 
@@ -1089,12 +1235,20 @@ EngineControl::set_state (const XMLNode& root)
                prop = child->property ("val");
 
                if (!prop || prop->value().empty()) {
-         if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
-               continue;
+
+                       if (((using_dummy || using_ffado)
+                               && ( child->name() == "interface"
+                                       || child->name() == "inputdevice"
+                                       || child->name() == "outputdevice"))
+                               || child->name() == "timeout")
+                       {
+                               continue;
+                       }
+
                        error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
                        continue;
                }
-               
+
                strval = prop->value();
 
                /* adjustments/spinners */
@@ -1108,12 +1262,6 @@ EngineControl::set_state (const XMLNode& root)
                } else if (child->name() == "ports") {
                        val = atoi (strval);
                        ports_adjustment.set_value(val);
-               } else if (child->name() == "inchannels") {
-                       val = atoi (strval);
-                       input_channels.set_value(val);
-               } else if (child->name() == "outchannels") {
-                       val = atoi (strval);
-                       output_channels.set_value(val);
                } else if (child->name() == "inlatency") {
                        val = atoi (strval);
                        input_latency.set_value(val);
@@ -1157,24 +1305,34 @@ EngineControl::set_state (const XMLNode& root)
                } else if (child->name() == "periodsize") {
                        period_size_combo.set_active_text(strval);
                } else if (child->name() == "serverpath") {
-                       /* do not allow us to use a server path that doesn't
-                          exist on this system. this handles cases where
-                          the user has an RC file listing a serverpath
-                          from some other machine.
-                       */
-                       vector<string>::iterator x;
-                       for (x = server_strings.begin(); x != server_strings.end(); ++x) {
-                               if (*x == strval) {
-                                       break;
-                               }
-                       }
-                       if (x != server_strings.end()) {
-                               serverpath_combo.set_active_text (strval);
-                       } else {
-                               warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
-                                                            strval)
-                                       << endmsg;
-                       }
+
+                        /* only attempt to set this if we have bothered to look
+                           up server names already. otherwise this is all
+                           redundant (actually, all of this dialog/widget
+                           is redundant in that case ...)
+                        */
+
+                        if (!server_strings.empty()) {
+                                /* do not allow us to use a server path that doesn't
+                                   exist on this system. this handles cases where
+                                   the user has an RC file listing a serverpath
+                                   from some other machine.
+                                */
+                                vector<string>::iterator x;
+                                for (x = server_strings.begin(); x != server_strings.end(); ++x) {
+                                        if (*x == strval) {
+                                                break;
+                                        }
+                                }
+                                if (x != server_strings.end()) {
+                                        serverpath_combo.set_active_text (strval);
+                                } else {
+                                        warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
+                                                                   strval)
+                                                << endmsg;
+                                }
+                        }
+
                } else if (child->name() == "driver") {
                        driver_combo.set_active_text(strval);
                } else if (child->name() == "interface") {
@@ -1189,6 +1347,8 @@ EngineControl::set_state (const XMLNode& root)
                        input_device_combo.set_active_text(strval);
                } else if (child->name() == "outputdevice") {
                        output_device_combo.set_active_text(strval);
+               } else if (child->name() == "mididriver") {
+                       midi_driver_combo.set_active_text(strval);
                }
        }
 }