Engine Dialog: use new API
[ardour.git] / gtk2_ardour / engine_dialog.cc
index 27f46456fcd8c05e604bf31615b932aea9b5a267..980f72596b41c282d253b061c99669d5af9c8b61 100644 (file)
@@ -49,6 +49,7 @@
 #include "pbd/error.h"
 
 #include "opts.h"
+#include "debug.h"
 #include "ardour_ui.h"
 #include "engine_dialog.h"
 #include "gui_thread.h"
@@ -62,6 +63,8 @@ using namespace PBD;
 using namespace Glib;
 using namespace ARDOUR_UI_UTILS;
 
+#define DEBUG_ECONTROL(msg) DEBUG_TRACE (PBD::DEBUG::EngineControl, string_compose ("%1: %2\n", __LINE__, msg));
+
 static const unsigned int midi_tab = 2;
 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
 
@@ -95,6 +98,7 @@ EngineControl::EngineControl ()
        , _desired_sample_rate (0)
        , started_at_least_once (false)
        , queue_device_changed (false)
+       , block_signals(0)
 {
        using namespace Notebook_Helpers;
        vector<string> backend_names;
@@ -279,47 +283,106 @@ EngineControl::EngineControl ()
        ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
 
        if (audio_setup) {
-               set_state (*audio_setup);
-       }
-
-       if (backend_combo.get_active_text().empty()) {
-               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-               backend_combo.set_active_text (backend_names.front());
+               if (!set_state (*audio_setup)) {
+                       set_default_state ();
+               }
+       } else {
+               set_default_state ();
        }
 
-       backend_changed ();
-
-       /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
-       if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
-               backend_combo.set_active_text (backend_names.back());
-               /* ignore: don't save state */
-               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-               backend_changed ();
-       }
+       connect_changed_signals ();
 
+       notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
 
-       /* Connect to signals */
+       connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
+       connect_disconnect_button.set_no_show_all();
 
-       backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
-       driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
-       sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
-       buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
-       device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
-       midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
+}
 
-       input_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::input_device_changed));
-       output_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::output_device_changed));
+void
+EngineControl::connect_changed_signals ()
+{
+       backend_combo_connection = backend_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::backend_changed));
+       driver_combo_connection = driver_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::driver_changed));
+       sample_rate_combo_connection = sample_rate_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
+       buffer_size_combo_connection = buffer_size_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
+       device_combo_connection = device_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::device_changed));
+       midi_option_combo_connection = midi_option_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::midi_option_changed));
+
+       input_device_combo_connection = input_device_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::input_device_changed));
+       output_device_combo_connection = output_device_combo.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::output_device_changed));
+
+       input_latency_connection = input_latency.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::parameter_changed));
+       output_latency_connection = output_latency.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::parameter_changed));
+       input_channels_connection = input_channels.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::parameter_changed));
+       output_channels_connection = output_channels.signal_changed ().connect (
+           sigc::mem_fun (*this, &EngineControl::parameter_changed));
+}
 
-       input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
-       output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
-       input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
-       output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
+void
+EngineControl::block_changed_signals ()
+{
+       if (block_signals++ == 0) {
+               DEBUG_ECONTROL ("Blocking changed signals");
+               backend_combo_connection.block ();
+               driver_combo_connection.block ();
+               sample_rate_combo_connection.block ();
+               buffer_size_combo_connection.block ();
+               device_combo_connection.block ();
+               input_device_combo_connection.block ();
+               output_device_combo_connection.block ();
+               midi_option_combo_connection.block ();
+               input_latency_connection.block ();
+               output_latency_connection.block ();
+               input_channels_connection.block ();
+               output_channels_connection.block ();
+       }
+}
 
