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