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