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