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