-       notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
+void
+EngineControl::unblock_changed_signals ()
+{
+       if (--block_signals == 0) {
+               DEBUG_ECONTROL ("Unblocking changed signals");
+               backend_combo_connection.unblock ();
+               driver_combo_connection.unblock ();
+               sample_rate_combo_connection.unblock ();
+               buffer_size_combo_connection.unblock ();
+               device_combo_connection.unblock ();
+               input_device_combo_connection.unblock ();
+               output_device_combo_connection.unblock ();
+               midi_option_combo_connection.unblock ();
+               input_latency_connection.unblock ();
+               output_latency_connection.unblock ();
+               input_channels_connection.unblock ();
+               output_channels_connection.unblock ();
+       }
+}
 
-       connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
-       connect_disconnect_button.set_no_show_all();
+EngineControl::SignalBlocker::SignalBlocker (EngineControl& engine_control,
+                                             const std::string& reason)
+    : ec (engine_control)
+    , m_reason (reason)
+{
+       DEBUG_ECONTROL (string_compose ("SignalBlocker: %1", m_reason));
+       ec.block_changed_signals ();
+}
 
+EngineControl::SignalBlocker::~SignalBlocker ()
+{
+       DEBUG_ECONTROL (string_compose ("~SignalBlocker: %1", m_reason));
+       ec.unblock_changed_signals ();
 }
 
 void
@@ -750,6 +813,7 @@ EngineControl::refresh_midi_display (std::string focus)
 void
 EngineControl::backend_changed ()
 {
+       SignalBlocker blocker (*this, "backend_changed");
        string backend_name = backend_combo.get_active_text();
        boost::shared_ptr<ARDOUR::AudioBackend> backend;
 
@@ -759,6 +823,8 @@ EngineControl::backend_changed ()
                return;
        }
 
+       DEBUG_ECONTROL (string_compose ("Backend name: %1", backend_name));
+
        _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
 
        build_notebook ();
@@ -766,27 +832,8 @@ EngineControl::backend_changed ()
        _midi_devices.clear();
 
        if (backend->requires_driver_selection()) {
-               vector<string> drivers = backend->enumerate_drivers();
-               driver_combo.set_sensitive (true);
-
-               if (!drivers.empty()) {
-                       {
-                               string current_driver;
-                               current_driver = backend->driver_name ();
-
-                               // driver might not have been set yet
-                               if (current_driver == "") {
-                                       current_driver = driver_combo.get_active_text ();
-                                       if (current_driver == "")
-                                               // driver has never been set, make sure it's not blank
-                                               current_driver = drivers.front ();
-                               }
-
-                               PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                               set_popdown_strings (driver_combo, drivers);
-                               driver_combo.set_active_text (current_driver);
-                       }
-
+               if (set_driver_popdown_strings ()) {
+                       driver_combo.set_sensitive (true);
                        driver_changed ();
                }
 
@@ -798,6 +845,23 @@ EngineControl::backend_changed ()
                list_devices ();
        }
 
+       update_midi_options ();
+
+       connect_disconnect_button.hide();
+
+       midi_option_changed();
+
+       started_at_least_once = false;
+
+       if (!ignore_changes) {
+               maybe_display_saved_state ();
+       }
+}
+
+void
+EngineControl::update_midi_options ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        vector<string> midi_options = backend->enumerate_midi_options();
 
        if (midi_options.size() == 1) {
@@ -812,16 +876,6 @@ EngineControl::backend_changed ()
                        midi_option_combo.set_sensitive (false);
                }
        }
-
-       connect_disconnect_button.hide();
-
-       midi_option_changed();
-
-       started_at_least_once = false;
-
-       if (!ignore_changes) {
-               maybe_display_saved_state ();
-       }
 }
 
 bool
@@ -842,20 +896,33 @@ EngineControl::print_channel_count (Gtk::SpinButton* sb)
        return true;
 }
 
