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