engine dialog fixes for multiple devices
[ardour.git] / gtk2_ardour / engine_dialog.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <exception>
21 #include <vector>
22 #include <cmath>
23 #include <fstream>
24 #include <map>
25
26 #include <boost/scoped_ptr.hpp>
27
28 #include <gtkmm/messagedialog.h>
29
30 #include "pbd/error.h"
31 #include "pbd/xml++.h"
32 #include "pbd/unwind.h"
33 #include "pbd/failed_constructor.h"
34
35 #include <gtkmm/alignment.h>
36 #include <gtkmm/stock.h>
37 #include <gtkmm/notebook.h>
38 #include <gtkmm2ext/utils.h>
39
40 #include "ardour/audio_backend.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/mtdm.h"
43 #include "ardour/mididm.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/types.h"
46 #include "ardour/profile.h"
47
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
50
51 #include "opts.h"
52 #include "ardour_ui.h"
53 #include "engine_dialog.h"
54 #include "gui_thread.h"
55 #include "utils.h"
56 #include "i18n.h"
57
58 using namespace std;
59 using namespace Gtk;
60 using namespace Gtkmm2ext;
61 using namespace PBD;
62 using namespace Glib;
63 using namespace ARDOUR_UI_UTILS;
64
65 static const unsigned int midi_tab = 2;
66 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
67
68 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
69
70 EngineControl::EngineControl ()
71         : ArdourDialog (_("Audio/MIDI Setup"))
72         , engine_status ("")
73         , basic_packer (9, 4)
74         , input_latency_adjustment (0, 0, 99999, 1)
75         , input_latency (input_latency_adjustment)
76         , output_latency_adjustment (0, 0, 99999, 1)
77         , output_latency (output_latency_adjustment)
78         , input_channels_adjustment (0, 0, 256, 1)
79         , input_channels (input_channels_adjustment)
80         , output_channels_adjustment (0, 0, 256, 1)
81         , output_channels (output_channels_adjustment)
82         , ports_adjustment (128, 8, 1024, 1, 16)
83         , ports_spinner (ports_adjustment)
84         , control_app_button (_("Device Control Panel"))
85         , midi_devices_button (_("Midi Device Setup"))
86         , lm_measure_label (_("Measure"))
87         , lm_use_button (_("Use results"))
88         , lm_back_button (_("Back to settings ... (ignore results)"))
89         , lm_button_audio (_("Calibrate Audio"))
90         , lm_table (12, 3)
91         , have_lm_results (false)
92         , lm_running (false)
93         , midi_back_button (_("Back to settings"))
94         , ignore_changes (0)
95         , _desired_sample_rate (0)
96         , started_at_least_once (false)
97         , queue_device_changed (false)
98 {
99         using namespace Notebook_Helpers;
100         vector<string> backend_names;
101         Label* label;
102         AttachOptions xopt = AttachOptions (FILL|EXPAND);
103         int row;
104
105         set_name (X_("AudioMIDISetup"));
106
107         /* the backend combo is the one thing that is ALWAYS visible */
108
109         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
110
111         if (backends.empty()) {
112                 MessageDialog msg (string_compose (_("No audio/MIDI backends detected. %1 cannot run\n\n(This is a build/packaging/system error. It should never happen.)"), PROGRAM_NAME));
113                 msg.run ();
114                 throw failed_constructor ();
115         }
116
117         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
118                 backend_names.push_back ((*b)->name);
119         }
120
121         set_popdown_strings (backend_combo, backend_names);
122         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
123
124         /* setup basic packing characteristics for the table used on the main
125          * tab of the notebook
126          */
127
128         basic_packer.set_spacings (6);
129         basic_packer.set_border_width (12);
130         basic_packer.set_homogeneous (false);
131
132         /* pack it in */
133
134         basic_hbox.pack_start (basic_packer, false, false);
135
136         /* latency measurement tab */
137
138         lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
139
140         row = 0;
141         lm_table.set_row_spacings (12);
142         lm_table.set_col_spacings (6);
143         lm_table.set_homogeneous (false);
144
145         lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
146         row++;
147
148         lm_preamble.set_width_chars (60);
149         lm_preamble.set_line_wrap (true);
150         lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
151
152         lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
153         row++;
154
155         Gtk::Label* preamble;
156         preamble = manage (new Label);
157         preamble->set_width_chars (60);
158         preamble->set_line_wrap (true);
159         preamble->set_markup (_("Select two channels below and connect them using a cable."));
160
161         lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
162         row++;
163
164         label = manage (new Label (_("Output channel")));
165         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
166
167         Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
168         misc_align->add (lm_output_channel_combo);
169         lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
170         ++row;
171
172         label = manage (new Label (_("Input channel")));
173         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
174
175         misc_align = manage (new Alignment (0.0, 0.5));
176         misc_align->add (lm_input_channel_combo);
177         lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
178         ++row;
179
180         xopt = AttachOptions(0);
181
182         lm_measure_label.set_padding (10, 10);
183         lm_measure_button.add (lm_measure_label);
184         lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
185         lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
186         lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
187
188         lm_use_button.set_sensitive (false);
189
190         /* Increase the default spacing around the labels of these three
191          * buttons
192          */
193
194         Gtk::Misc* l;
195
196         if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
197                 l->set_padding (10, 10);
198         }
199
200         if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
201                 l->set_padding (10, 10);
202         }
203
204         preamble = manage (new Label);
205         preamble->set_width_chars (60);
206         preamble->set_line_wrap (true);
207         preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
208         lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
209         row++;
210
211         preamble = manage (new Label);
212         preamble->set_width_chars (60);
213         preamble->set_line_wrap (true);
214         preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
215         lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
216
217         ++row; // skip a row in the table
218         ++row; // skip a row in the table
219
220         lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
221
222         ++row; // skip a row in the table
223         ++row; // skip a row in the table
224
225         lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
226         lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
227         lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
228
229         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
230
231         lm_vbox.set_border_width (12);
232         lm_vbox.pack_start (lm_table, false, false);
233
234         midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
235
236         /* pack it all up */
237
238         notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
239         notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
240         notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
241         notebook.set_border_width (12);
242
243         notebook.set_show_tabs (false);
244         notebook.show_all ();
245
246         notebook.set_name ("SettingsNotebook");
247
248         /* packup the notebook */
249
250         get_vbox()->set_border_width (12);
251         get_vbox()->pack_start (notebook);
252
253         get_action_area()->pack_start (engine_status);
254         engine_status.show();
255
256         /* need a special function to print "all available channels" when the
257          * channel counts hit zero.
258          */
259
260         input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
261         output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
262
263         midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
264         midi_devices_button.set_sensitive (false);
265         midi_devices_button.set_name ("generic button");
266         midi_devices_button.set_can_focus(true);
267
268         control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
269         manage_control_app_sensitivity ();
270
271         cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
272         apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
273         ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
274
275         /* Pick up any existing audio setup configuration, if appropriate */
276
277         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
278
279         ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
280         ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
281         ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
282         ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
283
284         if (audio_setup) {
285                 set_state (*audio_setup);
286         }
287
288         if (backend_combo.get_active_text().empty()) {
289                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
290                 backend_combo.set_active_text (backend_names.front());
291         }
292
293         backend_changed ();
294
295         /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
296         if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
297                 backend_combo.set_active_text (backend_names.back());
298                 /* ignore: don't save state */
299                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
300                 backend_changed ();
301         }
302
303
304         /* Connect to signals */
305
306         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
307         sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
308         buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
309         device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
310         midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
311
312         input_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::input_device_changed));
313         output_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::output_device_changed));
314
315         input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316         output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
317         input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
318         output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
319
320         notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
321
322         connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
323         connect_disconnect_button.set_no_show_all();
324
325 }
326
327 void
328 EngineControl::on_show ()
329 {
330         ArdourDialog::on_show ();
331         if (!ARDOUR::AudioEngine::instance()->current_backend() || !ARDOUR::AudioEngine::instance()->running()) {
332                 // re-check _have_control (jackd running) see #6041
333                 backend_changed ();
334         }
335         device_changed ();
336         input_device_changed ();
337         output_device_changed ();
338         ok_button->grab_focus();
339 }
340
341 void
342 EngineControl::on_response (int response_id)
343 {
344         ArdourDialog::on_response (response_id);
345
346         switch (response_id) {
347                 case RESPONSE_APPLY:
348                         push_state_to_backend (true);
349                         break;
350                 case RESPONSE_OK:
351 #ifdef PLATFORM_WINDOWS
352                         // For some reason we don't understand, 'hide()'
353                         // needs to get called first in Windows
354                         hide ();
355
356                         // But if there's no session open, this can produce
357                         // a long gap when nothing appears to be happening.
358                         // Let's show the splash image while we're waiting.
359                         if ( !ARDOUR_COMMAND_LINE::no_splash ) {
360                                 if ( ARDOUR_UI::instance() ) {
361                                         if ( !ARDOUR_UI::instance()->session_loaded ) {
362                                                 ARDOUR_UI::instance()->show_splash();
363                                         }
364                                 }
365                         }
366                         push_state_to_backend (true);
367                         break;
368 #else
369                         push_state_to_backend (true);
370                         hide ();
371                         break;
372 #endif
373                 case RESPONSE_DELETE_EVENT:
374                         {
375                                 GdkEventButton ev;
376                                 ev.type = GDK_BUTTON_PRESS;
377                                 ev.button = 1;
378                                 on_delete_event ((GdkEventAny*) &ev);
379                                 break;
380                         }
381                 default:
382                         hide ();
383         }
384 }
385
386 void
387 EngineControl::build_notebook ()
388 {
389         Label* label;
390         AttachOptions xopt = AttachOptions (FILL|EXPAND);
391
392         /* clear the table */
393
394         Gtkmm2ext::container_clear (basic_vbox);
395         Gtkmm2ext::container_clear (basic_packer);
396
397         if (control_app_button.get_parent()) {
398                 control_app_button.get_parent()->remove (control_app_button);
399         }
400
401         label = manage (left_aligned_label (_("Audio System:")));
402         basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
403         basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
404
405         lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
406         lm_button_audio.set_name ("generic button");
407         lm_button_audio.set_can_focus(true);
408
409         if (_have_control) {
410                 build_full_control_notebook ();
411         } else {
412                 build_no_control_notebook ();
413         }
414
415         basic_vbox.pack_start (basic_hbox, false, false);
416
417         {
418                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
419                 basic_vbox.show_all ();
420         }
421 }
422
423 void
424 EngineControl::build_full_control_notebook ()
425 {
426         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
427         assert (backend);
428
429         using namespace Notebook_Helpers;
430         Label* label;
431         vector<string> strings;
432         AttachOptions xopt = AttachOptions (FILL|EXPAND);
433         int row = 1; // row zero == backend combo
434
435         /* start packing it up */
436
437         if (backend->requires_driver_selection()) {
438                 label = manage (left_aligned_label (_("Driver:")));
439                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
440                 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
441                 row++;
442         }
443
444         if (backend->use_separate_input_and_output_devices()) {
445                 label = manage (left_aligned_label (_("Input Device:")));
446                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
447                 basic_packer.attach (input_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
448                 row++;
449                 label = manage (left_aligned_label (_("Output Device:")));
450                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
451                 basic_packer.attach (output_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
452                 row++;
453                 // reset so it isn't used in state comparisons
454                 device_combo.set_active_text ("");
455         } else {
456                 label = manage (left_aligned_label (_("Device:")));
457                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
458                 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
459                 row++;
460                 // reset these so they don't get used in state comparisons
461                 input_device_combo.set_active_text ("");
462                 output_device_combo.set_active_text ("");
463         }
464
465         label = manage (left_aligned_label (_("Sample rate:")));
466         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
467         basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
468         row++;
469
470
471         label = manage (left_aligned_label (_("Buffer size:")));
472         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
473         basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
474         buffer_size_duration_label.set_alignment (0.0); /* left-align */
475         basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
476
477         /* button spans 2 rows */
478
479         basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
480         row++;
481
482         input_channels.set_name ("InputChannels");
483         input_channels.set_flags (Gtk::CAN_FOCUS);
484         input_channels.set_digits (0);
485         input_channels.set_wrap (false);
486         output_channels.set_editable (true);
487
488         if (!ARDOUR::Profile->get_mixbus()) {
489                 label = manage (left_aligned_label (_("Input Channels:")));
490                 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
491                 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
492                 ++row;
493         }
494         
495         output_channels.set_name ("OutputChannels");
496         output_channels.set_flags (Gtk::CAN_FOCUS);
497         output_channels.set_digits (0);
498         output_channels.set_wrap (false);
499         output_channels.set_editable (true);
500
501         if (!ARDOUR::Profile->get_mixbus()) {
502                 label = manage (left_aligned_label (_("Output Channels:")));
503                 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
504                 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
505                 ++row;
506         }
507         
508         input_latency.set_name ("InputLatency");
509         input_latency.set_flags (Gtk::CAN_FOCUS);
510         input_latency.set_digits (0);
511         input_latency.set_wrap (false);
512         input_latency.set_editable (true);
513
514         label = manage (left_aligned_label (_("Hardware input latency:")));
515         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
516         basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
517         label = manage (left_aligned_label (_("samples")));
518         basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
519         ++row;
520
521         output_latency.set_name ("OutputLatency");
522         output_latency.set_flags (Gtk::CAN_FOCUS);
523         output_latency.set_digits (0);
524         output_latency.set_wrap (false);
525         output_latency.set_editable (true);
526
527         label = manage (left_aligned_label (_("Hardware output latency:")));
528         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
529         basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
530         label = manage (left_aligned_label (_("samples")));
531         basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
532
533         /* button spans 2 rows */
534
535         basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
536         ++row;
537
538         label = manage (left_aligned_label (_("MIDI System:")));
539         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
540         basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
541         basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
542         row++;
543 }
544
545 void
546 EngineControl::build_no_control_notebook ()
547 {
548         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
549         assert (backend);
550
551         using namespace Notebook_Helpers;
552         Label* label;
553         vector<string> strings;
554         AttachOptions xopt = AttachOptions (FILL|EXPAND);
555         int row = 1; // row zero == backend combo
556         const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
557
558         label = manage (new Label);
559         label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
560         basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
561         row++;
562
563         if (backend->can_change_sample_rate_when_running()) {
564                 label = manage (left_aligned_label (_("Sample rate:")));
565                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
566                 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
567                 row++;
568         }
569
570         if (backend->can_change_buffer_size_when_running()) {
571                 label = manage (left_aligned_label (_("Buffer size:")));
572                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
573                 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
574                 buffer_size_duration_label.set_alignment (0.0); /* left-align */
575                 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
576                 row++;
577         }
578
579         basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
580         row++;
581 }
582
583 EngineControl::~EngineControl ()
584 {
585         ignore_changes = true;
586 }
587
588 void
589 EngineControl::disable_latency_tab ()
590 {
591         vector<string> empty;
592         set_popdown_strings (lm_output_channel_combo, empty);
593         set_popdown_strings (lm_input_channel_combo, empty);
594         lm_measure_button.set_sensitive (false);
595         lm_use_button.set_sensitive (false);
596 }
597
598 void
599 EngineControl::enable_latency_tab ()
600 {
601         vector<string> outputs;
602         vector<string> inputs;
603
604         ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
605         ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
606         ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
607
608         if (!ARDOUR::AudioEngine::instance()->running()) {
609                 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
610                 notebook.set_current_page (0);
611                 msg.run ();
612                 return;
613         }
614         else if (inputs.empty() || outputs.empty()) {
615                 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
616                 notebook.set_current_page (0);
617                 msg.run ();
618                 return;
619         }
620
621         lm_back_button_signal.disconnect();
622         if (_measure_midi) {
623                 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
624                 lm_preamble.hide ();
625         } else {
626                 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
627                 lm_preamble.show ();
628         }
629
630         set_popdown_strings (lm_output_channel_combo, outputs);
631         lm_output_channel_combo.set_active_text (outputs.front());
632         lm_output_channel_combo.set_sensitive (true);
633
634         set_popdown_strings (lm_input_channel_combo, inputs);
635         lm_input_channel_combo.set_active_text (inputs.front());
636         lm_input_channel_combo.set_sensitive (true);
637
638         lm_measure_button.set_sensitive (true);
639 }
640
641 void
642 EngineControl::setup_midi_tab_for_backend ()
643 {
644         string backend = backend_combo.get_active_text ();
645
646         Gtkmm2ext::container_clear (midi_vbox);
647
648         midi_vbox.set_border_width (12);
649         midi_device_table.set_border_width (12);
650
651         if (backend == "JACK") {
652                 setup_midi_tab_for_jack ();
653         }
654
655         midi_vbox.pack_start (midi_device_table, true, true);
656         midi_vbox.pack_start (midi_back_button, false, false);
657         midi_vbox.show_all ();
658 }
659
660 void
661 EngineControl::setup_midi_tab_for_jack ()
662 {
663 }
664
665 void
666 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
667         if (for_input) {
668                 device->input_latency = a->get_value();
669         } else {
670                 device->output_latency = a->get_value();
671         }
672 }
673
674 void
675 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
676         b->set_active (!b->get_active());
677         device->enabled = b->get_active();
678         refresh_midi_display(device->name);
679 }
680
681 void
682 EngineControl::refresh_midi_display (std::string focus)
683 {
684         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
685         assert (backend);
686
687         int row  = 0;
688         AttachOptions xopt = AttachOptions (FILL|EXPAND);
689         Gtk::Label* l;
690
691         Gtkmm2ext::container_clear (midi_device_table);
692
693         midi_device_table.set_spacings (6);
694
695         l = manage (new Label);
696         l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
697         midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
698         l->set_alignment (0.5, 0.5);
699         row++;
700         l->show ();
701
702         l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
703         midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
704         l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
705         midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
706         row++;
707         l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
708         midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
709         l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
710         midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
711         row++;
712
713         for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
714                 ArdourButton *m;
715                 Gtk::Button* b;
716                 Gtk::Adjustment *a;
717                 Gtk::SpinButton *s;
718                 bool enabled = (*p)->enabled;
719
720                 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
721                 m->set_name ("midi device");
722                 m->set_can_focus (Gtk::CAN_FOCUS);
723                 m->add_events (Gdk::BUTTON_RELEASE_MASK);
724                 m->set_active (enabled);
725                 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
726                 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
727                 if ((*p)->name == focus) {
728                         m->grab_focus();
729                 }
730
731                 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
732                 s = manage (new Gtk::SpinButton (*a));
733                 a->set_value ((*p)->input_latency);
734                 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
735                 s->set_sensitive (_can_set_midi_latencies && enabled);
736                 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
737
738                 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
739                 s = manage (new Gtk::SpinButton (*a));
740                 a->set_value ((*p)->output_latency);
741                 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
742                 s->set_sensitive (_can_set_midi_latencies && enabled);
743                 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
744
745                 b = manage (new Button (_("Calibrate")));
746                 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
747                 b->set_sensitive (_can_set_midi_latencies && enabled);
748                 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
749
750                 row++;
751         }
752 }
753
754 void
755 EngineControl::update_sensitivity ()
756 {
757 }
758
759 void
760 EngineControl::backend_changed ()
761 {
762         string backend_name = backend_combo.get_active_text();
763         boost::shared_ptr<ARDOUR::AudioBackend> backend;
764
765         if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
766                 /* eh? setting the backend failed... how ? */
767                 /* A: stale config contains a backend that does not exist in current build */
768                 return;
769         }
770
771         _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
772
773         build_notebook ();
774         setup_midi_tab_for_backend ();
775         _midi_devices.clear();
776
777         if (backend->requires_driver_selection()) {
778                 vector<string> drivers = backend->enumerate_drivers();
779                 driver_combo.set_sensitive (true);
780
781                 if (!drivers.empty()) {
782                         {
783                                 string current_driver;
784                                 current_driver = backend->driver_name ();
785
786                                 // driver might not have been set yet
787                                 if (current_driver == "") {
788                                         current_driver = driver_combo.get_active_text ();
789                                         if (current_driver == "")
790                                                 // driver has never been set, make sure it's not blank
791                                                 current_driver = drivers.front ();
792                                 }
793
794                                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
795                                 set_popdown_strings (driver_combo, drivers);
796                                 driver_combo.set_active_text (current_driver);
797                         }
798
799                         driver_changed ();
800                 }
801
802         } else {
803                 driver_combo.set_sensitive (false);
804                 /* this will change the device text which will cause a call to
805                  * device changed which will set up parameters
806                  */
807                 list_devices ();
808         }
809
810         vector<string> midi_options = backend->enumerate_midi_options();
811
812         if (midi_options.size() == 1) {
813                 /* only contains the "none" option */
814                 midi_option_combo.set_sensitive (false);
815         } else {
816                 if (_have_control) {
817                         set_popdown_strings (midi_option_combo, midi_options);
818                         midi_option_combo.set_active_text (midi_options.front());
819                         midi_option_combo.set_sensitive (true);
820                 } else {
821                         midi_option_combo.set_sensitive (false);
822                 }
823         }
824
825         connect_disconnect_button.hide();
826
827         midi_option_changed();
828
829         started_at_least_once = false;
830
831         if (!ignore_changes) {
832                 maybe_display_saved_state ();
833         }
834 }
835
836 bool
837 EngineControl::print_channel_count (Gtk::SpinButton* sb)
838 {
839         if (ARDOUR::Profile->get_mixbus()) {
840                 return true;
841         }
842         
843         uint32_t cnt = (uint32_t) sb->get_value();
844         if (cnt == 0) {
845                 sb->set_text (_("all available channels"));
846         } else {
847                 char buf[32];
848                 snprintf (buf, sizeof (buf), "%d", cnt);
849                 sb->set_text (buf);
850         }
851         return true;
852 }
853
854 // @return true if there are devices available
855 bool
856 EngineControl::set_device_popdown_strings ()
857 {
858         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
859         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
860
861         /* NOTE: Ardour currently does not display the "available" field of the
862          * returned devices.
863          *
864          * Doing so would require a different GUI widget than the combo
865          * box/popdown that we currently use, since it has no way to list
866          * items that are not selectable. Something more like a popup menu,
867          * which could have unselectable items, would be appropriate.
868          */
869
870         vector<string> available_devices;
871
872         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
873                 available_devices.push_back (i->name);
874         }
875
876
877         if (!available_devices.empty()) {
878                 update_sensitivity ();
879
880                 {
881                         string current_device, found_device;
882                         current_device = device_combo.get_active_text ();
883                         if (current_device == "") {
884                                 current_device = backend->device_name ();
885                         }
886
887                         // Make sure that the active text is still relevant for this
888                         // device (it might only be relevant to the previous device!!)
889                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
890                                 if (*i == current_device)
891                                         found_device = current_device;
892                         }
893                         if (found_device == "")
894                                 // device has never been set (or was not relevant
895                                 // for this backend) Let's make sure it's not blank
896                                 current_device = available_devices.front ();
897
898                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
899                         set_popdown_strings (device_combo, available_devices);
900
901                         device_combo.set_active_text (current_device);
902                 }
903
904                 device_changed ();
905                 return true;
906         }
907         return false;
908 }
909
910 // @return true if there are input devices available
911 bool
912 EngineControl::set_input_device_popdown_strings ()
913 {
914         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
915         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_input_devices ();
916
917         vector<string> available_devices;
918
919         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
920                 available_devices.push_back (i->name);
921         }
922
923         if (!available_devices.empty()) {
924                 update_sensitivity ();
925
926                 {
927                         string current_device, found_device;
928                         current_device = input_device_combo.get_active_text ();
929                         if (current_device == "") {
930                                 current_device = backend->input_device_name ();
931                         }
932
933                         // Make sure that the active text is still relevant for this
934                         // device (it might only be relevant to the previous device!!)
935                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
936                                 if (*i == current_device)
937                                         found_device = current_device;
938                         }
939                         if (found_device == "")
940                                 // device has never been set (or was not relevant
941                                 // for this backend) Let's make sure it's not blank
942                                 current_device = available_devices.front ();
943
944                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
945                         set_popdown_strings (input_device_combo, available_devices);
946
947                         input_device_combo.set_active_text (current_device);
948                 }
949
950                 input_device_changed ();
951                 return true;
952         }
953
954         return false;
955 }
956
957 // @return true if there are output devices available
958 bool
959 EngineControl::set_output_device_popdown_strings ()
960 {
961         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
962         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_output_devices ();
963
964         vector<string> available_devices;
965
966         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
967                 available_devices.push_back (i->name);
968         }
969
970         if (!available_devices.empty()) {
971                 update_sensitivity ();
972
973                 {
974                         string current_device, found_device;
975                         current_device = output_device_combo.get_active_text ();
976                         if (current_device == "") {
977                                 current_device = backend->output_device_name ();
978                         }
979
980                         // Make sure that the active text is still relevant for this
981                         // device (it might only be relevant to the previous device!!)
982                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
983                                 if (*i == current_device)
984                                         found_device = current_device;
985                         }
986                         if (found_device == "")
987                                 // device has never been set (or was not relevant
988                                 // for this backend) Let's make sure it's not blank
989                                 current_device = available_devices.front ();
990
991                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
992                         set_popdown_strings (output_device_combo, available_devices);
993
994                         output_device_combo.set_active_text (current_device);
995                 }
996
997                 output_device_changed ();
998                 return true;
999         }
1000
1001         return false;
1002 }
1003
1004 void
1005 EngineControl::list_devices ()
1006 {
1007         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1008         assert (backend);
1009
1010         /* now fill out devices, mark sample rates, buffer sizes insensitive */
1011
1012         bool devices_available = false;
1013
1014         if (backend->use_separate_input_and_output_devices ()) {
1015                 bool input_devices_available = set_input_device_popdown_strings ();
1016                 bool output_devices_available = set_output_device_popdown_strings ();
1017                 devices_available = input_devices_available || output_devices_available;
1018         } else {
1019                 devices_available = set_device_popdown_strings ();
1020         }
1021
1022         if (devices_available) {
1023                 input_latency.set_sensitive (true);
1024                 output_latency.set_sensitive (true);
1025                 input_channels.set_sensitive (true);
1026                 output_channels.set_sensitive (true);
1027
1028                 ok_button->set_sensitive (true);
1029                 apply_button->set_sensitive (true);
1030
1031         } else {
1032                 device_combo.clear();
1033                 input_device_combo.clear();
1034                 output_device_combo.clear();
1035                 sample_rate_combo.set_sensitive (false);
1036                 buffer_size_combo.set_sensitive (false);
1037                 input_latency.set_sensitive (false);
1038                 output_latency.set_sensitive (false);
1039                 input_channels.set_sensitive (false);
1040                 output_channels.set_sensitive (false);
1041                 if (_have_control) {
1042                         ok_button->set_sensitive (false);
1043                         apply_button->set_sensitive (false);
1044                 } else {
1045                         ok_button->set_sensitive (true);
1046                         apply_button->set_sensitive (true);
1047                         if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
1048                                 sample_rate_combo.set_sensitive (true);
1049                         }
1050                         if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
1051                                 buffer_size_combo.set_sensitive (true);
1052                         }
1053
1054                 }
1055         }
1056 }
1057
1058 void
1059 EngineControl::driver_changed ()
1060 {
1061         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1062         assert (backend);
1063
1064         backend->set_driver (driver_combo.get_active_text());
1065         list_devices ();
1066
1067         if (!ignore_changes) {
1068                 maybe_display_saved_state ();
1069         }
1070 }
1071
1072 void
1073 EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
1074 {
1075         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1076         string desired;
1077         vector<float> sr;
1078         vector<string> s;
1079
1080         if (_have_control) {
1081                 sr = backend->available_sample_rates (device_name);
1082         } else {
1083
1084                 sr.push_back (8000.0f);
1085                 sr.push_back (16000.0f);
1086                 sr.push_back (32000.0f);
1087                 sr.push_back (44100.0f);
1088                 sr.push_back (48000.0f);
1089                 sr.push_back (88200.0f);
1090                 sr.push_back (96000.0f);
1091                 sr.push_back (192000.0f);
1092                 sr.push_back (384000.0f);
1093         }
1094
1095         for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
1096                 s.push_back (rate_as_string (*x));
1097                 if (*x == _desired_sample_rate) {
1098                         desired = s.back();
1099                 }
1100         }
1101
1102         if (!s.empty()) {
1103                 sample_rate_combo.set_sensitive (true);
1104                 set_popdown_strings (sample_rate_combo, s);
1105
1106                 if (desired.empty()) {
1107                         sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
1108                 } else {
1109                         sample_rate_combo.set_active_text (desired);
1110                 }
1111
1112         } else {
1113                 sample_rate_combo.set_sensitive (false);
1114         }
1115 }
1116
1117 void
1118 EngineControl::set_buffersize_popdown_strings (const std::string& device_name)
1119 {
1120         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1121         vector<uint32_t> bs;
1122         vector<string> s;
1123
1124         if (_have_control) {
1125                 bs = backend->available_buffer_sizes (device_name);
1126         } else if (backend->can_change_buffer_size_when_running()) {
1127                 bs.push_back (8);
1128                 bs.push_back (16);
1129                 bs.push_back (32);
1130                 bs.push_back (64);
1131                 bs.push_back (128);
1132                 bs.push_back (256);
1133                 bs.push_back (512);
1134                 bs.push_back (1024);
1135                 bs.push_back (2048);
1136                 bs.push_back (4096);
1137                 bs.push_back (8192);
1138         }
1139         s.clear ();
1140         for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1141                 s.push_back (bufsize_as_string (*x));
1142         }
1143
1144         if (!s.empty()) {
1145                 buffer_size_combo.set_sensitive (true);
1146                 set_popdown_strings (buffer_size_combo, s);
1147
1148                 uint32_t period = backend->buffer_size();
1149                 if (0 == period) {
1150                         period = backend->default_buffer_size(device_name);
1151                 }
1152                 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1153                 show_buffer_duration ();
1154         } else {
1155                 buffer_size_combo.set_sensitive (false);
1156         }
1157 }
1158
1159 void
1160 EngineControl::device_changed ()
1161 {
1162         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1163         assert (backend);
1164
1165         string device_name_in;
1166         string device_name_out; // only used if backend support separate I/O devices
1167
1168         if (backend->use_separate_input_and_output_devices()) {
1169                 device_name_in  = get_input_device_name ();
1170                 device_name_out = get_output_device_name ();
1171         } else {
1172                 device_name_in = get_device_name ();
1173         }
1174
1175         /* we set the backend-device to query various device related intormation.
1176          * This has the side effect that backend->device_name() will match
1177          * the device_name and  'change_device' will never be true.
1178          * so work around this by setting...
1179          */
1180         if (backend->use_separate_input_and_output_devices()) {
1181                 if (device_name_in != backend->input_device_name() || device_name_out != backend->output_device_name ()) {
1182                         queue_device_changed = true;
1183                 }
1184         } else {
1185                 if (device_name_in != backend->device_name()) {
1186                         queue_device_changed = true;
1187                 }
1188         }
1189         
1190         //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
1191         if (backend->use_separate_input_and_output_devices()) {
1192                 backend->set_input_device_name (device_name_in);
1193                 backend->set_output_device_name (device_name_out);
1194         } else {
1195                 backend->set_device_name(device_name_in);
1196         }
1197
1198         {
1199                 /* don't allow programmatic change to combos to cause a
1200                    recursive call to this method.
1201                  */
1202                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1203
1204                 /* backends that support separate devices, need to ignore
1205                  * the device-name - and use the devies set above
1206                  */
1207                 set_samplerate_popdown_strings (device_name_in);
1208                 set_buffersize_popdown_strings (device_name_in);
1209                 /* XXX theoretically need to set min + max channel counts here
1210                 */
1211
1212                 manage_control_app_sensitivity ();
1213         }
1214
1215         /* pick up any saved state for this device */
1216
1217         if (!ignore_changes) {
1218                 maybe_display_saved_state ();
1219         }
1220 }
1221
1222 void
1223 EngineControl::input_device_changed ()
1224 {
1225         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1226         assert (backend);
1227         string input_device_name = input_device_combo.get_active_text ();
1228
1229         if (!ignore_changes && input_device_name != backend->input_device_name()) {
1230                 queue_device_changed = true;
1231         }
1232
1233         backend->set_input_device_name(input_device_name);
1234
1235         {
1236                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1237
1238                 set_samplerate_popdown_strings (input_device_name);
1239                 set_buffersize_popdown_strings (input_device_name);
1240                 /* XXX theoretically need to set min + max channel counts here
1241                 */
1242
1243                 manage_control_app_sensitivity ();
1244         }
1245
1246         /* pick up any saved state for this device */
1247
1248         if (!ignore_changes) {
1249                 maybe_display_saved_state ();
1250         }
1251 }
1252
1253 void
1254 EngineControl::output_device_changed ()
1255 {
1256         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1257         assert (backend);
1258         string output_device_name = output_device_combo.get_active_text ();
1259
1260         if (!ignore_changes && output_device_name != backend->output_device_name()) {
1261                 queue_device_changed = true;
1262         }
1263
1264         backend->set_output_device_name(output_device_name);
1265
1266         {
1267                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1268
1269                 set_samplerate_popdown_strings (output_device_name);
1270                 set_buffersize_popdown_strings (output_device_name);
1271                 /* XXX theoretically need to set min + max channel counts here
1272                 */
1273
1274                 manage_control_app_sensitivity ();
1275         }
1276
1277         /* pick up any saved state for this device */
1278
1279         if (!ignore_changes) {
1280                 maybe_display_saved_state ();
1281         }
1282 }
1283
1284 string
1285 EngineControl::bufsize_as_string (uint32_t sz)
1286 {
1287         /* Translators: "samples" is always plural here, so no
1288            need for plural+singular forms.
1289          */
1290         char buf[64];
1291         snprintf (buf, sizeof (buf), "%u %s", sz, P_("sample", "samples", sz));
1292         return buf;
1293 }
1294
1295 void
1296 EngineControl::sample_rate_changed ()
1297 {
1298         /* reset the strings for buffer size to show the correct msec value
1299            (reflecting the new sample rate).
1300          */
1301
1302         show_buffer_duration ();
1303
1304 }
1305
1306 void
1307 EngineControl::buffer_size_changed ()
1308 {
1309         show_buffer_duration ();
1310 }
1311
1312 void
1313 EngineControl::show_buffer_duration ()
1314 {
1315
1316         /* buffer sizes  - convert from just samples to samples + msecs for
1317          * the displayed string
1318          */
1319
1320         string bs_text = buffer_size_combo.get_active_text ();
1321         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1322         uint32_t rate = get_rate();
1323
1324         /* Developers: note the hard-coding of a double buffered model
1325            in the (2 * samples) computation of latency. we always start
1326            the audiobackend in this configuration.
1327          */
1328         /* note to jack1 developers: ardour also always starts the engine
1329          * in async mode (no jack2 --sync option) which adds an extra cycle
1330          * of latency with jack2 (and *3 would be correct)
1331          * The value can also be wrong if jackd is started externally..
1332          *
1333          * At the time of writing the ALSA backend always uses double-buffering *2,
1334          * The Dummy backend *1, and who knows what ASIO really does :)
1335          *
1336          * So just display the period size, that's also what
1337          * ARDOUR_UI::update_sample_rate() does for the status bar.
1338          * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1339          * but still, that's the buffer period, not [round-trip] latency)
1340          */
1341         char buf[32];
1342         snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1343         buffer_size_duration_label.set_text (buf);
1344 }
1345
1346 void
1347 EngineControl::midi_option_changed ()
1348 {
1349         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1350         assert (backend);
1351
1352         backend->set_midi_option (get_midi_option());
1353
1354         vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1355
1356         //_midi_devices.clear(); // TODO merge with state-saved settings..
1357         _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1358         std::vector<MidiDeviceSettings> new_devices;
1359
1360         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1361                 MidiDeviceSettings mds = find_midi_device (i->name);
1362                 if (i->available && !mds) {
1363                         uint32_t input_latency = 0;
1364                         uint32_t output_latency = 0;
1365                         if (_can_set_midi_latencies) {
1366                                 input_latency = backend->systemic_midi_input_latency (i->name);
1367                                 output_latency = backend->systemic_midi_output_latency (i->name);
1368                         }
1369                         bool enabled = backend->midi_device_enabled (i->name);
1370                         MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1371                         new_devices.push_back (ptr);
1372                 } else if (i->available) {
1373                         new_devices.push_back (mds);
1374                 }
1375         }
1376         _midi_devices = new_devices;
1377
1378         if (_midi_devices.empty()) {
1379                 midi_devices_button.set_sensitive (false);
1380         } else {
1381                 midi_devices_button.set_sensitive (true);
1382         }
1383 }
1384
1385 void
1386 EngineControl::parameter_changed ()
1387 {
1388 }
1389
1390 EngineControl::State
1391 EngineControl::get_matching_state (
1392                 const string& backend,
1393                 const string& driver,
1394                 const string& device)
1395 {
1396         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1397                 if ((*i)->backend == backend &&
1398                                 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1399                 {
1400                         return (*i);
1401                 }
1402         }
1403         return State();
1404 }
1405
1406 EngineControl::State
1407 EngineControl::get_matching_state (
1408                 const string& backend,
1409                 const string& driver,
1410                 const string& input_device,
1411                 const string& output_device)
1412 {
1413         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1414                 if ((*i)->backend == backend &&
1415                                 (!_have_control || ((*i)->driver == driver && ((*i)->input_device == input_device) && (*i)->output_device == output_device)))
1416                 {
1417                         return (*i);
1418                 }
1419         }
1420         return State();
1421 }
1422
1423 EngineControl::State
1424 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1425 {
1426         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1427
1428         if (backend) {
1429                 if (backend->use_separate_input_and_output_devices ()) {
1430                         return get_matching_state (backend_combo.get_active_text(),
1431                                         (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1432                                         input_device_combo.get_active_text(),
1433                                         output_device_combo.get_active_text());
1434                 } else {
1435                         return get_matching_state (backend_combo.get_active_text(),
1436                                         (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1437                                         device_combo.get_active_text());
1438                 }
1439         }
1440
1441         return get_matching_state (backend_combo.get_active_text(),
1442                         string(),
1443                         device_combo.get_active_text());
1444 }
1445
1446 bool EngineControl::equivalent_states (const EngineControl::State& state1,
1447                                        const EngineControl::State& state2)
1448 {
1449         if (state1->backend == state2->backend &&
1450                         state1->driver == state2->driver &&
1451                         state1->device == state2->device &&
1452                         state1->input_device == state2->input_device &&
1453                         state1->output_device == state2->output_device) {
1454                 return true;
1455         }
1456         return false;
1457 }
1458
1459 EngineControl::State
1460 EngineControl::save_state ()
1461 {
1462         State state;
1463
1464         if (!_have_control) {
1465                 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1466                 if (state) {
1467                         return state;
1468                 }
1469                 state.reset(new StateStruct);
1470                 state->backend = get_backend ();
1471         } else {
1472                 state.reset(new StateStruct);
1473                 store_state (state);
1474         }
1475
1476         for (StateList::iterator i = states.begin(); i != states.end();) {
1477                 if (equivalent_states (*i, state)) {
1478                         i =  states.erase(i);
1479                 } else {
1480                         ++i;
1481                 }
1482         }
1483
1484         states.push_back (state);
1485
1486         return state;
1487 }
1488
1489 void
1490 EngineControl::store_state (State state)
1491 {
1492         state->backend = get_backend ();
1493         state->driver = get_driver ();
1494         state->device = get_device_name ();
1495         state->input_device = get_input_device_name ();
1496         state->output_device = get_output_device_name ();
1497         state->sample_rate = get_rate ();
1498         state->buffer_size = get_buffer_size ();
1499         state->input_latency = get_input_latency ();
1500         state->output_latency = get_output_latency ();
1501         state->input_channels = get_input_channels ();
1502         state->output_channels = get_output_channels ();
1503         state->midi_option = get_midi_option ();
1504         state->midi_devices = _midi_devices;
1505 }
1506
1507 void
1508 EngineControl::maybe_display_saved_state ()
1509 {
1510         if (!_have_control) {
1511                 return;
1512         }
1513
1514         State state = get_saved_state_for_currently_displayed_backend_and_device ();
1515
1516         if (state) {
1517                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1518
1519                 if (!_desired_sample_rate) {
1520                         sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1521                 }
1522                 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1523                 /* call this explicitly because we're ignoring changes to
1524                    the controls at this point.
1525                  */
1526                 show_buffer_duration ();
1527                 input_latency.set_value (state->input_latency);
1528                 output_latency.set_value (state->output_latency);
1529
1530                 if (!state->midi_option.empty()) {
1531                         midi_option_combo.set_active_text (state->midi_option);
1532                         _midi_devices = state->midi_devices;
1533                 }
1534         }
1535 }
1536
1537 XMLNode&
1538 EngineControl::get_state ()
1539 {
1540         XMLNode* root = new XMLNode ("AudioMIDISetup");
1541         std::string path;
1542
1543         if (!states.empty()) {
1544                 XMLNode* state_nodes = new XMLNode ("EngineStates");
1545
1546                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1547
1548                         XMLNode* node = new XMLNode ("State");
1549
1550                         node->add_property ("backend", (*i)->backend);
1551                         node->add_property ("driver", (*i)->driver);
1552                         node->add_property ("device", (*i)->device);
1553                         node->add_property ("input-device", (*i)->input_device);
1554                         node->add_property ("output-device", (*i)->output_device);
1555                         node->add_property ("sample-rate", (*i)->sample_rate);
1556                         node->add_property ("buffer-size", (*i)->buffer_size);
1557                         node->add_property ("input-latency", (*i)->input_latency);
1558                         node->add_property ("output-latency", (*i)->output_latency);
1559                         node->add_property ("input-channels", (*i)->input_channels);
1560                         node->add_property ("output-channels", (*i)->output_channels);
1561                         node->add_property ("active", (*i)->active ? "yes" : "no");
1562                         node->add_property ("midi-option", (*i)->midi_option);
1563
1564                         XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1565                         for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1566                                 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1567                                 midi_device_stuff->add_property (X_("name"), (*p)->name);
1568                                 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1569                                 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1570                                 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1571                                 midi_devices->add_child_nocopy (*midi_device_stuff);
1572                         }
1573                         node->add_child_nocopy (*midi_devices);
1574
1575                         state_nodes->add_child_nocopy (*node);
1576                 }
1577
1578                 root->add_child_nocopy (*state_nodes);
1579         }
1580
1581         return *root;
1582 }
1583
1584 void
1585 EngineControl::set_state (const XMLNode& root)
1586 {
1587         XMLNodeList          clist, cclist;
1588         XMLNodeConstIterator citer, cciter;
1589         XMLNode* child;
1590         XMLNode* grandchild;
1591         XMLProperty* prop = NULL;
1592
1593         fprintf (stderr, "EngineControl::set_state\n");
1594
1595         if (root.name() != "AudioMIDISetup") {
1596                 return;
1597         }
1598
1599         clist = root.children();
1600
1601         states.clear ();
1602
1603         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1604
1605                 child = *citer;
1606
1607                 if (child->name() != "EngineStates") {
1608                         continue;
1609                 }
1610
1611                 cclist = child->children();
1612
1613                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1614                         State state (new StateStruct);
1615
1616                         grandchild = *cciter;
1617
1618                         if (grandchild->name() != "State") {
1619                                 continue;
1620                         }
1621
1622                         if ((prop = grandchild->property ("backend")) == 0) {
1623                                 continue;
1624                         }
1625                         state->backend = prop->value ();
1626
1627                         if ((prop = grandchild->property ("driver")) == 0) {
1628                                 continue;
1629                         }
1630                         state->driver = prop->value ();
1631
1632                         if ((prop = grandchild->property ("device")) == 0) {
1633                                 continue;
1634                         }
1635                         state->device = prop->value ();
1636
1637                         if ((prop = grandchild->property ("input-device")) == 0) {
1638                                 continue;
1639                         }
1640                         state->input_device = prop->value ();
1641
1642                         if ((prop = grandchild->property ("output-device")) == 0) {
1643                                 continue;
1644                         }
1645                         state->output_device = prop->value ();
1646
1647                         if ((prop = grandchild->property ("sample-rate")) == 0) {
1648                                 continue;
1649                         }
1650                         state->sample_rate = atof (prop->value ());
1651
1652                         if ((prop = grandchild->property ("buffer-size")) == 0) {
1653                                 continue;
1654                         }
1655                         state->buffer_size = atoi (prop->value ());
1656
1657                         if ((prop = grandchild->property ("input-latency")) == 0) {
1658                                 continue;
1659                         }
1660                         state->input_latency = atoi (prop->value ());
1661
1662                         if ((prop = grandchild->property ("output-latency")) == 0) {
1663                                 continue;
1664                         }
1665                         state->output_latency = atoi (prop->value ());
1666
1667                         if ((prop = grandchild->property ("input-channels")) == 0) {
1668                                 continue;
1669                         }
1670                         state->input_channels = atoi (prop->value ());
1671
1672                         if ((prop = grandchild->property ("output-channels")) == 0) {
1673                                 continue;
1674                         }
1675                         state->output_channels = atoi (prop->value ());
1676
1677                         if ((prop = grandchild->property ("active")) == 0) {
1678                                 continue;
1679                         }
1680                         state->active = string_is_affirmative (prop->value ());
1681
1682                         if ((prop = grandchild->property ("midi-option")) == 0) {
1683                                 continue;
1684                         }
1685                         state->midi_option = prop->value ();
1686
1687                         state->midi_devices.clear();
1688                         XMLNode* midinode;
1689                         if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1690                                 const XMLNodeList mnc = midinode->children();
1691                                 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1692                                         if ((*n)->property (X_("name")) == 0
1693                                                         || (*n)->property (X_("enabled")) == 0
1694                                                         || (*n)->property (X_("input-latency")) == 0
1695                                                         || (*n)->property (X_("output-latency")) == 0
1696                                                  ) {
1697                                                 continue;
1698                                         }
1699
1700                                         MidiDeviceSettings ptr (new MidiDeviceSetting(
1701                                                                 (*n)->property (X_("name"))->value (),
1702                                                                 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1703                                                                 atoi ((*n)->property (X_("input-latency"))->value ()),
1704                                                                 atoi ((*n)->property (X_("output-latency"))->value ())
1705                                                                 ));
1706                                         state->midi_devices.push_back (ptr);
1707                                 }
1708                         }
1709
1710 #if 1
1711                         /* remove accumulated duplicates (due to bug in ealier version)
1712                          * this can be removed again before release
1713                          */
1714                         for (StateList::iterator i = states.begin(); i != states.end();) {
1715                                 if ((*i)->backend == state->backend &&
1716                                                 (*i)->driver == state->driver &&
1717                                                 (*i)->device == state->device) {
1718                                         i =  states.erase(i);
1719                                 } else {
1720                                         ++i;
1721                                 }
1722                         }
1723 #endif
1724
1725                         states.push_back (state);
1726                 }
1727         }
1728
1729         /* now see if there was an active state and switch the setup to it */
1730
1731         // purge states of backend that are not available in this built
1732         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1733         vector<std::string> backend_names;
1734
1735         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1736                 backend_names.push_back((*i)->name);
1737         }
1738         for (StateList::iterator i = states.begin(); i != states.end();) {
1739                 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1740                         i = states.erase(i);
1741                 } else {
1742                         ++i;
1743                 }
1744         }
1745
1746         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1747
1748                 if ((*i)->active) {
1749                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1750                         backend_combo.set_active_text ((*i)->backend);
1751                         driver_combo.set_active_text ((*i)->driver);
1752                         device_combo.set_active_text ((*i)->device);
1753                         fprintf (stderr, "setting input device to: %s ", (*i)->input_device.c_str());
1754                         input_device_combo.set_active_text ((*i)->input_device);
1755                         fprintf (stderr, "setting output device to: %s ", (*i)->output_device.c_str());
1756                         output_device_combo.set_active_text ((*i)->output_device);
1757                         sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1758                         set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1759                         input_latency.set_value ((*i)->input_latency);
1760                         output_latency.set_value ((*i)->output_latency);
1761                         midi_option_combo.set_active_text ((*i)->midi_option);
1762                         break;
1763                 }
1764         }
1765 }
1766
1767 int
1768 EngineControl::push_state_to_backend (bool start)
1769 {
1770         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1771
1772         if (!backend) {
1773                 return 0;
1774         }
1775
1776         /* figure out what is going to change */
1777
1778         bool restart_required = false;
1779         bool was_running = ARDOUR::AudioEngine::instance()->running();
1780         bool change_driver = false;
1781         bool change_device = false;
1782         bool change_rate = false;
1783         bool change_bufsize = false;
1784         bool change_latency = false;
1785         bool change_channels = false;
1786         bool change_midi = false;
1787
1788         uint32_t ochan = get_output_channels ();
1789         uint32_t ichan = get_input_channels ();
1790
1791         if (_have_control) {
1792
1793                 if (started_at_least_once) {
1794
1795                         /* we can control the backend */
1796
1797                         if (backend->requires_driver_selection()) {
1798                                 if (get_driver() != backend->driver_name()) {
1799                                         change_driver = true;
1800                                 }
1801                         }
1802
1803                         if (backend->use_separate_input_and_output_devices()) {
1804                                 if (get_input_device_name() != backend->input_device_name()) {
1805                                         change_device = true;
1806                                 }
1807                                 if (get_output_device_name() != backend->output_device_name()) {
1808                                         change_device = true;
1809                                 }
1810                         } else {
1811                                 if (get_device_name() != backend->device_name()) {
1812                                         change_device = true;
1813                                 }
1814                         }
1815
1816                         if (queue_device_changed) {
1817                                 change_device = true;
1818                         }
1819
1820                         if (get_rate() != backend->sample_rate()) {
1821                                 change_rate = true;
1822                         }
1823
1824                         if (get_buffer_size() != backend->buffer_size()) {
1825                                 change_bufsize = true;
1826                         }
1827
1828                         if (get_midi_option() != backend->midi_option()) {
1829                                 change_midi = true;
1830                         }
1831
1832                         /* zero-requested channels means "all available" */
1833
1834                         if (ichan == 0) {
1835                                 ichan = backend->input_channels();
1836                         }
1837
1838                         if (ochan == 0) {
1839                                 ochan = backend->output_channels();
1840                         }
1841
1842                         if (ichan != backend->input_channels()) {
1843                                 change_channels = true;
1844                         }
1845
1846                         if (ochan != backend->output_channels()) {
1847                                 change_channels = true;
1848                         }
1849
1850                         if (get_input_latency() != backend->systemic_input_latency() ||
1851                                         get_output_latency() != backend->systemic_output_latency()) {
1852                                 change_latency = true;
1853                         }
1854                 } else {
1855                         /* backend never started, so we have to force a group
1856                            of settings.
1857                          */
1858                         change_device = true;
1859                         if (backend->requires_driver_selection()) {
1860                                 change_driver = true;
1861                         }
1862                         change_rate = true;
1863                         change_bufsize = true;
1864                         change_channels = true;
1865                         change_latency = true;
1866                         change_midi = true;
1867                 }
1868
1869         } else {
1870
1871                 /* we have no control over the backend, meaning that we can
1872                  * only possibly change sample rate and buffer size.
1873                  */
1874
1875
1876                 if (get_rate() != backend->sample_rate()) {
1877                         change_bufsize = true;
1878                 }
1879
1880                 if (get_buffer_size() != backend->buffer_size()) {
1881                         change_bufsize = true;
1882                 }
1883         }
1884
1885         queue_device_changed = false;
1886
1887         if (!_have_control) {
1888
1889                 /* We do not have control over the backend, so the best we can
1890                  * do is try to change the sample rate and/or bufsize and get
1891                  * out of here.
1892                  */
1893
1894                 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1895                         return 1;
1896                 }
1897
1898                 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1899                         return 1;
1900                 }
1901
1902                 if (change_rate) {
1903                         backend->set_sample_rate (get_rate());
1904                 }
1905
1906                 if (change_bufsize) {
1907                         backend->set_buffer_size (get_buffer_size());
1908                 }
1909
1910                 if (start) {
1911                         if (ARDOUR::AudioEngine::instance()->start ()) {
1912                                 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1913                                 return -1;
1914                         }
1915                 }
1916
1917                 post_push ();
1918
1919                 return 0;
1920         }
1921
1922         /* determine if we need to stop the backend before changing parameters */
1923
1924         if (change_driver || change_device || change_channels || change_latency ||
1925                         (change_rate && !backend->can_change_sample_rate_when_running()) ||
1926                         change_midi ||
1927                         (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1928                 restart_required = true;
1929         } else {
1930                 restart_required = false;
1931         }
1932
1933         if (was_running) {
1934
1935                 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1936                         /* no changes in any parameters that absolutely require a
1937                          * restart, so check those that might be changeable without a
1938                          * restart
1939                          */
1940
1941                         if (change_rate && !backend->can_change_sample_rate_when_running()) {
1942                                 /* can't do this while running ... */
1943                                 restart_required = true;
1944                         }
1945
1946                         if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1947                                 /* can't do this while running ... */
1948                                 restart_required = true;
1949                         }
1950                 }
1951         }
1952
1953         if (was_running) {
1954                 if (restart_required) {
1955                         if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1956                                 return -1;
1957                         }
1958                 }
1959         }
1960
1961
1962         if (change_driver && backend->set_driver (get_driver())) {
1963                 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1964                 return -1;
1965         }
1966         if (backend->use_separate_input_and_output_devices()) {
1967                 if (change_device && backend->set_input_device_name (get_input_device_name())) {
1968                         error << string_compose (_("Cannot set input device name to %1"), get_input_device_name()) << endmsg;
1969                         return -1;
1970                 }
1971                 if (change_device && backend->set_output_device_name (get_output_device_name())) {
1972                         error << string_compose (_("Cannot set output device name to %1"), get_output_device_name()) << endmsg;
1973                         return -1;
1974                 }
1975         } else {
1976                 if (change_device && backend->set_device_name (get_device_name())) {
1977                         error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1978                         return -1;
1979                 }
1980         }
1981         if (change_rate && backend->set_sample_rate (get_rate())) {
1982                 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1983                 return -1;
1984         }
1985         if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1986                 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1987                 return -1;
1988         }
1989
1990         if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1991                 if (backend->set_input_channels (get_input_channels())) {
1992                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1993                         return -1;
1994                 }
1995                 if (backend->set_output_channels (get_output_channels())) {
1996                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1997                         return -1;
1998                 }
1999         }
2000         if (change_latency) {
2001                 if (backend->set_systemic_input_latency (get_input_latency())) {
2002                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
2003                         return -1;
2004                 }
2005                 if (backend->set_systemic_output_latency (get_output_latency())) {
2006                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
2007                         return -1;
2008                 }
2009         }
2010
2011         if (change_midi) {
2012                 backend->set_midi_option (get_midi_option());
2013         }
2014
2015         if (1 /* TODO */) {
2016                 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
2017                         if (_measure_midi) {
2018                                 if (*p == _measure_midi) {
2019                                         backend->set_midi_device_enabled ((*p)->name, true);
2020                                 } else {
2021                                         backend->set_midi_device_enabled ((*p)->name, false);
2022                                 }
2023                                 continue;
2024                         }
2025                         backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
2026                         if (backend->can_set_systemic_midi_latencies()) {
2027                                 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
2028                                 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
2029                         }
2030                 }
2031         }
2032
2033         if (start || (was_running && restart_required)) {
2034                 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
2035                         return -1;
2036                 }
2037         }
2038
2039         post_push ();
2040
2041         return 0;
2042 }
2043
2044 void
2045 EngineControl::post_push ()
2046 {
2047         /* get a pointer to the current state object, creating one if
2048          * necessary
2049          */
2050
2051         State state = get_saved_state_for_currently_displayed_backend_and_device ();
2052
2053         if (!state) {
2054                 state = save_state ();
2055                 assert (state);
2056         } else {
2057                 store_state(state);
2058         }
2059
2060         /* all off */
2061
2062         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
2063                 (*i)->active = false;
2064         }
2065
2066         /* mark this one active (to be used next time the dialog is
2067          * shown)
2068          */
2069
2070         state->active = true;
2071
2072         if (_have_control) { // XXX
2073                 manage_control_app_sensitivity ();
2074         }
2075
2076         /* schedule a redisplay of MIDI ports */
2077         //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
2078 }
2079
2080
2081 float
2082 EngineControl::get_rate () const
2083 {
2084         float r = atof (sample_rate_combo.get_active_text ());
2085         /* the string may have been translated with an abbreviation for
2086          * thousands, so use a crude heuristic to fix this.
2087          */
2088         if (r < 1000.0) {
2089                 r *= 1000.0;
2090         }
2091         return r;
2092 }
2093
2094
2095 uint32_t
2096 EngineControl::get_buffer_size () const
2097 {
2098         string txt = buffer_size_combo.get_active_text ();
2099         uint32_t samples;
2100
2101         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
2102                 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
2103                 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
2104                 throw exception ();
2105         }
2106
2107         return samples;
2108 }
2109
2110 string
2111 EngineControl::get_midi_option () const
2112 {
2113         return midi_option_combo.get_active_text();
2114 }
2115
2116 uint32_t
2117 EngineControl::get_input_channels() const
2118 {
2119         if (ARDOUR::Profile->get_mixbus()) {
2120                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2121                 if (!backend) return 0;
2122                 return backend->input_channels();
2123         }
2124         return (uint32_t) input_channels_adjustment.get_value();
2125 }
2126
2127 uint32_t
2128 EngineControl::get_output_channels() const
2129 {
2130         if (ARDOUR::Profile->get_mixbus()) {
2131                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2132                 if (!backend) return 0;
2133                 return backend->input_channels();
2134         }
2135         return (uint32_t) output_channels_adjustment.get_value();
2136 }
2137
2138 uint32_t
2139 EngineControl::get_input_latency() const
2140 {
2141         return (uint32_t) input_latency_adjustment.get_value();
2142 }
2143
2144 uint32_t
2145 EngineControl::get_output_latency() const
2146 {
2147         return (uint32_t) output_latency_adjustment.get_value();
2148 }
2149
2150 string
2151 EngineControl::get_backend () const
2152 {
2153         return backend_combo.get_active_text ();
2154 }
2155
2156 string
2157 EngineControl::get_driver () const
2158 {
2159         if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
2160                 return driver_combo.get_active_text ();
2161         } else {
2162                 return "";
2163         }
2164 }
2165
2166 string
2167 EngineControl::get_device_name () const
2168 {
2169         return device_combo.get_active_text ();
2170 }
2171
2172 string
2173 EngineControl::get_input_device_name () const
2174 {
2175         return input_device_combo.get_active_text ();
2176 }
2177
2178 string
2179 EngineControl::get_output_device_name () const
2180 {
2181         return output_device_combo.get_active_text ();
2182 }
2183
2184 void
2185 EngineControl::control_app_button_clicked ()
2186 {
2187         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2188
2189         if (!backend) {
2190                 return;
2191         }
2192
2193         backend->launch_control_app ();
2194 }
2195
2196 void
2197 EngineControl::manage_control_app_sensitivity ()
2198 {
2199         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2200
2201         if (!backend) {
2202                 return;
2203         }
2204
2205         string appname = backend->control_app_name();
2206
2207         if (appname.empty()) {
2208                 control_app_button.set_sensitive (false);
2209         } else {
2210                 control_app_button.set_sensitive (true);
2211         }
2212 }
2213
2214 void
2215 EngineControl::set_desired_sample_rate (uint32_t sr)
2216 {
2217         _desired_sample_rate = sr;
2218         device_changed ();
2219         input_device_changed ();
2220         output_device_changed ();
2221 }
2222
2223 void
2224 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
2225 {
2226         if (page_num == 0) {
2227                 cancel_button->set_sensitive (true);
2228                 ok_button->set_sensitive (true);
2229                 apply_button->set_sensitive (true);
2230                 _measure_midi.reset();
2231         } else {
2232                 cancel_button->set_sensitive (false);
2233                 ok_button->set_sensitive (false);
2234                 apply_button->set_sensitive (false);
2235         }
2236
2237         if (page_num == midi_tab) {
2238                 /* MIDI tab */
2239                 refresh_midi_display ();
2240         }
2241
2242         if (page_num == latency_tab) {
2243                 /* latency tab */
2244
2245                 if (ARDOUR::AudioEngine::instance()->running()) {
2246                         // TODO - mark as 'stopped for latency
2247                         ARDOUR_UI::instance()->disconnect_from_engine ();
2248                 }
2249
2250                 {
2251                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2252
2253                         /* save any existing latency values */
2254
2255                         uint32_t il = (uint32_t) input_latency.get_value ();
2256                         uint32_t ol = (uint32_t) input_latency.get_value ();
2257
2258                         /* reset to zero so that our new test instance
2259                            will be clean of any existing latency measures.
2260
2261                            NB. this should really be done by the backend
2262                            when stated for latency measurement.
2263                         */
2264
2265                         input_latency.set_value (0);
2266                         output_latency.set_value (0);
2267
2268                         push_state_to_backend (false);
2269
2270                         /* reset control */
2271
2272                         input_latency.set_value (il);
2273                         output_latency.set_value (ol);
2274
2275                 }
2276                 // This should be done in push_state_to_backend()
2277                 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
2278                         disable_latency_tab ();
2279                 }
2280
2281                 enable_latency_tab ();
2282
2283         } else {
2284                 if (lm_running) {
2285                         end_latency_detection ();
2286                         ARDOUR::AudioEngine::instance()->stop_latency_detection();
2287                 }
2288         }
2289 }
2290
2291 /* latency measurement */
2292
2293 bool
2294 EngineControl::check_audio_latency_measurement ()
2295 {
2296         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2297
2298         if (mtdm->resolve () < 0) {
2299                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2300                 return true;
2301         }
2302
2303         if (mtdm->err () > 0.3) {
2304                 mtdm->invert ();
2305                 mtdm->resolve ();
2306         }
2307
2308         char buf[256];
2309         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2310
2311         if (sample_rate == 0) {
2312                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2313                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2314                 return false;
2315         }
2316
2317         int frames_total = mtdm->del();
2318         int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2319
2320         snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
2321                         _("Detected roundtrip latency: "),
2322                         frames_total, frames_total * 1000.0f/sample_rate,
2323                         _("Systemic latency: "),
2324                         extra, extra * 1000.0f/sample_rate);
2325
2326         bool solid = true;
2327
2328         if (mtdm->err () > 0.2) {
2329                 strcat (buf, " ");
2330                 strcat (buf, _("(signal detection error)"));
2331                 solid = false;
2332         }
2333
2334         if (mtdm->inv ()) {
2335                 strcat (buf, " ");
2336                 strcat (buf, _("(inverted - bad wiring)"));
2337                 solid = false;
2338         }
2339
2340         lm_results.set_markup (string_compose (results_markup, buf));
2341
2342         if (solid) {
2343                 have_lm_results = true;
2344                 end_latency_detection ();
2345                 lm_use_button.set_sensitive (true);
2346                 return false;
2347         }
2348
2349         return true;
2350 }
2351
2352 bool
2353 EngineControl::check_midi_latency_measurement ()
2354 {
2355         ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2356
2357         if (!mididm->have_signal () || mididm->latency () == 0) {
2358                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2359                 return true;
2360         }
2361
2362         char buf[256];
2363         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2364
2365         if (sample_rate == 0) {
2366                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2367                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2368                 return false;
2369         }
2370
2371         ARDOUR::framecnt_t frames_total = mididm->latency();
2372         ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2373         snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2374                         _("Detected roundtrip latency: "),
2375                         frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2376                         _("Systemic latency: "),
2377                         extra, extra * 1000.0f / sample_rate);
2378
2379         bool solid = true;
2380
2381         if (!mididm->ok ()) {
2382                 strcat (buf, " ");
2383                 strcat (buf, _("(averaging)"));
2384                 solid = false;
2385         }
2386
2387         if (mididm->deviation () > 50.0) {
2388                 strcat (buf, " ");
2389                 strcat (buf, _("(too large jitter)"));
2390                 solid = false;
2391         } else if (mididm->deviation () > 10.0) {
2392                 strcat (buf, " ");
2393                 strcat (buf, _("(large jitter)"));
2394         }
2395
2396         if (solid) {
2397                 have_lm_results = true;
2398                 end_latency_detection ();
2399                 lm_use_button.set_sensitive (true);
2400                 lm_results.set_markup (string_compose (results_markup, buf));
2401                 return false;
2402         } else if (mididm->processed () > 400) {
2403                 have_lm_results = false;
2404                 end_latency_detection ();
2405                 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2406                 return false;
2407         }
2408
2409         lm_results.set_markup (string_compose (results_markup, buf));
2410
2411         return true;
2412 }
2413
2414 void
2415 EngineControl::start_latency_detection ()
2416 {
2417         ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2418         ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2419
2420         if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2421                 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2422                 if (_measure_midi) {
2423                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2424                 } else {
2425                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2426                 }
2427                 lm_measure_label.set_text (_("Cancel"));
2428                 have_lm_results = false;
2429                 lm_use_button.set_sensitive (false);
2430                 lm_input_channel_combo.set_sensitive (false);
2431                 lm_output_channel_combo.set_sensitive (false);
2432                 lm_running = true;
2433         }
2434 }
2435
2436 void
2437 EngineControl::end_latency_detection ()
2438 {
2439         latency_timeout.disconnect ();
2440         ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2441         lm_measure_label.set_text (_("Measure"));
2442         if (!have_lm_results) {
2443                 lm_use_button.set_sensitive (false);
2444         }
2445         lm_input_channel_combo.set_sensitive (true);
2446         lm_output_channel_combo.set_sensitive (true);
2447         lm_running = false;
2448 }
2449
2450 void
2451 EngineControl::latency_button_clicked ()
2452 {
2453         if (!lm_running) {
2454                 start_latency_detection ();
2455         } else {
2456                 end_latency_detection ();
2457         }
2458 }
2459
2460 void
2461 EngineControl::use_latency_button_clicked ()
2462 {
2463         if (_measure_midi) {
2464                 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2465                 if (!mididm) {
2466                         return;
2467                 }
2468                 ARDOUR::framecnt_t frames_total = mididm->latency();
2469                 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2470                 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2471                 _measure_midi->input_latency = one_way;
2472                 _measure_midi->output_latency = one_way;
2473                 notebook.set_current_page (midi_tab);
2474         } else {
2475                 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2476
2477                 if (!mtdm) {
2478                         return;
2479                 }
2480
2481                 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2482                 one_way = std::max (0., one_way);
2483
2484                 input_latency_adjustment.set_value (one_way);
2485                 output_latency_adjustment.set_value (one_way);
2486
2487                 /* back to settings page */
2488                 notebook.set_current_page (0);
2489 }
2490         }
2491
2492
2493 bool
2494 EngineControl::on_delete_event (GdkEventAny* ev)
2495 {
2496         if (notebook.get_current_page() == 2) {
2497                 /* currently on latency tab - be sure to clean up */
2498                 end_latency_detection ();
2499         }
2500         return ArdourDialog::on_delete_event (ev);
2501 }
2502
2503 void
2504 EngineControl::engine_running ()
2505 {
2506         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2507         assert (backend);
2508
2509         set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2510         sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2511
2512         buffer_size_combo.set_sensitive (true);
2513         sample_rate_combo.set_sensitive (true);
2514
2515         connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2516         connect_disconnect_button.show();
2517
2518         started_at_least_once = true;
2519         engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2520 }
2521
2522 void
2523 EngineControl::engine_stopped ()
2524 {
2525         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2526         assert (backend);
2527
2528         buffer_size_combo.set_sensitive (false);
2529         connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2530         connect_disconnect_button.show();
2531
2532         sample_rate_combo.set_sensitive (true);
2533         buffer_size_combo.set_sensitive (true);
2534         engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2535 }
2536
2537 void
2538 EngineControl::device_list_changed ()
2539 {
2540         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2541         list_devices ();
2542         midi_option_changed();
2543 }
2544
2545 void
2546 EngineControl::connect_disconnect_click()
2547 {
2548         if (ARDOUR::AudioEngine::instance()->running()) {
2549                 ARDOUR_UI::instance()->disconnect_from_engine ();
2550         } else {
2551                 ARDOUR_UI::instance()->reconnect_to_engine ();
2552         }
2553 }
2554
2555 void
2556 EngineControl::calibrate_audio_latency ()
2557 {
2558         _measure_midi.reset ();
2559         have_lm_results = false;
2560         lm_use_button.set_sensitive (false);
2561         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2562         notebook.set_current_page (latency_tab);
2563 }
2564
2565 void
2566 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2567 {
2568         _measure_midi = s;
2569         have_lm_results = false;
2570         lm_use_button.set_sensitive (false);
2571         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2572         notebook.set_current_page (latency_tab);
2573 }
2574
2575 void
2576 EngineControl::configure_midi_devices ()
2577 {
2578         notebook.set_current_page (midi_tab);
2579 }