+// @return true if there are drivers available
 bool
 EngineControl::set_driver_popdown_strings ()
 {
-       string backend_name = backend_combo.get_active_text();
-       boost::shared_ptr<ARDOUR::AudioBackend> backend;
+       DEBUG_ECONTROL ("set_driver_popdown_strings");
+       boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+       vector<string> drivers = backend->enumerate_drivers();
 
-       if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
-               /* eh? setting the backend failed... how ? */
-               /* A: stale config contains a backend that does not exist in current build */
+       if (drivers.empty ()) {
+               // This is an error...?
                return false;
        }
 
-       vector<string> drivers = backend->enumerate_drivers();
+       string current_driver = backend->driver_name ();
+
+       DEBUG_ECONTROL (string_compose ("backend->driver_name: %1", current_driver));
+
+       if (std::find (drivers.begin (), drivers.end (), current_driver) ==
+           drivers.end ()) {
+
+               current_driver = drivers.front ();
+       }
+
        set_popdown_strings (driver_combo, drivers);
+       DEBUG_ECONTROL (
+           string_compose ("driver_combo.set_active_text: %1", current_driver));
+       driver_combo.set_active_text (current_driver);
        return true;
 }
 
@@ -863,6 +930,7 @@ EngineControl::set_driver_popdown_strings ()
 bool
 EngineControl::set_device_popdown_strings ()
 {
+       DEBUG_ECONTROL ("set_device_popdown_strings");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
 
@@ -881,43 +949,35 @@ EngineControl::set_device_popdown_strings ()
                available_devices.push_back (i->name);
        }
 
+       if (available_devices.empty ()) {
+               return false;
+       }
 
-       if (!available_devices.empty()) {
+       string current_device = backend->device_name ();
 
-               {
-                       string current_device, found_device;
-                       current_device = device_combo.get_active_text ();
-                       if (current_device == "") {
-                               current_device = backend->device_name ();
-                       }
-
-                       // Make sure that the active text is still relevant for this
-                       // device (it might only be relevant to the previous device!!)
-                       for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
-                               if (*i == current_device)
-                                       found_device = current_device;
-                       }
-                       if (found_device == "")
-                               // device has never been set (or was not relevant
-                               // for this backend) Let's make sure it's not blank
-                               current_device = available_devices.front ();
+       // Make sure that backend->device_name () is a valid
+       // device, the backend may not return a valid device if it hasn't
+       // been set yet.
+       if (std::find (available_devices.begin (),
+                      available_devices.end (),
+                      current_device) == available_devices.end ()) {
 
-                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                       set_popdown_strings (device_combo, available_devices);
+               current_device = available_devices.front ();
+       }
 
-                       device_combo.set_active_text (current_device);
-               }
+       set_popdown_strings (device_combo, available_devices);
+       DEBUG_ECONTROL (
+           string_compose ("set device_combo active text: %1", current_device));
 
-               device_changed ();
-               return true;
-       }
-       return false;
+       device_combo.set_active_text (current_device);
+       return true;
 }
 
 // @return true if there are input devices available
 bool
 EngineControl::set_input_device_popdown_strings ()
 {
+       DEBUG_ECONTROL ("set_input_device_popdown_strings");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_input_devices ();
 
@@ -927,43 +987,35 @@ EngineControl::set_input_device_popdown_strings ()
                available_devices.push_back (i->name);
        }
 
-       if (!available_devices.empty()) {
-
-               {
-                       string current_device, found_device;
-                       current_device = input_device_combo.get_active_text ();
-                       if (current_device == "") {
-                               current_device = backend->input_device_name ();
-                       }
-
-                       // Make sure that the active text is still relevant for this
-                       // device (it might only be relevant to the previous device!!)
-                       for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
-                               if (*i == current_device)
-                                       found_device = current_device;
-                       }
-                       if (found_device == "")
-                               // device has never been set (or was not relevant
-                               // for this backend) Let's make sure it's not blank
-                               current_device = available_devices.front ();
+       if (available_devices.empty()) {
+               return false;
+       }
 
-                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                       set_popdown_strings (input_device_combo, available_devices);
+       string current_device = backend->input_device_name ();
 
-                       input_device_combo.set_active_text (current_device);
-               }
+       // Make sure that backend->input_device_name () is a valid
+       // device, the backend may not return a valid device if it hasn't
+       // been set yet.
+       if (std::find (available_devices.begin (),
+                      available_devices.end (),
+                      current_device) == available_devices.end ()) {
 
-               device_changed ();
-               return true;
+               current_device = available_devices.front ();
        }
 
