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