merge with master
[ardour.git] / gtk2_ardour / engine_dialog.cc
index 87431ee6c0878cb18630223af3c6d13023ebdff7..a9e04d8db0e65bcc3ae0f86df467b2f1845a5cc5 100644 (file)
@@ -1,10 +1,33 @@
+/*
+    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/epa.h"
 #include "pbd/xml++.h"
 
 #ifdef __APPLE__
 #include <CoreFoundation/CFString.h>
 #include <sys/param.h>
 #include <mach-o/dyld.h>
-#else
+#elif !defined(__FreeBSD__)
 #include <alsa/asoundlib.h>
 #endif
 
-#include "ardour/profile.h"
 #include <jack/jack.h>
 
 #include <gtkmm/stock.h>
 #include <gtkmm2ext/utils.h>
 
+#include "ardour/rc_configuration.h"
+
 #include "pbd/convert.h"
 #include "pbd/error.h"
 #include "pbd/pathscanner.h"
@@ -42,10 +66,12 @@ using namespace Glib;
 EngineControl::EngineControl ()
        : periods_adjustment (2, 2, 16, 1, 2),
          periods_spinner (periods_adjustment),
-         priority_adjustment (60, 10, 90, 1, 10),
-         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")),
@@ -57,7 +83,7 @@ EngineControl::EngineControl ()
          verbose_output_button (_("Verbose output")),
          start_button (_("Start")),
          stop_button (_("Stop")),
-#ifdef __APPLE__
+#ifdef __APPLE___
          basic_packer (5, 2),
          options_packer (4, 2),
          device_packer (4, 2)
@@ -113,8 +139,11 @@ EngineControl::EngineControl ()
 #ifdef __APPLE__
        strings.push_back (X_("CoreAudio"));
 #else
+#ifndef __FreeBSD__
        strings.push_back (X_("ALSA"));
+#endif
        strings.push_back (X_("OSS"));
+       strings.push_back (X_("FreeBoB"));
        strings.push_back (X_("FFADO"));
 #endif
        strings.push_back (X_("NetJACK"));
@@ -126,8 +155,8 @@ EngineControl::EngineControl ()
        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);
@@ -136,38 +165,44 @@ EngineControl::EngineControl ()
        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")));
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+       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++;
@@ -179,16 +214,16 @@ EngineControl::EngineControl ()
        row++;
        /* no audio mode with CoreAudio, its duplex or nuthin' */
 
-#ifndef __APPLE__
-       label = manage (new Label (_("Audio Mode")));
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+       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);
 
        /*
 
@@ -216,19 +251,10 @@ EngineControl::EngineControl ()
        ++row;
 
        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);
-       options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
-       options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
-       ++row;
-       priority_spinner.set_value (60);
-
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
        options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
        ++row;
        options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
@@ -266,15 +292,18 @@ EngineControl::EngineControl ()
        ++row;
 
 #endif /* PROVIDE_TOO_MANY_OPTIONS */
-       label = manage (new Label (_("Number of ports")));
-       label->set_alignment (1.0, 0.5);
+       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;
 
-#ifndef __APPLE__
-       label = manage (new Label (_("Dither")));
-       label->set_alignment (1.0, 0.5);
+       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;
+
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+       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;
@@ -291,9 +320,8 @@ EngineControl::EngineControl ()
        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,37 +331,27 @@ EngineControl::EngineControl ()
        device_packer.set_spacings (6);
        row = 0;
 
-#ifndef __APPLE__
-       label = manage (new Label (_("Input device")));
-       label->set_alignment (1.0, 0.5);
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+       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);
        ++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);
@@ -350,6 +368,14 @@ EngineControl::EngineControl ()
 
        set_border_width (12);
        pack_start (notebook);
+
+       /* Pick up any existing audio setup configuration, if appropriate */
+
+       XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
+       
+       if (audio_setup) {
+               set_state (*audio_setup);
+       }
 }
 
 EngineControl::~EngineControl ()
@@ -362,12 +388,10 @@ 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 */
 
@@ -376,11 +400,14 @@ EngineControl::build_command_line (vector<string>& cmd)
        /* now jackd arguments */
 
        str = timeout_combo.get_active_text ();
