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