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