+
        if (str != _("Ignore")) {
+
                double secs = 0;
                uint32_t msecs;
                secs = atof (str);
                msecs = (uint32_t) floor (secs * 1000.0);
+
                if (msecs > 0) {
                        cmd.push_back ("-t");
                        cmd.push_back (to_string (msecs, std::dec));
@@ -396,8 +423,8 @@ EngineControl::build_command_line (vector<string>& cmd)
 
        if (realtime_button.get_active()) {
                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()) {
@@ -417,31 +444,22 @@ 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;
-
-               /* do this until FFADO becomes the standard */
-
-               char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
-
-               if (hack) {
-                       cmd.push_back (hack);
-               } else {
-                       cmd.push_back ("freebob");
-               }
-
+               cmd.push_back ("firewire");
        } else if ( driver == X_("Dummy")) {
                using_dummy = true;
                cmd.push_back ("dummy");
@@ -452,11 +470,11 @@ EngineControl::build_command_line (vector<string>& cmd)
        if (!using_coreaudio) {
                str = audio_mode_combo.get_active_text();
 
-               if (str == _("Playback/Recording on 1 Device")) {
+               if (str == _("Playback/recording on 1 device")) {
 
                        /* relax */
 
-               } else if (str == _("Playback/Recording on 2 Devices")) {
+               } 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());
@@ -468,6 +486,7 @@ EngineControl::build_command_line (vector<string>& cmd)
 
                        cmd.push_back ("-C");
                        cmd.push_back (input_device);
+
                        cmd.push_back ("-P");
                        cmd.push_back (output_device);
 
@@ -477,7 +496,7 @@ EngineControl::build_command_line (vector<string>& cmd)
                        cmd.push_back ("-C");
                }
 
-               if (! using_dummy ) {
+               if (!using_dummy) {
                        cmd.push_back ("-n");
                        cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
                }
@@ -489,9 +508,26 @@ EngineControl::build_command_line (vector<string>& cmd)
        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")) {
+               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()) {
@@ -530,6 +566,13 @@ EngineControl::build_command_line (vector<string>& cmd)
                        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__
@@ -545,16 +588,31 @@ EngineControl::build_command_line (vector<string>& cmd)
                cmd.push_back (device);
 #endif
 
-       } else if (using_oss) {
-
-       } else if (using_netjack) {
-
        }
 }
 
+bool
+EngineControl::need_setup ()
+{
+       return !engine_running();
+}
+
 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, because ardour's startup script may have reset
+        * something in ways that interfere with finding/starting JACK.
+         */
+
+        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);
 
@@ -577,7 +635,7 @@ EngineControl::setup_engine ()
                return 1; // try again
        }
 
-       Glib::ustring jackdrc_path = Glib::get_home_dir();
+       std::string jackdrc_path = Glib::get_home_dir();
        jackdrc_path += "/.jackdrc";
 
        ofstream jackdrc (jackdrc_path.c_str());
@@ -585,12 +643,11 @@ 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 ();
 
@@ -599,14 +656,6 @@ EngineControl::setup_engine ()
        return 0;
 }
 
-void
-EngineControl::realtime_changed ()
-{
-#ifndef __APPLE__
-       priority_spinner.set_sensitive (realtime_button.get_active());
-#endif
-}
-
 void
 EngineControl::enumerate_devices (const string& driver)
 {
@@ -617,9 +666,11 @@ EngineControl::enumerate_devices (const string& driver)
                devices[driver] = enumerate_coreaudio_devices ();
 #endif
 
-#ifndef __APPLE__
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
        } 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") {
@@ -657,7 +708,7 @@ EngineControl::enumerate_coreaudio_devices ()
        // (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 ();
 
@@ -673,7 +724,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++) {
 
@@ -723,7 +774,7 @@ EngineControl::enumerate_coreaudio_devices ()
 
 
        if (devs.size() == 0) {
-               MessageDialog msg (_("\
+               MessageDialog msg (string_compose (_("\
 You do not have any audio devices capable of\n\
 simultaneous playback and recording.\n\n\
 Please use Applications -> Utilities -> Audio MIDI Setup\n\
@@ -733,8 +784,8 @@ Please send email to Apple and ask them why new Macs\n\
 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."
-                                          ),
+%1 and choose the relevant device then."
+                                                          ), PROGRAM_NAME),
                                   true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
                msg.set_title (_("No suitable audio devices"));
                msg.set_position (Gtk::WIN_POS_MOUSE);
@@ -746,6 +797,8 @@ Ardour and choose the relevant device then."
        return devs;
 }
 #else