-       return false;
+       set_popdown_strings (input_device_combo, available_devices);
+
+       DEBUG_ECONTROL (
+           string_compose ("set input_device_combo active text: %1", current_device));
+       input_device_combo.set_active_text (current_device);
+       return true;
 }
 
 // @return true if there are output devices available
 bool
 EngineControl::set_output_device_popdown_strings ()
 {
+       DEBUG_ECONTROL ("set_output_device_popdown_strings");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_output_devices ();
 
@@ -973,42 +1025,34 @@ EngineControl::set_output_device_popdown_strings ()
                available_devices.push_back (i->name);
        }
 
-       if (!available_devices.empty()) {
-
-               {
-                       string current_device, found_device;
-                       current_device = output_device_combo.get_active_text ();
-                       if (current_device == "") {
-                               current_device = backend->output_device_name ();
-                       }
-
-                       // Make sure that the active text is still relevant for this
-                       // device (it might only be relevant to the previous device!!)
-                       for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
-                               if (*i == current_device)
-                                       found_device = current_device;
-                       }
-                       if (found_device == "")
-                               // device has never been set (or was not relevant
-                               // for this backend) Let's make sure it's not blank
-                               current_device = available_devices.front ();
+       if (available_devices.empty()) {
+               return false;
+       }
 
-                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                       set_popdown_strings (output_device_combo, available_devices);
+       string current_device = backend->output_device_name ();
 
-                       output_device_combo.set_active_text (current_device);
-               }
+       // Make sure that backend->output_device_name () is a valid
+       // device, the backend may not return a valid device if it hasn't
+       // been set yet.
+       if (std::find (available_devices.begin (),
+                      available_devices.end (),
+                      current_device) == available_devices.end ()) {
 
-               device_changed ();
-               return true;
+               current_device = available_devices.front ();
        }
 
-       return false;
+       set_popdown_strings (output_device_combo, available_devices);
+
+       DEBUG_ECONTROL (
+           string_compose ("set output_device_combo active text: %1", current_device));
+       output_device_combo.set_active_text (current_device);
+       return true;
 }
 
 void
 EngineControl::list_devices ()
 {
+       DEBUG_ECONTROL ("list_devices");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
@@ -1025,6 +1069,8 @@ EngineControl::list_devices ()
        }
 
        if (devices_available) {
+               device_changed ();
+
                input_latency.set_sensitive (true);
                output_latency.set_sensitive (true);
                input_channels.set_sensitive (true);
@@ -1063,6 +1109,7 @@ EngineControl::list_devices ()
 void
 EngineControl::driver_changed ()
 {
+       SignalBlocker blocker (*this, "driver_changed");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
@@ -1074,27 +1121,55 @@ EngineControl::driver_changed ()
        }
 }
 
+vector<float>
+EngineControl::get_sample_rates_for_all_devices ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend =
+           ARDOUR::AudioEngine::instance ()->current_backend ();
+       vector<float> all_rates;
+
+       if (backend->use_separate_input_and_output_devices ()) {
+               all_rates = backend->available_sample_rates (get_input_device_name (), get_output_device_name ());
+       } else {
+               all_rates = backend->available_sample_rates (get_device_name ());
+       }
+       return all_rates;
+}
+
+vector<float>
+EngineControl::get_default_sample_rates ()
+{
+       vector<float> rates;
+       rates.push_back (8000.0f);
+       rates.push_back (16000.0f);
+       rates.push_back (32000.0f);
+       rates.push_back (44100.0f);
+       rates.push_back (48000.0f);
+       rates.push_back (88200.0f);
+       rates.push_back (96000.0f);
+       rates.push_back (192000.0f);
+       rates.push_back (384000.0f);
+       return rates;
+}
+
 void
-EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
+EngineControl::set_samplerate_popdown_strings ()
 {
+       DEBUG_ECONTROL ("set_samplerate_popdown_strings");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        string desired;
        vector<float> sr;
        vector<string> s;
 
        if (_have_control) {
-               sr = backend->available_sample_rates (device_name);
+               sr = get_sample_rates_for_all_devices ();
+               // currently possible if both devices are set to "None" and the backend
+               // returns no supported rates for both devices
+               if (sr.empty()) {
+                       sr = get_default_sample_rates ();
+               }
        } else {
-
-               sr.push_back (8000.0f);
-               sr.push_back (16000.0f);
-               sr.push_back (32000.0f);
-               sr.push_back (44100.0f);
-               sr.push_back (48000.0f);
-               sr.push_back (88200.0f);
-               sr.push_back (96000.0f);
-               sr.push_back (192000.0f);
-               sr.push_back (384000.0f);
+               sr = get_default_sample_rates ();
        }
 
        for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
@@ -1108,8 +1183,14 @@ EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
                sample_rate_combo.set_sensitive (true);
                set_popdown_strings (sample_rate_combo, s);
 
-               if (desired.empty()) {
-                       sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
+               if (desired.empty ()) {
+                       float new_active_sr = backend->default_sample_rate ();
+
+                       if (std::find (sr.begin (), sr.end (), new_active_sr) == sr.end ()) {
+                               new_active_sr = sr.front ();
+                       }
+
+                       sample_rate_combo.set_active_text (rate_as_string (new_active_sr));
                } else {
                        sample_rate_combo.set_active_text (desired);
                }
@@ -1119,33 +1200,69 @@ EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
        }
 }
 
+vector<uint32_t>
+EngineControl::get_buffer_sizes_for_all_devices ()
+{
+       boost::shared_ptr<ARDOUR::AudioBackend> backend =
+           ARDOUR::AudioEngine::instance ()->current_backend ();
+       vector<uint32_t> all_sizes;
+
+       if (backend->use_separate_input_and_output_devices ()) {
+               all_sizes = backend->available_buffer_sizes (get_input_device_name (), get_output_device_name ());
+       } else {
+               all_sizes = backend->available_buffer_sizes (get_device_name ());
+       }
+       return all_sizes;
+}
+
+vector<uint32_t>
+EngineControl::get_default_buffer_sizes ()
+{
+       vector<uint32_t> sizes;
+       sizes.push_back (8);
+       sizes.push_back (16);
+       sizes.push_back (32);
+       sizes.push_back (64);
+       sizes.push_back (128);
+       sizes.push_back (256);
+       sizes.push_back (512);
+       sizes.push_back (1024);
+       sizes.push_back (2048);
+       sizes.push_back (4096);
+       sizes.push_back (8192);
+       return sizes;
+}
+
 void
-EngineControl::set_buffersize_popdown_strings (const std::string& device_name)
+EngineControl::set_buffersize_popdown_strings ()
 {
+       DEBUG_ECONTROL ("set_buffersize_popdown_strings");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        vector<uint32_t> bs;
        vector<string> s;
+       string device_name;
 
        if (_have_control) {
-               bs = backend->available_buffer_sizes (device_name);
+               bs = get_buffer_sizes_for_all_devices ();
+               // currently possible if both devices are set to "None" and the backend
+               // returns no supported sizes for both devices
+               if (bs.empty()) {
+                       bs = get_default_buffer_sizes ();
+               }
        } else if (backend->can_change_buffer_size_when_running()) {
-               bs.push_back (8);
-               bs.push_back (16);
-               bs.push_back (32);
-               bs.push_back (64);
-               bs.push_back (128);
-               bs.push_back (256);
-               bs.push_back (512);
-               bs.push_back (1024);
-               bs.push_back (2048);
-               bs.push_back (4096);
-               bs.push_back (8192);
-       }
-       s.clear ();
+               bs = get_default_buffer_sizes ();
+       }
+
        for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
                s.push_back (bufsize_as_string (*x));
        }
 
