923024bdc2c853b0e2fcee211534150a289a007b
[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_saved_state_for_currently_displayed_backend_and_device ()
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 EngineControl::State*
558 EngineControl::save_state ()
559 {
560         bool existing = true;
561         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
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         return state;
583 }
584
585 void
586 EngineControl::maybe_display_saved_state ()
587 {
588         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
589
590         if (state) {
591                 ignore_changes++;
592                 if (!_desired_sample_rate) {
593                         sample_rate_combo.set_active_text (state->sample_rate);
594                 }
595                 buffer_size_combo.set_active_text (state->buffer_size);
596                 input_latency.set_value (state->input_latency);
597                 output_latency.set_value (state->output_latency);
598                 ignore_changes--;
599         }
600 }
601         
602 XMLNode&
603 EngineControl::get_state ()
604 {
605         XMLNode* root = new XMLNode ("AudioMIDISetup");
606         std::string path;
607
608         if (!states.empty()) {
609                 XMLNode* state_nodes = new XMLNode ("EngineStates");
610                 
611                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
612                         
613                         XMLNode* node = new XMLNode ("State");
614                         
615                         node->add_property ("backend", (*i).backend);
616                         node->add_property ("driver", (*i).driver);
617                         node->add_property ("device", (*i).device);
618                         node->add_property ("sample-rate", (*i).sample_rate);
619                         node->add_property ("buffer-size", (*i).buffer_size);
620                         node->add_property ("input-latency", (*i).input_latency);
621                         node->add_property ("output-latency", (*i).output_latency);
622                         node->add_property ("input-channels", (*i).input_channels);
623                         node->add_property ("output-channels", (*i).output_channels);
624                         node->add_property ("active", (*i).active ? "yes" : "no");
625                         
626                         state_nodes->add_child_nocopy (*node);
627                 }
628                 
629                 root->add_child_nocopy (*state_nodes);
630         }
631
632         return *root;
633 }
634
635 void
636 EngineControl::set_state (const XMLNode& root)
637 {
638         XMLNodeList          clist, cclist;
639         XMLNodeConstIterator citer, cciter;
640         XMLNode* child;
641         XMLNode* grandchild;
642         XMLProperty* prop = NULL;
643
644         if (root.name() != "AudioMIDISetup") {
645                 return;
646         }
647
648         clist = root.children();
649
650         states.clear ();
651
652         for (citer = clist.begin(); citer != clist.end(); ++citer) {
653
654                 child = *citer;
655                 
656                 if (child->name() != "EngineStates") {
657                         continue;
658                 }
659
660                 cclist = child->children();
661
662                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
663                         State state;
664                         
665                         grandchild = *cciter;
666
667                         if (grandchild->name() != "State") {
668                                 continue;
669                         }
670                         
671                         if ((prop = grandchild->property ("backend")) == 0) {
672                                 continue;
673                         }
674                         state.backend = prop->value ();
675                         
676                         if ((prop = grandchild->property ("driver")) == 0) {
677                                 continue;
678                         }
679                         state.driver = prop->value ();
680                         
681                         if ((prop = grandchild->property ("device")) == 0) {
682                                 continue;
683                         }
684                         state.device = prop->value ();
685                         
686                         if ((prop = grandchild->property ("sample-rate")) == 0) {
687                                 continue;
688                         }
689                         state.sample_rate = prop->value ();
690                         
691                         if ((prop = grandchild->property ("buffer-size")) == 0) {
692                                 continue;
693                         }
694                         state.buffer_size = prop->value ();
695                         
696                         if ((prop = grandchild->property ("input-latency")) == 0) {
697                                 continue;
698                         }
699                         state.input_latency = atoi (prop->value ());
700                         
701                         if ((prop = grandchild->property ("output-latency")) == 0) {
702                                 continue;
703                         }
704                         state.output_latency = atoi (prop->value ());
705                         
706                         if ((prop = grandchild->property ("input-channels")) == 0) {
707                                 continue;
708                         }
709                         state.input_channels = atoi (prop->value ());
710                         
711                         if ((prop = grandchild->property ("output-channels")) == 0) {
712                                 continue;
713                         }
714                         state.output_channels = atoi (prop->value ());
715
716                         if ((prop = grandchild->property ("active")) == 0) {
717                                 continue;
718                         }
719                         state.active = string_is_affirmative (prop->value ());
720                         
721                         states.push_back (state);
722                 }
723         }
724
725         /* now see if there was an active state and switch the setup to it */
726         
727         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
728                 if ((*i).active) {
729                         ignore_changes++;
730                         backend_combo.set_active_text ((*i).backend);
731                         driver_combo.set_active_text ((*i).driver);
732                         device_combo.set_active_text ((*i).device);
733                         sample_rate_combo.set_active_text ((*i).sample_rate);
734                         buffer_size_combo.set_active_text ((*i).buffer_size);
735                         input_latency.set_value ((*i).input_latency);
736                         output_latency.set_value ((*i).output_latency);
737                         ignore_changes--;
738
739                         push_state_to_backend (false);
740                         break;
741                 }
742         }
743 }
744
745
746 int
747 EngineControl::push_state_to_backend (bool start)
748 {
749         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
750
751         if (!backend) {
752                 return 0;
753          }
754
755         /* grab the parameters from the GUI and apply them */
756
757         try {
758                 if (backend->requires_driver_selection()) {
759                         if (backend->set_driver (get_driver())) {
760                                 return -1;
761                         }
762                 }
763
764                 if (backend->set_device_name (get_device_name())) {
765                         return -1;
766                 }
767
768                 if (backend->set_sample_rate (get_rate())) {
769                         error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
770                         return -1;
771                 }
772                 if (backend->set_buffer_size (get_buffer_size())) {
773                         error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
774                         return -1;
775                 }
776                 if (backend->set_input_channels (get_input_channels())) {
777                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
778                         return -1;
779                 }
780                 if (backend->set_output_channels (get_output_channels())) {
781                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
782                         return -1;
783                 }
784                 if (backend->set_systemic_input_latency (get_input_latency())) {
785                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
786                         return -1;
787                 }
788                 if (backend->set_systemic_output_latency (get_output_latency())) {
789                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
790                         return -1;
791                 }
792
793                 /* get a pointer to the current state object, creating one if
794                  * necessary
795                  */
796
797                 State* state = get_saved_state_for_currently_displayed_backend_and_device ();
798
799                 if (!state) {
800                         state = save_state ();
801                         assert (state);
802                 }
803
804                 /* all off */
805
806                 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
807                         (*i).active = false;
808                 }
809
810                 /* mark this one active (to be used next time the dialog is
811                  * shown)
812                  */
813
814                 state->active = true;
815                 
816                 if (start) {
817                         if (ARDOUR::AudioEngine::instance()->start()) {
818                                 return -1;
819                         }
820                 }
821
822                 manage_control_app_sensitivity ();
823                 return 0;
824
825         } catch (...) {
826                 cerr << "exception thrown...\n";
827                 return -1;
828         }
829 }
830
831 uint32_t
832 EngineControl::get_rate () const
833 {
834         double r = atof (sample_rate_combo.get_active_text ());
835         /* the string may have been translated with an abbreviation for
836          * thousands, so use a crude heuristic to fix this.
837          */
838         if (r < 1000.0) {
839                 r *= 1000.0;
840         }
841         return lrint (r);
842 }
843
844 uint32_t
845 EngineControl::get_buffer_size () const
846 {
847         string txt = buffer_size_combo.get_active_text ();
848         uint32_t samples;
849
850         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
851                 throw exception ();
852         }
853
854         return samples;
855 }
856
857 uint32_t
858 EngineControl::get_input_channels() const
859 {
860         return (uint32_t) input_channels_adjustment.get_value();
861 }
862
863 uint32_t
864 EngineControl::get_output_channels() const
865 {
866         return (uint32_t) output_channels_adjustment.get_value();
867 }
868
869 uint32_t
870 EngineControl::get_input_latency() const
871 {
872         return (uint32_t) input_latency_adjustment.get_value();
873 }
874
875 uint32_t
876 EngineControl::get_output_latency() const
877 {
878         return (uint32_t) output_latency_adjustment.get_value();
879 }
880
881 string
882 EngineControl::get_driver () const
883 {
884         return driver_combo.get_active_text ();
885 }
886
887 string
888 EngineControl::get_device_name () const
889 {
890         return device_combo.get_active_text ();
891 }
892
893 void
894 EngineControl::control_app_button_clicked ()
895 {
896         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
897         
898         if (!backend) {
899                 return;
900         }
901         
902         backend->launch_control_app ();
903 }
904
905 void
906 EngineControl::manage_control_app_sensitivity ()
907 {
908         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
909         
910         if (!backend) {
911                 return;
912         }
913         
914         string appname = backend->control_app_name();
915
916         if (appname.empty()) {
917                 control_app_button.set_sensitive (false);
918         } else {
919                 control_app_button.set_sensitive (true);
920         }
921 }
922
923 void
924 EngineControl::set_desired_sample_rate (uint32_t sr)
925 {
926         _desired_sample_rate = sr;
927         device_changed ();
928 }
929
930 /* latency measurement */
931
932 void
933 EngineControl::update_latency_display ()
934 {
935         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
936         if (sample_rate == 0) {
937                 lm_results.set_text (_("Disconnected from audio engine"));
938         } else {
939                 char buf[64];
940                 //snprintf (buf, sizeof (buf), "%10.3lf frames %10.3lf ms",
941                 //(float)_pi->latency(), (float)_pi->latency() * 1000.0f/sample_rate);
942                 strcpy (buf, "got something");
943                 lm_results.set_text(buf);
944         }
945 }
946
947 bool
948 EngineControl::check_latency_measurement ()
949 {
950         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
951         static uint32_t cnt = 0;
952
953         if (mtdm->resolve () < 0) {
954                 string txt = _("No signal detected ");
955                 uint32_t dots = cnt++%10;
956                 for (uint32_t i = 0; i < dots; ++i) {
957                         txt += '.';
958                 }
959                 lm_results.set_text (txt);
960                 return true;
961         }
962
963         if (mtdm->err () > 0.3) {
964                 mtdm->invert ();
965                 mtdm->resolve ();
966         }
967
968         char buf[128];
969         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
970
971         if (sample_rate == 0) {
972                 lm_results.set_text (_("Disconnected from audio engine"));
973                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
974                 return false;
975         }
976
977         uint32_t frames_total = mtdm->del();
978         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
979
980         snprintf (buf, sizeof (buf), "%u samples %10.3lf ms", extra, extra * 1000.0f/sample_rate);
981
982         bool solid = true;
983
984         if (mtdm->err () > 0.2) {
985                 strcat (buf, " ??");
986                 solid = false;
987         }
988
989         if (mtdm->inv ()) {
990                 strcat (buf, " (Inv)");
991                 solid = false;
992         }
993
994         if (solid) {
995                 // _pi->set_measured_latency (rint (mtdm->del()));
996                 lm_measure_button.set_active (false);
997                 lm_use_button.set_sensitive (true);
998                 strcat (buf, " (set)");
999         }
1000         
1001         lm_results.set_text (buf);
1002
1003         return true;
1004 }
1005
1006 void
1007 EngineControl::latency_button_toggled ()
1008 {
1009         if (lm_measure_button.get_active ()) {
1010
1011                 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1012                 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1013                 cerr << "latency detection on " << lm_input_channel_combo.get_active_text() << " => " << lm_output_channel_combo.get_active_text() << endl;
1014                 ARDOUR::AudioEngine::instance()->start_latency_detection ();
1015                 lm_results.set_text (_("Detecting ..."));
1016                 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
1017
1018         } else {
1019                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1020                 latency_timeout.disconnect ();
1021                 update_latency_display ();
1022         }
1023 }
1024
1025 void
1026 EngineControl::use_latency_button_clicked ()
1027 {
1028         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1029
1030         if (!mtdm) {
1031                 return;
1032         }
1033
1034         uint32_t frames_total = mtdm->del();
1035         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1036         uint32_t one_way = extra/2;
1037
1038         input_latency_adjustment.set_value (one_way);
1039         output_latency_adjustment.set_value (one_way);
1040 }