+
+#if !defined(__FreeBSD__)
 vector<string>
 EngineControl::enumerate_alsa_devices ()
 {
@@ -771,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);
@@ -806,6 +842,7 @@ EngineControl::enumerate_alsa_devices ()
 
        return devs;
 }
+#endif
 
 vector<string>
 EngineControl::enumerate_ffado_devices ()
@@ -821,6 +858,7 @@ EngineControl::enumerate_freebob_devices ()
        vector<string> devs;
        return devs;
 }
+
 vector<string>
 EngineControl::enumerate_oss_devices ()
 {
@@ -846,22 +884,19 @@ EngineControl::driver_changed ()
 {
        string driver = driver_combo.get_active_text();
        string::size_type maxlen = 0;
-       int maxindex = -1;
        int n = 0;
 
        enumerate_devices (driver);
 
        vector<string>& strings = devices[driver];
 
-       if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
-               error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
+       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;
                }
        }
 
@@ -893,14 +928,21 @@ 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
 EngineControl::redisplay_latency ()
 {
        uint32_t rate = get_rate();
-#ifdef __APPLE_
+#if defined(__APPLE__) || defined(__FreeBSD__)
        float periods = 2;
 #else
        float periods = periods_adjustment.get_value();
@@ -911,23 +953,26 @@ 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")) {
+       if (str == _("Playback/recording on 1 device")) {
                input_device_combo.set_sensitive (false);
                output_device_combo.set_sensitive (false);
-       } else if (str == _("Playback/Recording on 2 Devices")) {
+       } else if (str == _("Playback/recording on 2 devices")) {
                input_device_combo.set_sensitive (true);
                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);
        }
 }
 
@@ -1003,6 +1048,9 @@ EngineControl::find_jack_servers (vector<string>& strings)
 #endif
 
        jack_servers = scanner (path, jack_server_filter, 0, false, true);
+       if (!jack_servers) {
+               return;
+       }
 
        vector<string *>::iterator iter;
 
@@ -1027,6 +1075,7 @@ EngineControl::get_device_name (const string& driver, const string& human_readab
                   another computer system in it
                */
                MessageDialog msg (_("You need to choose an audio device first."));
+                msg.set_position (WIN_POS_MOUSE);
                msg.run ();
                return string();
        }
@@ -1053,28 +1102,16 @@ 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));
        root->add_child_nocopy (*child);
 
-       child = new XMLNode ("priority");
-       child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
-       root->add_child_nocopy (*child);
-
        child = new XMLNode ("ports");
        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);
@@ -1155,6 +1192,10 @@ EngineControl::get_state ()
        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;
 }
 
@@ -1166,15 +1207,21 @@ EngineControl::set_state (const XMLNode& root)
        XMLNode* child;
        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 (prop && (prop->value() == "FFADO") ) {
+                       using_ffado = true;
+               }
+
        }
 
        clist = root.children();
@@ -1187,9 +1234,15 @@ EngineControl::set_state (const XMLNode& root)
 
                if (!prop || prop->value().empty()) {
 
-                       if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
-                               child->name() == "timeout")
+                       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;
                }
@@ -1201,18 +1254,9 @@ EngineControl::set_state (const XMLNode& root)
                if (child->name() == "periods") {
                        val = atoi (strval);
                        periods_adjustment.set_value(val);
-               } else if (child->name() == "priority") {
-                       val = atoi (strval);
-                       priority_adjustment.set_value(val);
                } 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);
@@ -1256,13 +1300,13 @@ EngineControl::set_state (const XMLNode& root)
                } else if (child->name() == "periodsize") {
                        period_size_combo.set_active_text(strval);
                } else if (child->name() == "serverpath") {
-                        
+
                         /* 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
@@ -1283,7 +1327,7 @@ EngineControl::set_state (const XMLNode& root)
                                                 << endmsg;
                                 }
                         }
-                        
+
                } else if (child->name() == "driver") {
                        driver_combo.set_active_text(strval);
                } else if (child->name() == "interface") {
@@ -1298,6 +1342,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);
                }
        }
 }