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