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