+       if (backend->use_separate_input_and_output_devices ()) {
+               device_name = get_input_device_name ();
+       } else {
+               device_name = get_device_name ();
+       }
+
        if (!s.empty()) {
                buffer_size_combo.set_sensitive (true);
                set_popdown_strings (buffer_size_combo, s);
@@ -1165,6 +1282,7 @@ EngineControl::set_buffersize_popdown_strings (const std::string& device_name)
 void
 EngineControl::device_changed ()
 {
+       SignalBlocker blocker (*this, "device_changed");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
@@ -1207,11 +1325,8 @@ EngineControl::device_changed ()
                 */
                PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
 
-               /* backends that support separate devices, need to ignore
-                * the device-name - and use the devies set above
-                */
-               set_samplerate_popdown_strings (device_name_in);
-               set_buffersize_popdown_strings (device_name_in);
+               set_samplerate_popdown_strings ();
+               set_buffersize_popdown_strings ();
                /* XXX theoretically need to set min + max channel counts here
                */
 
@@ -1228,12 +1343,14 @@ EngineControl::device_changed ()
 void
 EngineControl::input_device_changed ()
 {
+       DEBUG_ECONTROL ("input_device_changed");
        device_changed ();
 }
 
 void
 EngineControl::output_device_changed ()
 {
+       DEBUG_ECONTROL ("output_device_changed");
        device_changed ();
 }
 
@@ -1251,6 +1368,7 @@ EngineControl::bufsize_as_string (uint32_t sz)
 void
 EngineControl::sample_rate_changed ()
 {
+       DEBUG_ECONTROL ("sample_rate_changed");
        /* reset the strings for buffer size to show the correct msec value
           (reflecting the new sample rate).
         */
@@ -1262,13 +1380,14 @@ EngineControl::sample_rate_changed ()
 void
 EngineControl::buffer_size_changed ()
 {
+       DEBUG_ECONTROL ("buffer_size_changed");
        show_buffer_duration ();
 }
 
 void
 EngineControl::show_buffer_duration ()
 {
-
+       DEBUG_ECONTROL ("show_buffer_duration");
        /* buffer sizes  - convert from just samples to samples + msecs for
         * the displayed string
         */
@@ -1302,6 +1421,7 @@ EngineControl::show_buffer_duration ()
 void
 EngineControl::midi_option_changed ()
 {
+       DEBUG_ECONTROL ("midi_option_changed");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
        assert (backend);
 
@@ -1540,6 +1660,22 @@ EngineControl::get_state ()
 }
 
 void
+EngineControl::set_default_state ()
+{
+       vector<string> backend_names;
+       vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
+
+       for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
+               backend_names.push_back ((*b)->name);
+       }
+       backend_combo.set_active_text (backend_names.front());
+
+       // We could set default backends per platform etc here
+
+       backend_changed ();
+}
+
+bool
 EngineControl::set_state (const XMLNode& root)
 {
        XMLNodeList          clist, cclist;
@@ -1551,7 +1687,7 @@ EngineControl::set_state (const XMLNode& root)
        fprintf (stderr, "EngineControl::set_state\n");
 
        if (root.name() != "AudioMIDISetup") {
-               return;
+               return false;
        }
 
        clist = root.children();
@@ -1704,37 +1840,105 @@ EngineControl::set_state (const XMLNode& root)
        for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
 
                if ((*i)->active) {
-                       PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-                       backend_combo.set_active_text ((*i)->backend);
+                       return set_current_state (*i);
+               }
+       }
+       return false;
+}
 
-                       /* The driver popdown strings need to be populated now so that
-                        * set_active_text will actually set a valid entry. Then
-                        * backend_changed() will populate all the other combo's so they
-                        * can also be set to valid entries and the state will be restored
-                        * correctly.
-                        */
-                       if (!(*i)->driver.empty()) {
-                               set_driver_popdown_strings ();
-                       }
-                       driver_combo.set_active_text ((*i)->driver);
-                       backend_changed ();
-
-                       device_combo.set_active_text ((*i)->device);
-                       input_device_combo.set_active_text ((*i)->input_device);
-                       output_device_combo.set_active_text ((*i)->output_device);
-                       sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
-                       set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
-                       input_latency.set_value ((*i)->input_latency);
-                       output_latency.set_value ((*i)->output_latency);
-                       midi_option_combo.set_active_text ((*i)->midi_option);
-                       break;
+bool
+EngineControl::set_current_state (const State& state)
+{
+       DEBUG_ECONTROL ("set_current_state");
+
+       boost::shared_ptr<ARDOUR::AudioBackend> backend;
+
+       if (!(backend = ARDOUR::AudioEngine::instance ()->set_backend (
+                 state->backend, "ardour", ""))) {
+               DEBUG_ECONTROL (string_compose ("Unable to set backend to %1", state->backend));
+               // this shouldn't happen as the invalid backend names should have been
+               // removed from the list of states.
+               return false;
+       }
+
+       // now reflect the change in the backend in the GUI so backend_changed will
+       // do the right thing
+       backend_combo.set_active_text (state->backend);
+
+       if (!state->driver.empty ()) {
+               if (!backend->requires_driver_selection ()) {
+                       DEBUG_ECONTROL ("Backend should require driver selection");
+                       // A backend has changed from having driver selection to not having
+                       // it or someone has been manually editing a config file and messed
+                       // it up
+                       return false;
+               }
+
+               if (backend->set_driver (state->driver) != 0) {
+                       DEBUG_ECONTROL (string_compose ("Unable to set driver %1", state->driver));
+                       // Driver names for a backend have changed and the name in the
+                       // config file is now invalid or support for driver is no longer
+                       // included in the backend
+                       return false;
                }
+               // no need to set the driver_combo as backend_changed will use
+               // backend->driver_name to set the active driver
        }
+
+       if (!state->device.empty ()) {
+               if (backend->set_device_name (state->device) != 0) {
+                       DEBUG_ECONTROL (
+                           string_compose ("Unable to set device name %1", state->device));
+                       // device is no longer available on the system
+                       return false;
+               }
+               // no need to set active device as it will be picked up in
+               // via backend_changed ()/set_device_popdown_strings
+
+       } else {
+               // backend supports separate input/output devices
+               if (backend->set_input_device_name (state->input_device) != 0) {
+                       DEBUG_ECONTROL (string_compose ("Unable to set input device name %1",
+                                                       state->input_device));
+                       // input device is no longer available on the system
+                       return false;
+               }
+
+               if (backend->set_output_device_name (state->output_device) != 0) {
+                       DEBUG_ECONTROL (string_compose ("Unable to set output device name %1",
+                                                       state->input_device));
+                       // output device is no longer available on the system
+                       return false;
+               }
+               // no need to set active devices as it will be picked up in via
+               // backend_changed ()/set_*_device_popdown_strings
+       }
+
+       backend_changed ();
+
+       // Now restore the state of the rest of the controls
+
+       // We don't use a SignalBlocker as set_current_state is currently only
+       // called from set_state before any signals are connected. If at some point
+       // a more general named state mechanism is implemented and
+       // set_current_state is called while signals are connected then a
+       // SignalBlocker will need to be instantiated before setting these.
+
+       device_combo.set_active_text (state->device);
+       input_device_combo.set_active_text (state->input_device);
+       output_device_combo.set_active_text (state->output_device);
+       sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
+       set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
+       input_latency.set_value (state->input_latency);
+       output_latency.set_value (state->output_latency);
+       midi_option_combo.set_active_text (state->midi_option);
+       return true;
 }
 
 int
 EngineControl::push_state_to_backend (bool start)
 {
+       DEBUG_ECONTROL ("push_state_to_backend");
        boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
 
        if (!backend) {