make use of measured latency values to set dialog controls, and use actual port laten...
[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
33 #include <gtkmm/stock.h>
34 #include <gtkmm/notebook.h>
35 #include <gtkmm2ext/utils.h>
36
37 #include "ardour/audio_backend.h"
38 #include "ardour/audioengine.h"
39 #include "ardour/mtdm.h"
40 #include "ardour/rc_configuration.h"
41 #include "ardour/types.h"
42
43 #include "pbd/convert.h"
44 #include "pbd/error.h"
45
46 #include "engine_dialog.h"
47 #include "gui_thread.h"
48 #include "i18n.h"
49
50 using namespace std;
51 using namespace Gtk;
52 using namespace Gtkmm2ext;
53 using namespace PBD;
54 using namespace Glib;
55
56 EngineControl::EngineControl ()
57         : ArdourDialog (_("Audio/MIDI Setup"))
58         , input_latency_adjustment (0, 0, 99999, 1)
59         , input_latency (input_latency_adjustment)
60         , output_latency_adjustment (0, 0, 99999, 1)
61         , output_latency (output_latency_adjustment)
62         , input_channels_adjustment (0, 0, 256, 1)
63         , input_channels (input_channels_adjustment)
64         , output_channels_adjustment (0, 0, 256, 1)
65         , output_channels (output_channels_adjustment)
66         , ports_adjustment (128, 8, 1024, 1, 16)
67         , ports_spinner (ports_adjustment)
68         , control_app_button (_("Launch Control App"))
69         , lm_measure_button (_("Measure latency"))
70         , lm_use_button (_("Use results"))
71         , lm_table (5, 2)
72         , basic_packer (9, 3)
73         , ignore_changes (0)
74         , _desired_sample_rate (0)
75 {
76         build_notebook ();
77
78         get_vbox()->set_border_width (12);
79         get_vbox()->pack_start (notebook);
80
81         control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
82         manage_control_app_sensitivity ();
83
84         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
85         add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
86         add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
87
88         /* Pick up any existing audio setup configuration, if appropriate */
89
90         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
91         
92         /* push a change as if we altered the backend */
93         backend_changed ();
94
95         if (audio_setup) {
96                 set_state (*audio_setup);
97         } 
98
99         ARDOUR::AudioEngine::instance()->Stopped.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::disable_latency_tab, this), gui_context());
100
101         if (!ARDOUR::AudioEngine::instance()->connected()) {
102                 ARDOUR::AudioEngine::instance()->Running.connect (*this, MISSING_INVALIDATOR, boost::bind (&EngineControl::enable_latency_tab, this), gui_context());
103                 disable_latency_tab ();
104         } else {
105                 enable_latency_tab ();
106         }
107 }
108
109 void
110 EngineControl::on_response (int response_id)
111 {
112         ArdourDialog::on_response (response_id);
113
114         switch (response_id) {
115         case RESPONSE_APPLY:
116                 push_state_to_backend (true);
117                 break;
118         case RESPONSE_OK:
119                 push_state_to_backend (true);
120                 hide ();
121                 break;
122         default:
123                 hide ();
124         }
125 }
126
127 void
128 EngineControl::build_notebook ()
129 {
130         using namespace Notebook_Helpers;
131         Label* label;
132         vector<string> strings;
133         int row = 0;
134
135         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
136         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
137                 strings.push_back ((*b)->name);
138         }
139
140         set_popdown_strings (backend_combo, strings);
141         backend_combo.set_active_text (strings.front());
142
143         basic_packer.set_spacings (6);
144         basic_packer.set_border_width (12);
145         basic_packer.set_homogeneous (true);
146
147         row = 0;
148
149         AttachOptions xopt = AttachOptions (FILL|EXPAND);
150
151         label = manage (left_aligned_label (_("Audio System:")));
152         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
153         basic_packer.attach (backend_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
154         row++;
155
156         label = manage (left_aligned_label (_("Driver:")));
157         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
158         basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
159         row++;
160
161         label = manage (left_aligned_label (_("Device:")));
162         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
163         basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
164         row++;
165
166         label = manage (left_aligned_label (_("Sample rate:")));
167         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
168         basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
169         row++;
170
171
172         label = manage (left_aligned_label (_("Buffer size:")));
173         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
174         basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
175         buffer_size_duration_label.set_alignment (0.0); /* left-align */
176         basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
177         row++;
178
179
180         label = manage (left_aligned_label (_("Input Channels:")));
181         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
182         basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
183         ++row;
184
185
186         label = manage (left_aligned_label (_("Output Channels:")));
187         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
188         basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
189         ++row;
190
191
192         label = manage (left_aligned_label (_("Hardware input latency:")));
193         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
194         basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
195         label = manage (left_aligned_label (_("samples")));
196         basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
197         ++row;
198
199         label = manage (left_aligned_label (_("Hardware output latency:")));
200         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
201         basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
202         label = manage (left_aligned_label (_("samples")));
203         basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
204         ++row;
205
206         basic_hbox.pack_start (basic_packer, false, false);
207         basic_vbox.pack_start (basic_hbox, false, false);
208
209         Gtk::HBox* hpacker = manage (new HBox);
210         hpacker->set_border_width (12);
211         hpacker->pack_start (control_app_button, false, false);
212         hpacker->show ();
213         control_app_button.show();
214         basic_vbox.pack_start (*hpacker);
215
216         midi_packer.set_border_width (12);
217
218         /* latency measurement tab */
219         
220         lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
221         
222         row = 0;
223         lm_table.set_row_spacings (12);
224
225         lm_table.attach (lm_title, 0, 2, row, row+1, xopt, (AttachOptions) 0);
226         row++;
227
228         lm_preamble.set_width_chars (60);
229         lm_preamble.set_line_wrap (true);
230         lm_preamble.set_markup (_("1. <span weight=\"bold\">Turn down the volume on your hardware to a very low level.</span>\n\n\
231 2. Connect the two channels that you select below using either a cable or (less ideally) a speaker \
232 and microphone.\n\n\
233 3. Once the channels are connected, click the \"Measure latency\" button.\n\n\
234 4. When satisfied with the results, click the \"Use results\" button."));
235
236         lm_table.attach (lm_preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
237         row++;
238
239         label = manage (new Label (_("Output channel")));
240         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
241         lm_table.attach (lm_output_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
242         ++row;
243
244         label = manage (new Label (_("Input channel")));
245         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
246         lm_table.attach (lm_input_channel_combo, 1, 2, row, row+1, xopt, (AttachOptions) 0);
247         ++row;
248
249         xopt = AttachOptions(0);
250
251         lm_measure_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::latency_button_toggled));
252         lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
253         lm_use_button.set_sensitive (false);
254                 
255         lm_table.attach (lm_measure_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
256         ++row;
257         lm_table.attach (lm_results, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
258         ++row;
259         lm_table.attach (lm_use_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
260         ++row;
261
262         lm_results.set_markup ("<i>No measurement results yet</i>");
263
264         lm_vbox.pack_start (lm_table, false, false);
265
266         /* pack it all up */
267
268         notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
269         notebook.pages().push_back (TabElem (midi_hbox, _("MIDI")));
270         notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
271         notebook.set_border_width (12);
272
273         notebook.set_tab_pos (POS_RIGHT);
274         notebook.show_all ();
275
276         notebook.set_name ("SettingsNotebook");
277
278         /* Connect to signals */
279
280         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
281         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
282         sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
283         buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
284         input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
285         output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
286         device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
287 }
288
289 EngineControl::~EngineControl ()
290 {
291
292 }
293
294 void
295 EngineControl::disable_latency_tab ()
296 {
297         vector<string> empty;
298         set_popdown_strings (lm_output_channel_combo, empty);
299         set_popdown_strings (lm_input_channel_combo, empty);
300 }
301
302 void
303 EngineControl::enable_latency_tab ()
304 {
305         vector<string> outputs;
306         ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
307         set_popdown_strings (lm_output_channel_combo, outputs);
308         lm_output_channel_combo.set_active_text (outputs.front());
309
310         vector<string> inputs;
311         ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
312         set_popdown_strings (lm_input_channel_combo, inputs);
313         lm_input_channel_combo.set_active_text (inputs.front());
314 }
315
316 void
317 EngineControl::backend_changed ()
318 {
319         if (ignore_changes) {
320                 return;
321         }
322
323         string backend_name = backend_combo.get_active_text();
324         boost::shared_ptr<ARDOUR::AudioBackend> backend;
325
326         if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
327                 /* eh? setting the backend failed... how ? */
328                 return;
329         }
330
331         if (backend->requires_driver_selection()) {
332                 vector<string> drivers = backend->enumerate_drivers();
333                 driver_combo.set_sensitive (true);
334                 set_popdown_strings (driver_combo, drivers);
335                 driver_combo.set_active_text (drivers.front());
336                 driver_changed ();
337         } else {
338                 driver_combo.set_sensitive (false);
339                 list_devices ();
340         }
341         
342         maybe_display_saved_state ();
343 }
344
345 bool
346 EngineControl::print_channel_count (Gtk::SpinButton* sb)
347 {
348         uint32_t cnt = (uint32_t) sb->get_value();
349         if (cnt == 0) {
350                 sb->set_text (_("all available channels"));
351         } else {
352                 char buf[32];
353                 snprintf (buf, sizeof (buf), "%d", cnt);
354                 sb->set_text (buf);
355         }
356         return true;
357 }
358
359 void
360 EngineControl::list_devices ()
361 {
362         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
363         assert (backend);
364
365         /* now fill out devices, mark sample rates, buffer sizes insensitive */
366             
367         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
368         
369         /* NOTE: Ardour currently does not display the "available" field of the
370          * returned devices.
371          *
372          * Doing so would require a different GUI widget than the combo
373          * box/popdown that we currently use, since it has no way to list
374          * items that are not selectable. Something more like a popup menu,
375          * which could have unselectable items, would be appropriate.
376          */
377
378         vector<string> available_devices;
379
380         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
381                 available_devices.push_back (i->name);
382         }
383
384         set_popdown_strings (device_combo, available_devices);
385         
386         if (!available_devices.empty()) {
387                 device_combo.set_active_text (available_devices.front());
388         }
389
390         device_changed ();
391 }
392         
393 void
394 EngineControl::driver_changed ()
395 {
396         if (ignore_changes) {
397                 return;
398         }
399
400         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
401         assert (backend);
402
403         backend->set_driver (driver_combo.get_active_text());
404         list_devices ();
405
406         maybe_display_saved_state ();
407 }
408
409 void
410 EngineControl::device_changed ()
411 {
412         if (ignore_changes) {
413                 return;
414         }
415
416         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
417         assert (backend);
418         string device_name = device_combo.get_active_text ();
419         vector<string> s;
420
421         /* don't allow programmatic change to sample_rate_combo to cause a
422            recursive call to this method.
423         */
424            
425         ignore_changes++;
426
427         /* sample rates */
428         
429         string desired;
430
431         vector<float> sr = backend->available_sample_rates (device_name);
432         for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
433                 char buf[32];
434                 if (fmod (*x, 1000.0f)) {
435                         snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
436                 } else {
437                         snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
438                 }
439                 s.push_back (buf);
440                 if (*x == _desired_sample_rate) {
441                         desired = buf;
442                 }
443         }
444
445         set_popdown_strings (sample_rate_combo, s);
446         if (desired.empty()) {
447                 sample_rate_combo.set_active_text (s.front());
448         } else {
449                 sample_rate_combo.set_active_text (desired);
450         }
451
452         vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
453         s.clear ();
454         for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
455                 char buf[32];
456                 /* Translators: "samples" is always plural here, so no
457                    need for plural+singular forms.
458                 */
459                 snprintf (buf, sizeof (buf), _("%u samples"), *x);
460                 s.push_back (buf);
461         }
462
463         set_popdown_strings (buffer_size_combo, s);
464         buffer_size_combo.set_active_text (s.front());
465         show_buffer_duration ();
466
467         manage_control_app_sensitivity ();
468
469         ignore_changes--;
470
471         maybe_display_saved_state ();
472 }       
473
474 void 
475 EngineControl::sample_rate_changed ()
476 {
477         if (ignore_changes) {
478                 return;
479         }
480
481         /* reset the strings for buffer size to show the correct msec value
482            (reflecting the new sample rate).
483         */
484
485         show_buffer_duration ();
486         save_state ();
487
488 }
489
490 void 
491 EngineControl::buffer_size_changed ()
492 {
493         if (ignore_changes) {
494                 return;
495         }
496
497         show_buffer_duration ();
498         save_state ();
499 }
500
501 void
502 EngineControl::show_buffer_duration ()
503 {
504
505         /* buffer sizes  - convert from just samples to samples + msecs for
506          * the displayed string
507          */
508
509         string bs_text = buffer_size_combo.get_active_text ();
510         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
511         uint32_t rate = get_rate();
512
513         /* Translators: "msecs" is ALWAYS plural here, so we do not
514            need singular form as well.
515         */
516         /* Developers: note the hard-coding of a double buffered model
517            in the (2 * samples) computation of latency. we always start
518            the audiobackend in this configuration.
519         */
520         char buf[32];
521         snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
522         buffer_size_duration_label.set_text (buf);
523 }
524
525 EngineControl::State*
526 EngineControl::get_matching_state (const string& backend,
527                                    const string& driver,
528                                    const string& device)
529 {
530         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
531                 if ((*i).backend == backend &&
532                     (*i).driver == driver &&
533                     (*i).device == device) {
534                         return &(*i);
535                 }
536         }
537         return 0;
538 }
539
540 EngineControl::State*
541 EngineControl::get_current_state ()
542 {
543         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
544
545         if (backend) {
546                 return get_matching_state (backend_combo.get_active_text(),
547                                            (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
548                                            device_combo.get_active_text());
549         }
550
551
552         return get_matching_state (backend_combo.get_active_text(),
553                                    string(),
554                                    device_combo.get_active_text());
555 }
556
557 void
558 EngineControl::save_state ()
559 {
560         bool existing = true;
561         State* state = get_current_state ();
562
563         if (!state) {
564                 existing = false;
565                 state = new State;
566         }
567         
568         state->backend = backend_combo.get_active_text ();
569         state->driver = driver_combo.get_active_text ();
570         state->device = device_combo.get_active_text ();
571         state->buffer_size = buffer_size_combo.get_active_text ();
572         state->sample_rate = sample_rate_combo.get_active_text ();
573         state->input_latency = (uint32_t) input_latency.get_value();
574         state->output_latency = (uint32_t) output_latency.get_value();
575         state->input_channels = (uint32_t) input_channels.get_value();
576         state->output_channels = (uint32_t) output_channels.get_value();
577
578         if (!existing) {
579                 states.push_back (*state);
580         }
581 }
582
583 void
584 EngineControl::maybe_display_saved_state ()
585 {
586         State* state = get_current_state ();
587
588         if (state) {
589                 ignore_changes++;
590                 if (!_desired_sample_rate) {
591                         sample_rate_combo.set_active_text (state->sample_rate);
592                 }
593                 buffer_size_combo.set_active_text (state->buffer_size);
594                 input_latency.set_value (state->input_latency);
595                 output_latency.set_value (state->output_latency);
596                 ignore_changes--;
597         }
598 }
599         
600 XMLNode&
601 EngineControl::get_state ()
602 {
603         XMLNode* root = new XMLNode ("AudioMIDISetup");
604         std::string path;
605
606         if (!states.empty()) {
607                 XMLNode* state_nodes = new XMLNode ("EngineStates");
608                 
609                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
610                         
611                         XMLNode* node = new XMLNode ("State");
612                         
613                         node->add_property ("backend", (*i).backend);
614                         node->add_property ("driver", (*i).driver);
615                         node->add_property ("device", (*i).device);
616                         node->add_property ("sample-rate", (*i).sample_rate);
617                         node->add_property ("buffer-size", (*i).buffer_size);
618                         node->add_property ("input-latency", (*i).input_latency);
619                         node->add_property ("output-latency", (*i).output_latency);
620                         node->add_property ("input-channels", (*i).input_channels);
621                         node->add_property ("output-channels", (*i).output_channels);
622                         node->add_property ("active", (*i).active ? "yes" : "no");
623                         
624                         state_nodes->add_child_nocopy (*node);
625                 }
626                 
627                 root->add_child_nocopy (*state_nodes);
628         }
629
630         return *root;
631 }
632
633 void
634 EngineControl::set_state (const XMLNode& root)
635 {
636         XMLNodeList          clist, cclist;
637         XMLNodeConstIterator citer, cciter;
638         XMLNode* child;
639         XMLNode* grandchild;
640         XMLProperty* prop = NULL;
641
642         if (root.name() != "AudioMIDISetup") {
643                 return;
644         }
645
646         clist = root.children();
647
648         states.clear ();
649
650         for (citer = clist.begin(); citer != clist.end(); ++citer) {
651
652                 child = *citer;
653                 
654                 if (child->name() != "EngineStates") {
655                         continue;
656                 }
657
658                 cclist = child->children();
659
660                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
661                         State state;
662                         
663                         grandchild = *cciter;
664
665                         if (grandchild->name() != "State") {
666                                 continue;
667                         }
668                         
669                         if ((prop = grandchild->property ("backend")) == 0) {
670                                 continue;
671                         }
672                         state.backend = prop->value ();
673                         
674                         if ((prop = grandchild->property ("driver")) == 0) {
675                                 continue;
676                         }
677                         state.driver = prop->value ();
678                         
679                         if ((prop = grandchild->property ("device")) == 0) {
680                                 continue;
681                         }
682                         state.device = prop->value ();
683                         
684                         if ((prop = grandchild->property ("sample-rate")) == 0) {
685                                 continue;
686                         }
687                         state.sample_rate = prop->value ();
688                         
689                         if ((prop = grandchild->property ("buffer-size")) == 0) {
690                                 continue;
691                         }
692                         state.buffer_size = prop->value ();
693                         
694                         if ((prop = grandchild->property ("input-latency")) == 0) {
695                                 continue;
696                         }
697                         state.input_latency = atoi (prop->value ());
698                         
699                         if ((prop = grandchild->property ("output-latency")) == 0) {
700                                 continue;
701                         }
702                         state.output_latency = atoi (prop->value ());
703                         
704                         if ((prop = grandchild->property ("input-channels")) == 0) {
705                                 continue;
706                         }
707                         state.input_channels = atoi (prop->value ());
708                         
709                         if ((prop = grandchild->property ("output-channels")) == 0) {
710                                 continue;
711                         }
712                         state.output_channels = atoi (prop->value ());
713
714                         if ((prop = grandchild->property ("active")) == 0) {
715                                 continue;
716                         }
717                         state.active = string_is_affirmative (prop->value ());
718                         
719                         states.push_back (state);
720                 }
721         }
722
723         /* now see if there was an active state and switch the setup to it */
724         
725         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
726                 if ((*i).active) {
727                         ignore_changes++;
728                         backend_combo.set_active_text ((*i).backend);
729                         driver_combo.set_active_text ((*i).driver);
730                         device_combo.set_active_text ((*i).device);
731                         sample_rate_combo.set_active_text ((*i).sample_rate);
732                         buffer_size_combo.set_active_text ((*i).buffer_size);
733                         input_latency.set_value ((*i).input_latency);
734                         output_latency.set_value ((*i).output_latency);
735                         ignore_changes--;
736
737                         push_state_to_backend (false);
738                         break;
739                 }
740         }
741 }
742
743
744 int
745 EngineControl::push_state_to_backend (bool start)
746 {
747         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
748
749         if (!backend) {
750                 return 0;
751          }
752
753         /* grab the parameters from the GUI and apply them */
754
755         try {
756                 if (backend->requires_driver_selection()) {
757                         if (backend->set_driver (get_driver())) {
758                                 return -1;
759                         }
760                 }
761
762                 if (backend->set_device_name (get_device_name())) {
763                         return -1;
764                 }
765
766                 if (backend->set_sample_rate (get_rate())) {
767                         error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
768                         return -1;
769                 }
770                 if (backend->set_buffer_size (get_buffer_size())) {
771                         error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
772                         return -1;
773                 }
774                 if (backend->set_input_channels (get_input_channels())) {
775                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
776                         return -1;
777                 }
778                 if (backend->set_output_channels (get_output_channels())) {
779                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
780                         return -1;
781                 }
782                 if (backend->set_systemic_input_latency (get_input_latency())) {
783                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
784                         return -1;
785                 }
786                 if (backend->set_systemic_output_latency (get_output_latency())) {
787                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
788                         return -1;
789                 }
790
791                 /* get a pointer to the current state object, creating one if
792                  * necessary
793                  */
794
795                 State* state = get_current_state ();
796
797                 if (!state) {
798                         save_state ();
799                         state = get_current_state ();
800                         assert (state);
801                 }
802
803                 /* all off */
804
805                 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
806                         (*i).active = false;
807                 }
808
809                 /* mark this one active (to be used next time the dialog is
810                  * shown)
811                  */
812
813                 state->active = true;
814                 
815                 if (start) {
816                         if (ARDOUR::AudioEngine::instance()->start()) {
817                                 return -1;
818                         }
819                 }
820
821                 manage_control_app_sensitivity ();
822                 return 0;
823
824         } catch (...) {
825                 cerr << "exception thrown...\n";
826                 return -1;
827         }
828 }
829
830 uint32_t
831 EngineControl::get_rate () const
832 {
833         double r = atof (sample_rate_combo.get_active_text ());
834         /* the string may have been translated with an abbreviation for
835          * thousands, so use a crude heuristic to fix this.
836          */
837         if (r < 1000.0) {
838                 r *= 1000.0;
839         }
840         return lrint (r);
841 }
842
843 uint32_t
844 EngineControl::get_buffer_size () const
845 {
846         string txt = buffer_size_combo.get_active_text ();
847         uint32_t samples;
848
849         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
850                 throw exception ();
851         }
852
853         return samples;
854 }
855
856 uint32_t
857 EngineControl::get_input_channels() const
858 {
859         return (uint32_t) input_channels_adjustment.get_value();
860 }
861
862 uint32_t
863 EngineControl::get_output_channels() const
864 {
865         return (uint32_t) output_channels_adjustment.get_value();
866 }
867
868 uint32_t
869 EngineControl::get_input_latency() const
870 {
871         return (uint32_t) input_latency_adjustment.get_value();
872 }
873
874 uint32_t
875 EngineControl::get_output_latency() const
876 {
877         return (uint32_t) output_latency_adjustment.get_value();
878 }
879
880 string
881 EngineControl::get_driver () const
882 {
883         return driver_combo.get_active_text ();
884 }
885
886 string
887 EngineControl::get_device_name () const
888 {
889         return device_combo.get_active_text ();
890 }
891
892 void
893 EngineControl::control_app_button_clicked ()
894 {
895         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
896         
897         if (!backend) {
898                 return;
899         }
900         
901         backend->launch_control_app ();
902 }
903
904 void
905 EngineControl::manage_control_app_sensitivity ()
906 {
907         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
908         
909         if (!backend) {
910                 return;
911         }
912         
913         string appname = backend->control_app_name();
914
915         if (appname.empty()) {
916                 control_app_button.set_sensitive (false);
917         } else {
918                 control_app_button.set_sensitive (true);
919         }
920 }
921
922 void
923 EngineControl::set_desired_sample_rate (uint32_t sr)
924 {
925         _desired_sample_rate = sr;
926         device_changed ();
927 }
928
929 /* latency measurement */
930
931 void
932 EngineControl::update_latency_display ()
933 {
934         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
935         if (sample_rate == 0) {
936                 lm_results.set_text (_("Disconnected from audio engine"));
937         } else {
938                 char buf[64];
939                 //snprintf (buf, sizeof (buf), "%10.3lf frames %10.3lf ms",
940                 //(float)_pi->latency(), (float)_pi->latency() * 1000.0f/sample_rate);
941                 strcpy (buf, "got something");
942                 lm_results.set_text(buf);
943         }
944 }
945
946 bool
947 EngineControl::check_latency_measurement ()
948 {
949         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
950         static uint32_t cnt = 0;
951
952         if (mtdm->resolve () < 0) {
953                 string txt = _("No signal detected ");
954                 uint32_t dots = cnt++%10;
955                 for (uint32_t i = 0; i < dots; ++i) {
956                         txt += '.';
957                 }
958                 lm_results.set_text (txt);
959                 return true;
960         }
961
962         if (mtdm->err () > 0.3) {
963                 mtdm->invert ();
964                 mtdm->resolve ();
965         }
966
967         char buf[128];
968         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
969
970         if (sample_rate == 0) {
971                 lm_results.set_text (_("Disconnected from audio engine"));
972                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
973                 return false;
974         }
975
976         uint32_t frames_total = mtdm->del();
977         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
978
979         snprintf (buf, sizeof (buf), "%u samples %10.3lf ms", extra, extra * 1000.0f/sample_rate);
980
981         bool solid = true;
982
983         if (mtdm->err () > 0.2) {
984                 strcat (buf, " ??");
985                 solid = false;
986         }
987
988         if (mtdm->inv ()) {
989                 strcat (buf, " (Inv)");
990                 solid = false;
991         }
992
993         if (solid) {
994                 // _pi->set_measured_latency (rint (mtdm->del()));
995                 lm_measure_button.set_active (false);
996                 lm_use_button.set_sensitive (true);
997                 strcat (buf, " (set)");
998         }
999         
1000         lm_results.set_text (buf);
1001
1002         return true;
1003 }
1004
1005 void
1006 EngineControl::latency_button_toggled ()
1007 {
1008         if (lm_measure_button.get_active ()) {
1009
1010                 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1011                 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1012                 cerr << "latency detection on " << lm_input_channel_combo.get_active_text() << " => " << lm_output_channel_combo.get_active_text() << endl;
1013                 ARDOUR::AudioEngine::instance()->start_latency_detection ();
1014                 lm_results.set_text (_("Detecting ..."));
1015                 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
1016
1017         } else {
1018                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1019                 latency_timeout.disconnect ();
1020                 update_latency_display ();
1021         }
1022 }
1023
1024 void
1025 EngineControl::use_latency_button_clicked ()
1026 {
1027         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1028
1029         if (!mtdm) {
1030                 return;
1031         }
1032
1033         uint32_t frames_total = mtdm->del();
1034         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1035         uint32_t one_way = extra/2;
1036
1037         input_latency_adjustment.set_value (one_way);
1038         output_latency_adjustment.set_value (one_way);
1039 }