engine dialog, update device is changed.
[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                 cout << "Mixbus crash trap. sb->get_value(): " << sb->get_value();
799                 return true;
800         }
801         
802         uint32_t cnt = (uint32_t) sb->get_value();
803         if (cnt == 0) {
804                 sb->set_text (_("all available channels"));
805         } else {
806                 char buf[32];
807                 snprintf (buf, sizeof (buf), "%d", cnt);
808                 sb->set_text (buf);
809         }
810         return true;
811 }
812
813 void
814 EngineControl::list_devices ()
815 {
816         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
817         assert (backend);
818
819         /* now fill out devices, mark sample rates, buffer sizes insensitive */
820
821         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
822
823         /* NOTE: Ardour currently does not display the "available" field of the
824          * returned devices.
825          *
826          * Doing so would require a different GUI widget than the combo
827          * box/popdown that we currently use, since it has no way to list
828          * items that are not selectable. Something more like a popup menu,
829          * which could have unselectable items, would be appropriate.
830          */
831
832         vector<string> available_devices;
833
834         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
835                 available_devices.push_back (i->name);
836         }
837
838         if (!available_devices.empty()) {
839
840                 update_sensitivity ();
841
842                 {
843                         string current_device, found_device;
844                         current_device = device_combo.get_active_text ();
845                         if (current_device == "") {
846                                 current_device = backend->device_name ();
847                         }
848
849                         // Make sure that the active text is still relevant for this
850                         // device (it might only be relevant to the previous device!!)
851                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
852                                 if (*i == current_device)
853                                         found_device = current_device;
854                         }
855                         if (found_device == "")
856                                 // device has never been set (or was not relevant
857                                 // for this backend) Let's make sure it's not blank
858                                 current_device = available_devices.front ();
859
860                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
861                         set_popdown_strings (device_combo, available_devices);
862
863                         device_combo.set_active_text (current_device);
864                 }
865
866                 device_changed ();
867
868                 input_latency.set_sensitive (true);
869                 output_latency.set_sensitive (true);
870                 input_channels.set_sensitive (true);
871                 output_channels.set_sensitive (true);
872
873                 ok_button->set_sensitive (true);
874                 apply_button->set_sensitive (true);
875
876         } else {
877                 device_combo.clear();
878                 sample_rate_combo.set_sensitive (false);
879                 buffer_size_combo.set_sensitive (false);
880                 input_latency.set_sensitive (false);
881                 output_latency.set_sensitive (false);
882                 input_channels.set_sensitive (false);
883                 output_channels.set_sensitive (false);
884                 if (_have_control) {
885                         ok_button->set_sensitive (false);
886                         apply_button->set_sensitive (false);
887                 } else {
888                         ok_button->set_sensitive (true);
889                         apply_button->set_sensitive (true);
890                         if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
891                                 sample_rate_combo.set_sensitive (true);
892                         }
893                         if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
894                                 buffer_size_combo.set_sensitive (true);
895                         }
896
897                 }
898         }
899 }
900
901 void
902 EngineControl::driver_changed ()
903 {
904         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
905         assert (backend);
906
907         backend->set_driver (driver_combo.get_active_text());
908         list_devices ();
909
910         if (!ignore_changes) {
911                 maybe_display_saved_state ();
912         }
913 }
914
915 void
916 EngineControl::device_changed ()
917 {
918
919         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
920         assert (backend);
921         string device_name = device_combo.get_active_text ();
922         vector<string> s;
923
924         if (device_name != backend->device_name()) {
925                 /* we set the backend-device to query various device related intormation.
926                  * This has the side effect that backend->device_name() will match
927                  * the device_name and  'change_device' will never be true.
928                  * so work around this by setting...
929                  */
930                 queue_device_changed = true;
931         }
932         
933         //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
934         backend->set_device_name(device_name);
935
936         {
937                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
938
939                 /* don't allow programmatic change to combos to cause a
940                    recursive call to this method.
941                  */
942
943                 /* sample rates */
944
945                 string desired;
946
947                 vector<float> sr;
948
949                 if (_have_control) {
950                         sr = backend->available_sample_rates (device_name);
951                 } else {
952
953                         sr.push_back (8000.0f);
954                         sr.push_back (16000.0f);
955                         sr.push_back (32000.0f);
956                         sr.push_back (44100.0f);
957                         sr.push_back (48000.0f);
958                         sr.push_back (88200.0f);
959                         sr.push_back (96000.0f);
960                         sr.push_back (192000.0f);
961                         sr.push_back (384000.0f);
962                 }
963
964                 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
965                         s.push_back (rate_as_string (*x));
966                         if (*x == _desired_sample_rate) {
967                                 desired = s.back();
968                         }
969                 }
970
971                 if (!s.empty()) {
972                         sample_rate_combo.set_sensitive (true);
973                         set_popdown_strings (sample_rate_combo, s);
974
975                         if (desired.empty()) {
976                                 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
977                         } else {
978                                 sample_rate_combo.set_active_text (desired);
979                         }
980
981                 } else {
982                         sample_rate_combo.set_sensitive (false);
983                 }
984
985                 /* buffer sizes */
986
987                 vector<uint32_t> bs;
988
989                 if (_have_control) {
990                         bs = backend->available_buffer_sizes (device_name);
991                 } else if (backend->can_change_buffer_size_when_running()) {
992                         bs.push_back (8);
993                         bs.push_back (16);
994                         bs.push_back (32);
995                         bs.push_back (64);
996                         bs.push_back (128);
997                         bs.push_back (256);
998                         bs.push_back (512);
999                         bs.push_back (1024);
1000                         bs.push_back (2048);
1001                         bs.push_back (4096);
1002                         bs.push_back (8192);
1003                 }
1004                 s.clear ();
1005                 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1006                         s.push_back (bufsize_as_string (*x));
1007                 }
1008
1009                 if (!s.empty()) {
1010                         buffer_size_combo.set_sensitive (true);
1011                         set_popdown_strings (buffer_size_combo, s);
1012
1013                         uint32_t period = backend->buffer_size();
1014                         if (0 == period) {
1015                                 period = backend->default_buffer_size(device_name);
1016                         }
1017                         buffer_size_combo.set_active_text (bufsize_as_string (period));
1018                         show_buffer_duration ();
1019                 } else {
1020                         buffer_size_combo.set_sensitive (false);
1021                 }
1022
1023                 /* XXX theoretically need to set min + max channel counts here
1024                 */
1025
1026                 manage_control_app_sensitivity ();
1027         }
1028
1029         /* pick up any saved state for this device */
1030
1031         if (!ignore_changes) {
1032                 maybe_display_saved_state ();
1033         }
1034 }
1035
1036 string
1037 EngineControl::bufsize_as_string (uint32_t sz)
1038 {
1039         /* Translators: "samples" is always plural here, so no
1040            need for plural+singular forms.
1041          */
1042         char buf[32];
1043         snprintf (buf, sizeof (buf), _("%u samples"), sz);
1044         return buf;
1045 }
1046
1047 void
1048 EngineControl::sample_rate_changed ()
1049 {
1050         /* reset the strings for buffer size to show the correct msec value
1051            (reflecting the new sample rate).
1052          */
1053
1054         show_buffer_duration ();
1055         if (!ignore_changes) {
1056                 save_state ();
1057         }
1058
1059 }
1060
1061 void
1062 EngineControl::buffer_size_changed ()
1063 {
1064         show_buffer_duration ();
1065         if (!ignore_changes) {
1066                 save_state ();
1067         }
1068 }
1069
1070 void
1071 EngineControl::show_buffer_duration ()
1072 {
1073
1074         /* buffer sizes  - convert from just samples to samples + msecs for
1075          * the displayed string
1076          */
1077
1078         string bs_text = buffer_size_combo.get_active_text ();
1079         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1080         uint32_t rate = get_rate();
1081
1082         /* Developers: note the hard-coding of a double buffered model
1083            in the (2 * samples) computation of latency. we always start
1084            the audiobackend in this configuration.
1085          */
1086         /* note to jack1 developers: ardour also always starts the engine
1087          * in async mode (no jack2 --sync option) which adds an extra cycle
1088          * of latency with jack2 (and *3 would be correct)
1089          * The value can also be wrong if jackd is started externally..
1090          *
1091          * At the time of writing the ALSA backend always uses double-buffering *2,
1092          * The Dummy backend *1, and who knows what ASIO really does :)
1093          *
1094          * So just display the period size, that's also what
1095          * ARDOUR_UI::update_sample_rate() does for the status bar.
1096          * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1097          * but still, that's the buffer period, not [round-trip] latency)
1098          */
1099         char buf[32];
1100         snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1101         buffer_size_duration_label.set_text (buf);
1102 }
1103
1104 void
1105 EngineControl::midi_option_changed ()
1106 {
1107         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1108         assert (backend);
1109
1110         backend->set_midi_option (get_midi_option());
1111
1112         vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1113
1114         //_midi_devices.clear(); // TODO merge with state-saved settings..
1115         _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1116         std::vector<MidiDeviceSettings> new_devices;
1117
1118         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1119                 MidiDeviceSettings mds = find_midi_device (i->name);
1120                 if (i->available && !mds) {
1121                         uint32_t input_latency = 0;
1122                         uint32_t output_latency = 0;
1123                         if (_can_set_midi_latencies) {
1124                                 input_latency = backend->systemic_midi_input_latency (i->name);
1125                                 output_latency = backend->systemic_midi_output_latency (i->name);
1126                         }
1127                         bool enabled = backend->midi_device_enabled (i->name);
1128                         MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1129                         new_devices.push_back (ptr);
1130                 } else if (i->available) {
1131                         new_devices.push_back (mds);
1132                 }
1133         }
1134         _midi_devices = new_devices;
1135
1136         if (_midi_devices.empty()) {
1137                 midi_devices_button.set_sensitive (false);
1138         } else {
1139                 midi_devices_button.set_sensitive (true);
1140         }
1141
1142         if (!ignore_changes) {
1143                 save_state ();
1144         }
1145 }
1146
1147 void
1148 EngineControl::parameter_changed ()
1149 {
1150         if (!ignore_changes) {
1151                 save_state ();
1152         }
1153 }
1154
1155 EngineControl::State
1156 EngineControl::get_matching_state (
1157                 const string& backend,
1158                 const string& driver,
1159                 const string& device)
1160 {
1161         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1162                 if ((*i)->backend == backend &&
1163                                 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1164                 {
1165                         return (*i);
1166                 }
1167         }
1168         return State();
1169 }
1170
1171 EngineControl::State
1172 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1173 {
1174         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1175
1176         if (backend) {
1177                 return get_matching_state (backend_combo.get_active_text(),
1178                                 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1179                                 device_combo.get_active_text());
1180         }
1181
1182
1183         return get_matching_state (backend_combo.get_active_text(),
1184                         string(),
1185                         device_combo.get_active_text());
1186 }
1187
1188 EngineControl::State
1189 EngineControl::save_state ()
1190 {
1191         State state;
1192
1193         if (!_have_control) {
1194                 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1195                 if (state) {
1196                         return state;
1197                 }
1198                 state.reset(new StateStruct);
1199                 state->backend = get_backend ();
1200         } else {
1201                 state.reset(new StateStruct);
1202                 store_state (state);
1203         }
1204
1205         for (StateList::iterator i = states.begin(); i != states.end();) {
1206                 if ((*i)->backend == state->backend &&
1207                                 (*i)->driver == state->driver &&
1208                                 (*i)->device == state->device) {
1209                         i =  states.erase(i);
1210                 } else {
1211                         ++i;
1212                 }
1213         }
1214
1215         states.push_back (state);
1216
1217         return state;
1218 }
1219
1220 void
1221 EngineControl::store_state (State state)
1222 {
1223         state->backend = get_backend ();
1224         state->driver = get_driver ();
1225         state->device = get_device_name ();
1226         state->sample_rate = get_rate ();
1227         state->buffer_size = get_buffer_size ();
1228         state->input_latency = get_input_latency ();
1229         state->output_latency = get_output_latency ();
1230         state->input_channels = get_input_channels ();
1231         state->output_channels = get_output_channels ();
1232         state->midi_option = get_midi_option ();
1233         state->midi_devices = _midi_devices;
1234 }
1235
1236 void
1237 EngineControl::maybe_display_saved_state ()
1238 {
1239         if (!_have_control) {
1240                 return;
1241         }
1242
1243         State state = get_saved_state_for_currently_displayed_backend_and_device ();
1244
1245         if (state) {
1246                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1247
1248                 if (!_desired_sample_rate) {
1249                         sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1250                 }
1251                 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
1252                 /* call this explicitly because we're ignoring changes to
1253                    the controls at this point.
1254                  */
1255                 show_buffer_duration ();
1256                 input_latency.set_value (state->input_latency);
1257                 output_latency.set_value (state->output_latency);
1258
1259                 if (!state->midi_option.empty()) {
1260                         midi_option_combo.set_active_text (state->midi_option);
1261                         _midi_devices = state->midi_devices;
1262                 }
1263         }
1264 }
1265
1266 XMLNode&
1267 EngineControl::get_state ()
1268 {
1269         XMLNode* root = new XMLNode ("AudioMIDISetup");
1270         std::string path;
1271
1272         if (!states.empty()) {
1273                 XMLNode* state_nodes = new XMLNode ("EngineStates");
1274
1275                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1276
1277                         XMLNode* node = new XMLNode ("State");
1278
1279                         node->add_property ("backend", (*i)->backend);
1280                         node->add_property ("driver", (*i)->driver);
1281                         node->add_property ("device", (*i)->device);
1282                         node->add_property ("sample-rate", (*i)->sample_rate);
1283                         node->add_property ("buffer-size", (*i)->buffer_size);
1284                         node->add_property ("input-latency", (*i)->input_latency);
1285                         node->add_property ("output-latency", (*i)->output_latency);
1286                         node->add_property ("input-channels", (*i)->input_channels);
1287                         node->add_property ("output-channels", (*i)->output_channels);
1288                         node->add_property ("active", (*i)->active ? "yes" : "no");
1289                         node->add_property ("midi-option", (*i)->midi_option);
1290
1291                         XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1292                         for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1293                                 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1294                                 midi_device_stuff->add_property (X_("name"), (*p)->name);
1295                                 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1296                                 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1297                                 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1298                                 midi_devices->add_child_nocopy (*midi_device_stuff);
1299                         }
1300                         node->add_child_nocopy (*midi_devices);
1301
1302                         state_nodes->add_child_nocopy (*node);
1303                 }
1304
1305                 root->add_child_nocopy (*state_nodes);
1306         }
1307
1308         return *root;
1309 }
1310
1311 void
1312 EngineControl::set_state (const XMLNode& root)
1313 {
1314         XMLNodeList          clist, cclist;
1315         XMLNodeConstIterator citer, cciter;
1316         XMLNode* child;
1317         XMLNode* grandchild;
1318         XMLProperty* prop = NULL;
1319
1320         if (root.name() != "AudioMIDISetup") {
1321                 return;
1322         }
1323
1324         clist = root.children();
1325
1326         states.clear ();
1327
1328         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1329
1330                 child = *citer;
1331
1332                 if (child->name() != "EngineStates") {
1333                         continue;
1334                 }
1335
1336                 cclist = child->children();
1337
1338                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1339                         State state (new StateStruct);
1340
1341                         grandchild = *cciter;
1342
1343                         if (grandchild->name() != "State") {
1344                                 continue;
1345                         }
1346
1347                         if ((prop = grandchild->property ("backend")) == 0) {
1348                                 continue;
1349                         }
1350                         state->backend = prop->value ();
1351
1352                         if ((prop = grandchild->property ("driver")) == 0) {
1353                                 continue;
1354                         }
1355                         state->driver = prop->value ();
1356
1357                         if ((prop = grandchild->property ("device")) == 0) {
1358                                 continue;
1359                         }
1360                         state->device = prop->value ();
1361
1362                         if ((prop = grandchild->property ("sample-rate")) == 0) {
1363                                 continue;
1364                         }
1365                         state->sample_rate = atof (prop->value ());
1366
1367                         if ((prop = grandchild->property ("buffer-size")) == 0) {
1368                                 continue;
1369                         }
1370                         state->buffer_size = atoi (prop->value ());
1371
1372                         if ((prop = grandchild->property ("input-latency")) == 0) {
1373                                 continue;
1374                         }
1375                         state->input_latency = atoi (prop->value ());
1376
1377                         if ((prop = grandchild->property ("output-latency")) == 0) {
1378                                 continue;
1379                         }
1380                         state->output_latency = atoi (prop->value ());
1381
1382                         if ((prop = grandchild->property ("input-channels")) == 0) {
1383                                 continue;
1384                         }
1385                         state->input_channels = atoi (prop->value ());
1386
1387                         if ((prop = grandchild->property ("output-channels")) == 0) {
1388                                 continue;
1389                         }
1390                         state->output_channels = atoi (prop->value ());
1391
1392                         if ((prop = grandchild->property ("active")) == 0) {
1393                                 continue;
1394                         }
1395                         state->active = string_is_affirmative (prop->value ());
1396
1397                         if ((prop = grandchild->property ("midi-option")) == 0) {
1398                                 continue;
1399                         }
1400                         state->midi_option = prop->value ();
1401
1402                         state->midi_devices.clear();
1403                         XMLNode* midinode;
1404                         if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1405                                 const XMLNodeList mnc = midinode->children();
1406                                 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1407                                         if ((*n)->property (X_("name")) == 0
1408                                                         || (*n)->property (X_("enabled")) == 0
1409                                                         || (*n)->property (X_("input-latency")) == 0
1410                                                         || (*n)->property (X_("output-latency")) == 0
1411                                                  ) {
1412                                                 continue;
1413                                         }
1414
1415                                         MidiDeviceSettings ptr (new MidiDeviceSetting(
1416                                                                 (*n)->property (X_("name"))->value (),
1417                                                                 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1418                                                                 atoi ((*n)->property (X_("input-latency"))->value ()),
1419                                                                 atoi ((*n)->property (X_("output-latency"))->value ())
1420                                                                 ));
1421                                         state->midi_devices.push_back (ptr);
1422                                 }
1423                         }
1424
1425 #if 1
1426                         /* remove accumulated duplicates (due to bug in ealier version)
1427                          * this can be removed again before release
1428                          */
1429                         for (StateList::iterator i = states.begin(); i != states.end();) {
1430                                 if ((*i)->backend == state->backend &&
1431                                                 (*i)->driver == state->driver &&
1432                                                 (*i)->device == state->device) {
1433                                         i =  states.erase(i);
1434                                 } else {
1435                                         ++i;
1436                                 }
1437                         }
1438 #endif
1439
1440                         states.push_back (state);
1441                 }
1442         }
1443
1444         /* now see if there was an active state and switch the setup to it */
1445
1446         // purge states of backend that are not available in this built
1447         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1448         vector<std::string> backend_names;
1449
1450         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1451                 backend_names.push_back((*i)->name);
1452         }
1453         for (StateList::iterator i = states.begin(); i != states.end();) {
1454                 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1455                         i = states.erase(i);
1456                 } else {
1457                         ++i;
1458                 }
1459         }
1460
1461         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1462
1463                 if ((*i)->active) {
1464                         ignore_changes++;
1465                         backend_combo.set_active_text ((*i)->backend);
1466                         driver_combo.set_active_text ((*i)->driver);
1467                         device_combo.set_active_text ((*i)->device);
1468                         sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1469                         buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1470                         input_latency.set_value ((*i)->input_latency);
1471                         output_latency.set_value ((*i)->output_latency);
1472                         midi_option_combo.set_active_text ((*i)->midi_option);
1473                         ignore_changes--;
1474                         break;
1475                 }
1476         }
1477 }
1478
1479 int
1480 EngineControl::push_state_to_backend (bool start)
1481 {
1482         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1483
1484         if (!backend) {
1485                 return 0;
1486         }
1487
1488         /* figure out what is going to change */
1489
1490         bool restart_required = false;
1491         bool was_running = ARDOUR::AudioEngine::instance()->running();
1492         bool change_driver = false;
1493         bool change_device = false;
1494         bool change_rate = false;
1495         bool change_bufsize = false;
1496         bool change_latency = false;
1497         bool change_channels = false;
1498         bool change_midi = false;
1499
1500         uint32_t ochan = get_output_channels ();
1501         uint32_t ichan = get_input_channels ();
1502
1503         if (_have_control) {
1504
1505                 if (started_at_least_once) {
1506
1507                         /* we can control the backend */
1508
1509                         if (backend->requires_driver_selection()) {
1510                                 if (get_driver() != backend->driver_name()) {
1511                                         change_driver = true;
1512                                 }
1513                         }
1514
1515                         if (queue_device_changed || get_device_name() != backend->device_name()) {
1516                                 change_device = true;
1517                         }
1518
1519                         if (get_rate() != backend->sample_rate()) {
1520                                 change_rate = true;
1521                         }
1522
1523                         if (get_buffer_size() != backend->buffer_size()) {
1524                                 change_bufsize = true;
1525                         }
1526
1527                         if (get_midi_option() != backend->midi_option()) {
1528                                 change_midi = true;
1529                         }
1530
1531                         /* zero-requested channels means "all available" */
1532
1533                         if (ichan == 0) {
1534                                 ichan = backend->input_channels();
1535                         }
1536
1537                         if (ochan == 0) {
1538                                 ochan = backend->output_channels();
1539                         }
1540
1541                         if (ichan != backend->input_channels()) {
1542                                 change_channels = true;
1543                         }
1544
1545                         if (ochan != backend->output_channels()) {
1546                                 change_channels = true;
1547                         }
1548
1549                         if (get_input_latency() != backend->systemic_input_latency() ||
1550                                         get_output_latency() != backend->systemic_output_latency()) {
1551                                 change_latency = true;
1552                         }
1553                 } else {
1554                         /* backend never started, so we have to force a group
1555                            of settings.
1556                          */
1557                         change_device = true;
1558                         if (backend->requires_driver_selection()) {
1559                                 change_driver = true;
1560                         }
1561                         change_rate = true;
1562                         change_bufsize = true;
1563                         change_channels = true;
1564                         change_latency = true;
1565                         change_midi = true;
1566                 }
1567
1568         } else {
1569
1570                 /* we have no control over the backend, meaning that we can
1571                  * only possibly change sample rate and buffer size.
1572                  */
1573
1574
1575                 if (get_rate() != backend->sample_rate()) {
1576                         change_bufsize = true;
1577                 }
1578
1579                 if (get_buffer_size() != backend->buffer_size()) {
1580                         change_bufsize = true;
1581                 }
1582         }
1583
1584         queue_device_changed = false;
1585
1586         if (!_have_control) {
1587
1588                 /* We do not have control over the backend, so the best we can
1589                  * do is try to change the sample rate and/or bufsize and get
1590                  * out of here.
1591                  */
1592
1593                 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1594                         return 1;
1595                 }
1596
1597                 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1598                         return 1;
1599                 }
1600
1601                 if (change_rate) {
1602                         backend->set_sample_rate (get_rate());
1603                 }
1604
1605                 if (change_bufsize) {
1606                         backend->set_buffer_size (get_buffer_size());
1607                 }
1608
1609                 if (start) {
1610                         if (ARDOUR::AudioEngine::instance()->start ()) {
1611                                 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1612                                 return -1;
1613                         }
1614                 }
1615
1616                 post_push ();
1617
1618                 return 0;
1619         }
1620
1621         /* determine if we need to stop the backend before changing parameters */
1622
1623         if (change_driver || change_device || change_channels || change_latency ||
1624                         (change_rate && !backend->can_change_sample_rate_when_running()) ||
1625                         change_midi ||
1626                         (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1627                 restart_required = true;
1628         } else {
1629                 restart_required = false;
1630         }
1631
1632         if (was_running) {
1633
1634                 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1635                         /* no changes in any parameters that absolutely require a
1636                          * restart, so check those that might be changeable without a
1637                          * restart
1638                          */
1639
1640                         if (change_rate && !backend->can_change_sample_rate_when_running()) {
1641                                 /* can't do this while running ... */
1642                                 restart_required = true;
1643                         }
1644
1645                         if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1646                                 /* can't do this while running ... */
1647                                 restart_required = true;
1648                         }
1649                 }
1650         }
1651
1652         if (was_running) {
1653                 if (restart_required) {
1654                         if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1655                                 return -1;
1656                         }
1657                 }
1658         }
1659
1660
1661         if (change_driver && backend->set_driver (get_driver())) {
1662                 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1663                 return -1;
1664         }
1665         if (change_device && backend->set_device_name (get_device_name())) {
1666                 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1667                 return -1;
1668         }
1669         if (change_rate && backend->set_sample_rate (get_rate())) {
1670                 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1671                 return -1;
1672         }
1673         if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1674                 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1675                 return -1;
1676         }
1677
1678         if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1679                 if (backend->set_input_channels (get_input_channels())) {
1680                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1681                         return -1;
1682                 }
1683                 if (backend->set_output_channels (get_output_channels())) {
1684                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1685                         return -1;
1686                 }
1687         }
1688         if (change_latency) {
1689                 if (backend->set_systemic_input_latency (get_input_latency())) {
1690                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1691                         return -1;
1692                 }
1693                 if (backend->set_systemic_output_latency (get_output_latency())) {
1694                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1695                         return -1;
1696                 }
1697         }
1698
1699         if (change_midi) {
1700                 backend->set_midi_option (get_midi_option());
1701         }
1702
1703         if (1 /* TODO */) {
1704                 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1705                         if (_measure_midi) {
1706                                 if (*p == _measure_midi) {
1707                                         backend->set_midi_device_enabled ((*p)->name, true);
1708                                 } else {
1709                                         backend->set_midi_device_enabled ((*p)->name, false);
1710                                 }
1711                                 continue;
1712                         }
1713                         backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1714                         if (backend->can_set_systemic_midi_latencies()) {
1715                                 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1716                                 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1717                         }
1718                 }
1719         }
1720
1721         if (start || (was_running && restart_required)) {
1722                 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1723                         return -1;
1724                 }
1725         }
1726
1727         post_push ();
1728
1729         return 0;
1730 }
1731
1732 void
1733 EngineControl::post_push ()
1734 {
1735         /* get a pointer to the current state object, creating one if
1736          * necessary
1737          */
1738
1739         State state = get_saved_state_for_currently_displayed_backend_and_device ();
1740
1741         if (!state) {
1742                 state = save_state ();
1743                 assert (state);
1744         }
1745
1746         /* all off */
1747
1748         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1749                 (*i)->active = false;
1750         }
1751
1752         /* mark this one active (to be used next time the dialog is
1753          * shown)
1754          */
1755
1756         state->active = true;
1757
1758         if (_have_control) { // XXX
1759                 manage_control_app_sensitivity ();
1760         }
1761
1762         /* schedule a redisplay of MIDI ports */
1763         //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1764 }
1765
1766
1767 float
1768 EngineControl::get_rate () const
1769 {
1770         float r = atof (sample_rate_combo.get_active_text ());
1771         /* the string may have been translated with an abbreviation for
1772          * thousands, so use a crude heuristic to fix this.
1773          */
1774         if (r < 1000.0) {
1775                 r *= 1000.0;
1776         }
1777         return r;
1778 }
1779
1780
1781 uint32_t
1782 EngineControl::get_buffer_size () const
1783 {
1784         string txt = buffer_size_combo.get_active_text ();
1785         uint32_t samples;
1786
1787         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1788                 throw exception ();
1789         }
1790
1791         return samples;
1792 }
1793
1794 string
1795 EngineControl::get_midi_option () const
1796 {
1797         return midi_option_combo.get_active_text();
1798 }
1799
1800 uint32_t
1801 EngineControl::get_input_channels() const
1802 {
1803         if (ARDOUR::Profile->get_mixbus()) {
1804                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1805                 if (!backend) return 0;
1806                 return backend->input_channels();
1807         }
1808         return (uint32_t) input_channels_adjustment.get_value();
1809 }
1810
1811 uint32_t
1812 EngineControl::get_output_channels() const
1813 {
1814         if (ARDOUR::Profile->get_mixbus()) {
1815                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1816                 if (!backend) return 0;
1817                 return backend->input_channels();
1818         }
1819         return (uint32_t) output_channels_adjustment.get_value();
1820 }
1821
1822 uint32_t
1823 EngineControl::get_input_latency() const
1824 {
1825         return (uint32_t) input_latency_adjustment.get_value();
1826 }
1827
1828 uint32_t
1829 EngineControl::get_output_latency() const
1830 {
1831         return (uint32_t) output_latency_adjustment.get_value();
1832 }
1833
1834 string
1835 EngineControl::get_backend () const
1836 {
1837         return backend_combo.get_active_text ();
1838 }
1839
1840 string
1841 EngineControl::get_driver () const
1842 {
1843         if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1844                 return driver_combo.get_active_text ();
1845         } else {
1846                 return "";
1847         }
1848 }
1849
1850 string
1851 EngineControl::get_device_name () const
1852 {
1853         return device_combo.get_active_text ();
1854 }
1855
1856 void
1857 EngineControl::control_app_button_clicked ()
1858 {
1859         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1860
1861         if (!backend) {
1862                 return;
1863         }
1864
1865         backend->launch_control_app ();
1866 }
1867
1868 void
1869 EngineControl::manage_control_app_sensitivity ()
1870 {
1871         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1872
1873         if (!backend) {
1874                 return;
1875         }
1876
1877         string appname = backend->control_app_name();
1878
1879         if (appname.empty()) {
1880                 control_app_button.set_sensitive (false);
1881         } else {
1882                 control_app_button.set_sensitive (true);
1883         }
1884 }
1885
1886 void
1887 EngineControl::set_desired_sample_rate (uint32_t sr)
1888 {
1889         _desired_sample_rate = sr;
1890         device_changed ();
1891 }
1892
1893 void
1894 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1895 {
1896         if (page_num == 0) {
1897                 cancel_button->set_sensitive (true);
1898                 ok_button->set_sensitive (true);
1899                 apply_button->set_sensitive (true);
1900                 _measure_midi.reset();
1901         } else {
1902                 cancel_button->set_sensitive (false);
1903                 ok_button->set_sensitive (false);
1904                 apply_button->set_sensitive (false);
1905         }
1906
1907         if (page_num == midi_tab) {
1908                 /* MIDI tab */
1909                 refresh_midi_display ();
1910         }
1911
1912         if (page_num == latency_tab) {
1913                 /* latency tab */
1914
1915                 if (ARDOUR::AudioEngine::instance()->running()) {
1916                         // TODO - mark as 'stopped for latency
1917                         ARDOUR_UI::instance()->disconnect_from_engine ();
1918                 }
1919
1920                 {
1921                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1922
1923                         /* save any existing latency values */
1924
1925                         uint32_t il = (uint32_t) input_latency.get_value ();
1926                         uint32_t ol = (uint32_t) input_latency.get_value ();
1927
1928                         /* reset to zero so that our new test instance
1929                            will be clean of any existing latency measures.
1930
1931                            NB. this should really be done by the backend
1932                            when stated for latency measurement.
1933                         */
1934
1935                         input_latency.set_value (0);
1936                         output_latency.set_value (0);
1937
1938                         push_state_to_backend (false);
1939
1940                         /* reset control */
1941
1942                         input_latency.set_value (il);
1943                         output_latency.set_value (ol);
1944
1945                 }
1946                 // This should be done in push_state_to_backend()
1947                 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1948                         disable_latency_tab ();
1949                 }
1950
1951                 enable_latency_tab ();
1952
1953         } else {
1954                 if (lm_running) {
1955                         end_latency_detection ();
1956                         ARDOUR::AudioEngine::instance()->stop_latency_detection();
1957                 }
1958         }
1959 }
1960
1961 /* latency measurement */
1962
1963 bool
1964 EngineControl::check_audio_latency_measurement ()
1965 {
1966         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1967
1968         if (mtdm->resolve () < 0) {
1969                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1970                 return true;
1971         }
1972
1973         if (mtdm->err () > 0.3) {
1974                 mtdm->invert ();
1975                 mtdm->resolve ();
1976         }
1977
1978         char buf[256];
1979         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1980
1981         if (sample_rate == 0) {
1982                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1983                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1984                 return false;
1985         }
1986
1987         int frames_total = mtdm->del();
1988         int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1989
1990         snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1991                         _("Detected roundtrip latency: "),
1992                         frames_total, frames_total * 1000.0f/sample_rate,
1993                         _("Systemic latency: "),
1994                         extra, extra * 1000.0f/sample_rate);
1995
1996         bool solid = true;
1997
1998         if (mtdm->err () > 0.2) {
1999                 strcat (buf, " ");
2000                 strcat (buf, _("(signal detection error)"));
2001                 solid = false;
2002         }
2003
2004         if (mtdm->inv ()) {
2005                 strcat (buf, " ");
2006                 strcat (buf, _("(inverted - bad wiring)"));
2007                 solid = false;
2008         }
2009
2010         lm_results.set_markup (string_compose (results_markup, buf));
2011
2012         if (solid) {
2013                 have_lm_results = true;
2014                 end_latency_detection ();
2015                 lm_use_button.set_sensitive (true);
2016                 return false;
2017         }
2018
2019         return true;
2020 }
2021
2022 bool
2023 EngineControl::check_midi_latency_measurement ()
2024 {
2025         ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2026
2027         if (!mididm->have_signal () || mididm->latency () == 0) {
2028                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2029                 return true;
2030         }
2031
2032         char buf[256];
2033         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2034
2035         if (sample_rate == 0) {
2036                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2037                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2038                 return false;
2039         }
2040
2041         ARDOUR::framecnt_t frames_total = mididm->latency();
2042         ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2043         snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2044                         _("Detected roundtrip latency: "),
2045                         frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2046                         _("Systemic latency: "),
2047                         extra, extra * 1000.0f / sample_rate);
2048
2049         bool solid = true;
2050
2051         if (!mididm->ok ()) {
2052                 strcat (buf, " ");
2053                 strcat (buf, _("(averaging)"));
2054                 solid = false;
2055         }
2056
2057         if (mididm->deviation () > 50.0) {
2058                 strcat (buf, " ");
2059                 strcat (buf, _("(too large jitter)"));
2060                 solid = false;
2061         } else if (mididm->deviation () > 10.0) {
2062                 strcat (buf, " ");
2063                 strcat (buf, _("(large jitter)"));
2064         }
2065
2066         if (solid) {
2067                 have_lm_results = true;
2068                 end_latency_detection ();
2069                 lm_use_button.set_sensitive (true);
2070                 lm_results.set_markup (string_compose (results_markup, buf));
2071                 return false;
2072         } else if (mididm->processed () > 400) {
2073                 have_lm_results = false;
2074                 end_latency_detection ();
2075                 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2076                 return false;
2077         }
2078
2079         lm_results.set_markup (string_compose (results_markup, buf));
2080
2081         return true;
2082 }
2083
2084 void
2085 EngineControl::start_latency_detection ()
2086 {
2087         ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2088         ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2089
2090         if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2091                 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2092                 if (_measure_midi) {
2093                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2094                 } else {
2095                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2096                 }
2097                 lm_measure_label.set_text (_("Cancel"));
2098                 have_lm_results = false;
2099                 lm_use_button.set_sensitive (false);
2100                 lm_input_channel_combo.set_sensitive (false);
2101                 lm_output_channel_combo.set_sensitive (false);
2102                 lm_running = true;
2103         }
2104 }
2105
2106 void
2107 EngineControl::end_latency_detection ()
2108 {
2109         latency_timeout.disconnect ();
2110         ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2111         lm_measure_label.set_text (_("Measure"));
2112         if (!have_lm_results) {
2113                 lm_use_button.set_sensitive (false);
2114         }
2115         lm_input_channel_combo.set_sensitive (true);
2116         lm_output_channel_combo.set_sensitive (true);
2117         lm_running = false;
2118 }
2119
2120 void
2121 EngineControl::latency_button_clicked ()
2122 {
2123         if (!lm_running) {
2124                 start_latency_detection ();
2125         } else {
2126                 end_latency_detection ();
2127         }
2128 }
2129
2130 void
2131 EngineControl::use_latency_button_clicked ()
2132 {
2133         if (_measure_midi) {
2134                 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2135                 if (!mididm) {
2136                         return;
2137                 }
2138                 ARDOUR::framecnt_t frames_total = mididm->latency();
2139                 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2140                 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2141                 _measure_midi->input_latency = one_way;
2142                 _measure_midi->output_latency = one_way;
2143                 notebook.set_current_page (midi_tab);
2144         } else {
2145                 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2146
2147                 if (!mtdm) {
2148                         return;
2149                 }
2150
2151                 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2152                 one_way = std::max (0., one_way);
2153
2154                 input_latency_adjustment.set_value (one_way);
2155                 output_latency_adjustment.set_value (one_way);
2156
2157                 /* back to settings page */
2158                 notebook.set_current_page (0);
2159 }
2160         }
2161
2162
2163 bool
2164 EngineControl::on_delete_event (GdkEventAny* ev)
2165 {
2166         if (notebook.get_current_page() == 2) {
2167                 /* currently on latency tab - be sure to clean up */
2168                 end_latency_detection ();
2169         }
2170         return ArdourDialog::on_delete_event (ev);
2171 }
2172
2173 void
2174 EngineControl::engine_running ()
2175 {
2176         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2177         assert (backend);
2178
2179         buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2180         sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2181
2182         buffer_size_combo.set_sensitive (true);
2183         sample_rate_combo.set_sensitive (true);
2184
2185         connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2186         connect_disconnect_button.show();
2187
2188         started_at_least_once = true;
2189         engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2190 }
2191
2192 void
2193 EngineControl::engine_stopped ()
2194 {
2195         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2196         assert (backend);
2197
2198         buffer_size_combo.set_sensitive (false);
2199         connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2200         connect_disconnect_button.show();
2201
2202         sample_rate_combo.set_sensitive (true);
2203         buffer_size_combo.set_sensitive (true);
2204         engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2205 }
2206
2207 void
2208 EngineControl::device_list_changed ()
2209 {
2210         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2211         list_devices ();
2212         midi_option_changed();
2213 }
2214
2215 void
2216 EngineControl::connect_disconnect_click()
2217 {
2218         if (ARDOUR::AudioEngine::instance()->running()) {
2219                 ARDOUR_UI::instance()->disconnect_from_engine ();
2220         } else {
2221                 ARDOUR_UI::instance()->reconnect_to_engine ();
2222         }
2223 }
2224
2225 void
2226 EngineControl::calibrate_audio_latency ()
2227 {
2228         _measure_midi.reset ();
2229         have_lm_results = false;
2230         lm_use_button.set_sensitive (false);
2231         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2232         notebook.set_current_page (latency_tab);
2233 }
2234
2235 void
2236 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2237 {
2238         _measure_midi = s;
2239         have_lm_results = false;
2240         lm_use_button.set_sensitive (false);
2241         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2242         notebook.set_current_page (latency_tab);
2243 }
2244
2245 void
2246 EngineControl::configure_midi_devices ()
2247 {
2248         notebook.set_current_page (midi_tab);
2249 }