merge with master, fixing conflicts in 3 wscript files
[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/alignment.h>
35 #include <gtkmm/stock.h>
36 #include <gtkmm/notebook.h>
37 #include <gtkmm2ext/utils.h>
38
39 #include "ardour/audio_backend.h"
40 #include "ardour/audioengine.h"
41 #include "ardour/mtdm.h"
42 #include "ardour/rc_configuration.h"
43 #include "ardour/types.h"
44
45 #include "pbd/convert.h"
46 #include "pbd/error.h"
47
48 #include "ardour_ui.h"
49 #include "engine_dialog.h"
50 #include "gui_thread.h"
51 #include "utils.h"
52 #include "i18n.h"
53
54 using namespace std;
55 using namespace Gtk;
56 using namespace Gtkmm2ext;
57 using namespace PBD;
58 using namespace Glib;
59
60 EngineControl::EngineControl ()
61         : ArdourDialog (_("Audio/MIDI Setup"))
62         , basic_packer (9, 3)
63         , input_latency_adjustment (0, 0, 99999, 1)
64         , input_latency (input_latency_adjustment)
65         , output_latency_adjustment (0, 0, 99999, 1)
66         , output_latency (output_latency_adjustment)
67         , input_channels_adjustment (0, 0, 256, 1)
68         , input_channels (input_channels_adjustment)
69         , output_channels_adjustment (0, 0, 256, 1)
70         , output_channels (output_channels_adjustment)
71         , ports_adjustment (128, 8, 1024, 1, 16)
72         , ports_spinner (ports_adjustment)
73         , control_app_button (_("Device Control Panel"))
74         , lm_start_stop_label (_("Measure latency"))
75         , lm_use_button (_("Use results"))
76         , lm_table (5, 2)
77         , have_lm_results (false)
78         , midi_refresh_button (_("Refresh list"))
79         , aj_button (_("Start MIDI ALSA/JACK bridge"))
80         , ignore_changes (0)
81         , _desired_sample_rate (0)
82         , no_push (true)
83         , started_at_least_once (false)
84 {
85         using namespace Notebook_Helpers;
86         vector<string> strings;
87         Label* label;
88         AttachOptions xopt = AttachOptions (FILL|EXPAND);
89         int row;
90
91         set_name (X_("AudioMIDISetup"));
92
93         /* the backend combo is the one thing that is ALWAYS visible 
94          */
95
96         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
97         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
98                 strings.push_back ((*b)->name);
99         }
100
101         set_popdown_strings (backend_combo, strings);
102         backend_combo.set_active_text (strings.front());
103         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
104
105         /* setup basic packing characteristics for the table used on the main
106          * tab of the notebook
107          */
108
109         basic_packer.set_spacings (6);
110         basic_packer.set_border_width (12);
111         basic_packer.set_homogeneous (true);
112
113         /* pack it in */
114
115         basic_hbox.pack_start (basic_packer, false, false);
116
117         /* latency tab */
118
119         /* latency measurement tab */
120         
121         lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
122         
123         row = 0;
124         lm_table.set_row_spacings (12);
125
126         lm_table.attach (lm_title, 0, 2, row, row+1, xopt, (AttachOptions) 0);
127         row++;
128
129         Gtk::Label* preamble;
130
131         preamble = manage (new Label);
132         preamble->set_width_chars (60);
133         preamble->set_line_wrap (true);
134         preamble->set_markup (_("<span weight=\"bold\">Turn down the volume on your hardware to a very low level.</span>"));
135
136         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
137         row++;
138
139         preamble = manage (new Label);
140         preamble->set_width_chars (60);
141         preamble->set_line_wrap (true);
142         preamble->set_markup (_("Select two channels below and connect them using a cable or (less ideally) a speaker and microphone."));
143
144         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
145         row++;
146
147         label = manage (new Label (_("Output channel")));
148         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
149
150         Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
151         misc_align->add (lm_output_channel_combo);
152         lm_table.attach (*misc_align, 1, 2, row, row+1, xopt, (AttachOptions) 0);
153         ++row;
154
155         label = manage (new Label (_("Input channel")));
156         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
157
158         misc_align = manage (new Alignment (0.0, 0.5));
159         misc_align->add (lm_input_channel_combo);
160         lm_table.attach (*misc_align, 1, 2, row, row+1, FILL, (AttachOptions) 0);
161         ++row;
162
163         xopt = AttachOptions(0);
164
165         lm_measure_button.add (lm_start_stop_label);
166         
167         lm_measure_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::latency_button_toggled));
168         lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
169         lm_use_button.set_sensitive (false);
170
171         preamble = manage (new Label);
172         preamble->set_width_chars (60);
173         preamble->set_line_wrap (true);
174         preamble->set_markup (_("Once the channels are connected, click the \"Measure latency\" button."));
175         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
176         row++;
177
178         lm_table.attach (lm_measure_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
179         ++row;
180         lm_table.attach (lm_results, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
181         ++row;
182
183
184         preamble = manage (new Label);
185         preamble->set_width_chars (60);
186         preamble->set_line_wrap (true);
187         preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
188         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
189         row++;
190
191         lm_table.attach (lm_use_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
192         ++row;
193
194         lm_results.set_markup ("<i>No measurement results yet</i>");
195
196         lm_vbox.set_border_width (12);
197         lm_vbox.pack_start (lm_table, false, false);
198
199         /* pack it all up */
200
201         notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
202         notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
203         notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
204         notebook.set_border_width (12);
205
206         notebook.set_tab_pos (POS_RIGHT);
207         notebook.show_all ();
208
209         notebook.set_name ("SettingsNotebook");
210
211         /* packup the notebook */
212
213         get_vbox()->set_border_width (12);
214         get_vbox()->pack_start (notebook);
215
216         /* need a special function to print "all available channels" when the
217          * channel counts hit zero.
218          */
219
220         input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
221         output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
222
223         control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
224         manage_control_app_sensitivity ();
225
226         cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
227         ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
228         apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
229
230         /* Pick up any existing audio setup configuration, if appropriate */
231
232         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
233         
234         ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
235         ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
236         ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
237
238         backend_changed ();
239
240         if (audio_setup) {
241                 set_state (*audio_setup);
242         }
243
244         /* Connect to signals */
245
246         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
247         sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
248         buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
249         device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
250
251         input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
252         output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
253         input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
254         output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
255
256         notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
257
258         no_push = false;
259 }
260
261 void
262 EngineControl::on_response (int response_id)
263 {
264         ArdourDialog::on_response (response_id);
265
266         switch (response_id) {
267         case RESPONSE_APPLY:
268                 push_state_to_backend (true);
269                 break;
270         case RESPONSE_OK:
271                 push_state_to_backend (true);
272                 hide ();
273                 break;
274         case RESPONSE_DELETE_EVENT: {
275                 GdkEventButton ev;
276                 ev.type = GDK_BUTTON_PRESS;
277                 ev.button = 1;
278                 on_delete_event ((GdkEventAny*) &ev);
279                 break;
280         }
281         default:
282                 hide ();
283         }
284 }
285
286 void
287 EngineControl::build_notebook ()
288 {
289         Label* label;
290         AttachOptions xopt = AttachOptions (FILL|EXPAND);
291
292         /* clear the table */
293
294         Gtkmm2ext::container_clear (basic_vbox);
295         Gtkmm2ext::container_clear (basic_packer);
296
297         label = manage (left_aligned_label (_("Audio System:")));
298         basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
299         basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
300         
301         if (_have_control) {
302                 build_full_control_notebook ();
303         } else {
304                 build_no_control_notebook ();
305         }
306
307         basic_vbox.pack_start (basic_hbox, false, false);
308
309         if (_have_control) {
310                 Gtk::HBox* hpacker = manage (new HBox);
311                 hpacker->set_border_width (12);
312                 hpacker->pack_start (control_app_button, false, false);
313                 hpacker->show ();
314                 control_app_button.show();
315                 basic_vbox.pack_start (*hpacker);
316         }
317
318         basic_vbox.show_all ();
319 }
320
321 void
322 EngineControl::build_full_control_notebook ()
323 {
324         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
325         assert (backend);
326
327         using namespace Notebook_Helpers;
328         Label* label;
329         vector<string> strings;
330         AttachOptions xopt = AttachOptions (FILL|EXPAND);
331         int row = 1; // row zero == backend combo
332
333         /* start packing it up */
334
335         if (backend->requires_driver_selection()) {
336                 label = manage (left_aligned_label (_("Driver:")));
337                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
338                 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
339                 row++;
340         }
341
342         label = manage (left_aligned_label (_("Device:")));
343         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
344         basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
345         row++;
346
347         label = manage (left_aligned_label (_("Sample rate:")));
348         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
349         basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
350         row++;
351
352
353         label = manage (left_aligned_label (_("Buffer size:")));
354         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
355         basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
356         buffer_size_duration_label.set_alignment (0.0); /* left-align */
357         basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
358         row++;
359
360         input_channels.set_name ("InputChannels");
361         input_channels.set_flags(Gtk::CAN_FOCUS);
362         input_channels.set_digits(0);
363         input_channels.set_wrap(false);
364         output_channels.set_editable (true);
365
366         label = manage (left_aligned_label (_("Input Channels:")));
367         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
368         basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
369         ++row;
370
371         output_channels.set_name ("OutputChannels");
372         output_channels.set_flags(Gtk::CAN_FOCUS);
373         output_channels.set_digits(0);
374         output_channels.set_wrap(false);
375         output_channels.set_editable (true);
376
377         label = manage (left_aligned_label (_("Output Channels:")));
378         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
379         basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
380         ++row;
381
382         input_latency.set_name ("InputLatency");
383         input_latency.set_flags(Gtk::CAN_FOCUS);
384         input_latency.set_digits(0);
385         input_latency.set_wrap(false);
386         input_latency.set_editable (true);
387
388         label = manage (left_aligned_label (_("Hardware input latency:")));
389         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
390         basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
391         label = manage (left_aligned_label (_("samples")));
392         basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
393         ++row;
394
395         output_latency.set_name ("OutputLatency");
396         output_latency.set_flags(Gtk::CAN_FOCUS);
397         output_latency.set_digits(0);
398         output_latency.set_wrap(false);
399         output_latency.set_editable (true);
400
401         label = manage (left_aligned_label (_("Hardware output latency:")));
402         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
403         basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
404         label = manage (left_aligned_label (_("samples")));
405         basic_packer.attach (*label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
406         ++row;
407
408 }
409
410 void
411 EngineControl::build_no_control_notebook ()
412 {
413         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
414         assert (backend);
415
416         using namespace Notebook_Helpers;
417         Label* label;
418         vector<string> strings;
419         AttachOptions xopt = AttachOptions (FILL|EXPAND);
420         int row = 1; // row zero == backend combo
421         const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
422
423         label = manage (new Label);
424         label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
425         basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
426         row++;
427
428         if (backend->can_change_sample_rate_when_running()) {
429                 label = manage (left_aligned_label (_("Sample rate:")));
430                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
431                 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
432                 row++;
433         }
434
435         if (backend->can_change_buffer_size_when_running()) {
436                 label = manage (left_aligned_label (_("Buffer size:")));
437                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
438                 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
439                 buffer_size_duration_label.set_alignment (0.0); /* left-align */
440                 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
441                 row++;
442         }
443
444         connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
445
446         basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
447         row++;
448 }
449
450 EngineControl::~EngineControl ()
451 {
452
453 }
454
455 void
456 EngineControl::disable_latency_tab ()
457 {
458         vector<string> empty;
459         set_popdown_strings (lm_output_channel_combo, empty);
460         set_popdown_strings (lm_input_channel_combo, empty);
461         lm_measure_button.set_sensitive (false);
462         lm_use_button.set_sensitive (false);
463 }
464
465 void
466 EngineControl::enable_latency_tab ()
467 {
468         vector<string> outputs;
469         ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
470         set_popdown_strings (lm_output_channel_combo, outputs);
471         lm_output_channel_combo.set_active_text (outputs.front());
472
473         vector<string> inputs;
474         ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
475         set_popdown_strings (lm_input_channel_combo, inputs);
476         lm_input_channel_combo.set_active_text (inputs.front());
477
478         lm_measure_button.set_sensitive (true);
479 }
480
481 void
482 EngineControl::setup_midi_tab_for_backend ()
483 {
484         string backend = backend_combo.get_active_text ();
485
486         Gtkmm2ext::container_clear (midi_vbox);
487
488         midi_vbox.set_border_width (12);
489         midi_device_table.set_border_width (12);
490
491         if (backend == "JACK") {
492                 setup_midi_tab_for_jack ();
493         }
494
495         midi_vbox.pack_start (midi_device_table, true, true);
496         midi_vbox.pack_start (midi_refresh_button, false, false);
497         midi_vbox.show_all ();
498
499         midi_refresh_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::refresh_midi_display));
500 }
501
502 void
503 EngineControl::setup_midi_tab_for_jack ()
504 {
505         midi_vbox.pack_start (aj_button, false, false);
506 }       
507
508 void
509 EngineControl::refresh_midi_display ()
510 {
511         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
512         assert (backend);
513
514         vector<string> midi_inputs;
515         vector<string> midi_outputs;
516         int row  = 0;
517         AttachOptions xopt = AttachOptions (FILL|EXPAND);
518         Gtk::Label* l;
519
520         Gtkmm2ext::container_clear (midi_device_table);
521
522         backend->get_physical_inputs (ARDOUR::DataType::MIDI, midi_inputs);
523         backend->get_physical_outputs (ARDOUR::DataType::MIDI, midi_outputs);
524
525         midi_device_table.set_spacings (6);
526         midi_device_table.set_homogeneous (true);
527         midi_device_table.resize (midi_inputs.size() + midi_outputs.size() + 3, 1);
528
529         l = manage (new Label);
530         l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Inputs")));
531         midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
532         l->set_alignment (0, 0.5);
533         row++;
534         l->show ();
535         
536         for (vector<string>::iterator p = midi_inputs.begin(); p != midi_inputs.end(); ++p) {
537                 l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
538                 l->set_alignment (0, 0.5);
539                 midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
540                 l->show ();
541                 row++;
542         }
543
544         row++; // extra row of spacing
545
546         l = manage (new Label);
547         l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Outputs")));
548         midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
549         l->set_alignment (0, 0.5);
550         row++;
551         l->show ();
552
553         for (vector<string>::iterator p = midi_outputs.begin(); p != midi_outputs.end(); ++p) {
554                 l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
555                 l->set_alignment (0, 0.5);
556                 midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
557                 l->show ();
558                 row++;
559         }
560 }
561
562 void
563 EngineControl::update_sensitivity ()
564 {
565 }
566
567 void
568 EngineControl::backend_changed ()
569 {
570         if (ignore_changes) {
571                 return;
572         }
573
574         string backend_name = backend_combo.get_active_text();
575         boost::shared_ptr<ARDOUR::AudioBackend> backend;
576
577         if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
578                 /* eh? setting the backend failed... how ? */
579                 return;
580         }
581
582         _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
583
584         build_notebook ();
585         setup_midi_tab_for_backend ();
586
587         if (backend->requires_driver_selection()) {
588                 vector<string> drivers = backend->enumerate_drivers();
589                 
590                 if (!drivers.empty()) {
591                         {
592                                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
593                                 set_popdown_strings (driver_combo, drivers);
594                                 driver_combo.set_active_text (drivers.front());
595                         }
596
597                         driver_changed ();
598                 }
599                 
600         } else {
601                 driver_combo.set_sensitive (false);
602                 /* this will change the device text which will cause a call to
603                  * device changed which will set up parameters
604                  */
605                 list_devices ();
606         }
607         
608         maybe_display_saved_state ();
609 }
610
611 bool
612 EngineControl::print_channel_count (Gtk::SpinButton* sb)
613 {
614         uint32_t cnt = (uint32_t) sb->get_value();
615         if (cnt == 0) {
616                 sb->set_text (_("all available channels"));
617         } else {
618                 char buf[32];
619                 snprintf (buf, sizeof (buf), "%d", cnt);
620                 sb->set_text (buf);
621         }
622         return true;
623 }
624
625 void
626 EngineControl::list_devices ()
627 {
628         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
629         assert (backend);
630
631         /* now fill out devices, mark sample rates, buffer sizes insensitive */
632             
633         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
634         
635         /* NOTE: Ardour currently does not display the "available" field of the
636          * returned devices.
637          *
638          * Doing so would require a different GUI widget than the combo
639          * box/popdown that we currently use, since it has no way to list
640          * items that are not selectable. Something more like a popup menu,
641          * which could have unselectable items, would be appropriate.
642          */
643
644         vector<string> available_devices;
645
646         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
647                 available_devices.push_back (i->name);
648         }
649
650         if (!available_devices.empty()) {
651
652                 update_sensitivity ();
653                                                 
654                 {
655                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
656                         set_popdown_strings (device_combo, available_devices);
657                         device_combo.set_active_text (available_devices.front());
658                 }
659
660                 device_changed ();
661
662                 ok_button->set_sensitive (true);
663                 apply_button->set_sensitive (true);
664
665         } else {
666                 sample_rate_combo.set_sensitive (false);
667                 buffer_size_combo.set_sensitive (false);
668                 input_latency.set_sensitive (false);
669                 output_latency.set_sensitive (false);
670                 input_channels.set_sensitive (false);
671                 output_channels.set_sensitive (false);
672                 ok_button->set_sensitive (false);
673                 apply_button->set_sensitive (false);
674         }
675 }
676
677 void
678 EngineControl::driver_changed ()
679 {
680         if (ignore_changes) {
681                 return;
682         }
683
684         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
685         assert (backend);
686
687         backend->set_driver (driver_combo.get_active_text());
688         list_devices ();
689
690         maybe_display_saved_state ();
691 }
692
693 void
694 EngineControl::device_changed ()
695 {
696         if (ignore_changes) {
697                 return;
698         }
699
700         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
701         assert (backend);
702         string device_name = device_combo.get_active_text ();
703         vector<string> s;
704
705         {
706                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
707
708                 /* don't allow programmatic change to combos to cause a
709                    recursive call to this method.
710                 */
711                 
712                 /* sample rates */
713                 
714                 string desired;
715                 
716                 vector<float> sr;
717
718                 if (_have_control) {
719                         sr = backend->available_sample_rates (device_name);
720                 } else {
721
722                         sr.push_back (8000.0f);
723                         sr.push_back (16000.0f);
724                         sr.push_back (32000.0f);
725                         sr.push_back (44100.0f);
726                         sr.push_back (48000.0f);
727                         sr.push_back (88200.0f);
728                         sr.push_back (96000.0f);
729                         sr.push_back (192000.0f);
730                         sr.push_back (384000.0f);
731                 }
732
733                 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
734                         s.push_back (rate_as_string (*x));
735                         if (*x == _desired_sample_rate) {
736                                 desired = s.back();
737                         }
738                 }
739                 
740                 if (!s.empty()) {
741                         sample_rate_combo.set_sensitive (true);
742                         set_popdown_strings (sample_rate_combo, s);
743
744                         if (desired.empty()) {
745                                 sample_rate_combo.set_active_text (s.front());
746                         } else {
747                                 sample_rate_combo.set_active_text (desired);
748                         }
749
750                 } else {
751                         sample_rate_combo.set_sensitive (false);
752                 }
753
754                 /* buffer sizes */
755                 
756                 vector<uint32_t> bs;
757                 
758                 if (_have_control) {
759                         bs = backend->available_buffer_sizes(device_name);
760                 } else if (backend->can_change_buffer_size_when_running()) {
761                         bs.push_back (8);
762                         bs.push_back (16);
763                         bs.push_back (32);
764                         bs.push_back (64);
765                         bs.push_back (128);
766                         bs.push_back (256);
767                         bs.push_back (512);
768                         bs.push_back (1024);
769                         bs.push_back (2048);
770                         bs.push_back (4096);
771                         bs.push_back (8192);
772                 }
773                 s.clear ();
774                 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
775                         s.push_back (bufsize_as_string (*x));
776                 }
777                 
778                 if (!s.empty()) {
779                         buffer_size_combo.set_sensitive (true);
780                         set_popdown_strings (buffer_size_combo, s);
781                         
782                         buffer_size_combo.set_active_text (s.front());
783                         show_buffer_duration ();
784                 } else {
785                         buffer_size_combo.set_sensitive (false);
786                 }
787
788                 /* XXX theoretically need to set min + max channel counts here
789                  */
790                 
791                 manage_control_app_sensitivity ();
792         }
793
794         /* pick up any saved state for this device */
795
796         maybe_display_saved_state ();
797
798         /* and push it to the backend */
799
800         push_state_to_backend (false);
801 }       
802
803 string
804 EngineControl::bufsize_as_string (uint32_t sz)
805 {
806         /* Translators: "samples" is always plural here, so no
807            need for plural+singular forms.
808         */
809         char buf[32];
810         snprintf (buf, sizeof (buf), _("%u samples"), sz);
811         return buf;
812 }
813
814 void 
815 EngineControl::sample_rate_changed ()
816 {
817         if (ignore_changes) {
818                 return;
819         }
820
821         /* reset the strings for buffer size to show the correct msec value
822            (reflecting the new sample rate).
823         */
824
825         show_buffer_duration ();
826         save_state ();
827
828 }
829
830 void 
831 EngineControl::buffer_size_changed ()
832 {
833         if (ignore_changes) {
834                 return;
835         }
836
837         show_buffer_duration ();
838         save_state ();
839 }
840
841 void
842 EngineControl::show_buffer_duration ()
843 {
844
845         /* buffer sizes  - convert from just samples to samples + msecs for
846          * the displayed string
847          */
848
849         string bs_text = buffer_size_combo.get_active_text ();
850         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
851         uint32_t rate = get_rate();
852
853         /* Translators: "msecs" is ALWAYS plural here, so we do not
854            need singular form as well.
855         */
856         /* Developers: note the hard-coding of a double buffered model
857            in the (2 * samples) computation of latency. we always start
858            the audiobackend in this configuration.
859         */
860         char buf[32];
861         snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
862         buffer_size_duration_label.set_text (buf);
863 }
864
865 void
866 EngineControl::parameter_changed ()
867 {
868         if (!ignore_changes) {
869                 save_state ();
870         }
871 }
872
873 EngineControl::State*
874 EngineControl::get_matching_state (const string& backend,
875                                    const string& driver,
876                                    const string& device)
877 {
878         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
879                 if ((*i).backend == backend &&
880                     (*i).driver == driver &&
881                     (*i).device == device) {
882                         return &(*i);
883                 }
884         }
885         return 0;
886 }
887
888 EngineControl::State*
889 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
890 {
891         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
892
893         if (backend) {
894                 return get_matching_state (backend_combo.get_active_text(),
895                                            (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
896                                            device_combo.get_active_text());
897         }
898
899
900         return get_matching_state (backend_combo.get_active_text(),
901                                    string(),
902                                    device_combo.get_active_text());
903 }
904
905 EngineControl::State*
906 EngineControl::save_state ()
907 {
908         if (!_have_control) {
909                 return 0;
910         }
911
912         bool existing = true;
913         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
914
915         if (!state) {
916                 existing = false;
917                 state = new State;
918         }
919         
920         store_state (*state);
921
922         if (!existing) {
923                 states.push_back (*state);
924         }
925
926         return state;
927 }
928
929 void
930 EngineControl::store_state (State& state)
931 {
932         state.backend = get_backend ();
933         state.driver = get_driver ();
934         state.device = get_device_name ();
935         state.sample_rate = get_rate ();
936         state.buffer_size = get_buffer_size ();
937         state.input_latency = get_input_latency ();
938         state.output_latency = get_output_latency ();
939         state.input_channels = get_input_channels ();
940         state.output_channels = get_output_channels ();
941 }
942
943 void
944 EngineControl::maybe_display_saved_state ()
945 {
946         if (!_have_control) {
947                 return;
948         }
949
950         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
951
952         if (state) {
953                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
954
955                 if (!_desired_sample_rate) {
956                         sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
957                 }
958                 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
959                 /* call this explicitly because we're ignoring changes to
960                    the controls at this point.
961                 */
962                 show_buffer_duration ();
963                 input_latency.set_value (state->input_latency);
964                 output_latency.set_value (state->output_latency);
965         }
966 }
967         
968 XMLNode&
969 EngineControl::get_state ()
970 {
971         XMLNode* root = new XMLNode ("AudioMIDISetup");
972         std::string path;
973
974         if (!states.empty()) {
975                 XMLNode* state_nodes = new XMLNode ("EngineStates");
976                 
977                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
978                         
979                         XMLNode* node = new XMLNode ("State");
980                         
981                         node->add_property ("backend", (*i).backend);
982                         node->add_property ("driver", (*i).driver);
983                         node->add_property ("device", (*i).device);
984                         node->add_property ("sample-rate", (*i).sample_rate);
985                         node->add_property ("buffer-size", (*i).buffer_size);
986                         node->add_property ("input-latency", (*i).input_latency);
987                         node->add_property ("output-latency", (*i).output_latency);
988                         node->add_property ("input-channels", (*i).input_channels);
989                         node->add_property ("output-channels", (*i).output_channels);
990                         node->add_property ("active", (*i).active ? "yes" : "no");
991                         
992                         state_nodes->add_child_nocopy (*node);
993                 }
994                 
995                 root->add_child_nocopy (*state_nodes);
996         }
997
998         return *root;
999 }
1000
1001 void
1002 EngineControl::set_state (const XMLNode& root)
1003 {
1004         XMLNodeList          clist, cclist;
1005         XMLNodeConstIterator citer, cciter;
1006         XMLNode* child;
1007         XMLNode* grandchild;
1008         XMLProperty* prop = NULL;
1009
1010         if (root.name() != "AudioMIDISetup") {
1011                 return;
1012         }
1013
1014         clist = root.children();
1015
1016         states.clear ();
1017
1018         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1019
1020                 child = *citer;
1021                 
1022                 if (child->name() != "EngineStates") {
1023                         continue;
1024                 }
1025
1026                 cclist = child->children();
1027
1028                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1029                         State state;
1030                         
1031                         grandchild = *cciter;
1032
1033                         if (grandchild->name() != "State") {
1034                                 continue;
1035                         }
1036                         
1037                         if ((prop = grandchild->property ("backend")) == 0) {
1038                                 continue;
1039                         }
1040                         state.backend = prop->value ();
1041                         
1042                         if ((prop = grandchild->property ("driver")) == 0) {
1043                                 continue;
1044                         }
1045                         state.driver = prop->value ();
1046                         
1047                         if ((prop = grandchild->property ("device")) == 0) {
1048                                 continue;
1049                         }
1050                         state.device = prop->value ();
1051                         
1052                         if ((prop = grandchild->property ("sample-rate")) == 0) {
1053                                 continue;
1054                         }
1055                         state.sample_rate = atof (prop->value ());
1056                         
1057                         if ((prop = grandchild->property ("buffer-size")) == 0) {
1058                                 continue;
1059                         }
1060                         state.buffer_size = atoi (prop->value ());
1061                         
1062                         if ((prop = grandchild->property ("input-latency")) == 0) {
1063                                 continue;
1064                         }
1065                         state.input_latency = atoi (prop->value ());
1066                         
1067                         if ((prop = grandchild->property ("output-latency")) == 0) {
1068                                 continue;
1069                         }
1070                         state.output_latency = atoi (prop->value ());
1071                         
1072                         if ((prop = grandchild->property ("input-channels")) == 0) {
1073                                 continue;
1074                         }
1075                         state.input_channels = atoi (prop->value ());
1076                         
1077                         if ((prop = grandchild->property ("output-channels")) == 0) {
1078                                 continue;
1079                         }
1080                         state.output_channels = atoi (prop->value ());
1081
1082                         if ((prop = grandchild->property ("active")) == 0) {
1083                                 continue;
1084                         }
1085                         state.active = string_is_affirmative (prop->value ());
1086                         
1087                         states.push_back (state);
1088                 }
1089         }
1090
1091         /* now see if there was an active state and switch the setup to it */
1092         
1093         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1094
1095                 if ((*i).active) {
1096                         ignore_changes++;
1097                         backend_combo.set_active_text ((*i).backend);
1098                         driver_combo.set_active_text ((*i).driver);
1099                         device_combo.set_active_text ((*i).device);
1100                         sample_rate_combo.set_active_text (rate_as_string ((*i).sample_rate));
1101                         buffer_size_combo.set_active_text (bufsize_as_string ((*i).buffer_size));
1102                         input_latency.set_value ((*i).input_latency);
1103                         output_latency.set_value ((*i).output_latency);
1104                         ignore_changes--;
1105                         break;
1106                 }
1107         }
1108 }
1109
1110
1111 int
1112 EngineControl::push_state_to_backend (bool start)
1113 {
1114         if (no_push) {
1115                 return 0;
1116         }
1117
1118         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1119
1120         if (!backend) {
1121                 return 0;
1122         }
1123         
1124         /* figure out what is going to change */
1125
1126         bool restart_required = false;
1127         bool was_running = ARDOUR::AudioEngine::instance()->running();
1128         bool change_driver = false;
1129         bool change_device = false;
1130         bool change_rate = false;
1131         bool change_bufsize = false;
1132         bool change_latency = false;
1133         bool change_channels = false;
1134
1135         uint32_t ochan = get_output_channels ();
1136         uint32_t ichan = get_input_channels ();
1137
1138         if (_have_control) {
1139
1140                 if (started_at_least_once) {
1141                         
1142                         /* we can control the backend */
1143                         
1144                         if (backend->requires_driver_selection()) {
1145                                 if (get_driver() != backend->driver_name()) {
1146                                         change_driver = true;
1147                                 }
1148                         }
1149                         
1150                         if (get_device_name() != backend->device_name()) {
1151                                 change_device = true;
1152                         }
1153                         
1154                         if (get_rate() != backend->sample_rate()) {
1155                                 change_rate = true;
1156                         }
1157                         
1158                         if (get_buffer_size() != backend->buffer_size()) {
1159                                 change_bufsize = true;
1160                         }
1161                         
1162                         /* zero-requested channels means "all available" */
1163
1164                         if (ichan == 0) {
1165                                 ichan = backend->input_channels();
1166                         }
1167                         
1168                         if (ochan == 0) {
1169                                 ochan = backend->output_channels();
1170                         }
1171                         
1172                         if (ichan != backend->input_channels()) {
1173                                 change_channels = true;
1174                         }
1175                         
1176                         if (ochan != backend->output_channels()) {
1177                                 change_channels = true;
1178                         }
1179
1180                         if (get_input_latency() != backend->systemic_input_latency() ||
1181                             get_output_latency() != backend->systemic_output_latency()) {
1182                                 change_latency = true;
1183                         }
1184                 } else {
1185                         /* backend never started, so we have to force a group
1186                            of settings.
1187                         */
1188                         change_driver = true;
1189                         change_device = true;
1190                         change_rate = true;
1191                         change_bufsize = true;
1192                         change_channels = true;
1193                         change_latency = true;
1194                 }
1195
1196         } else {
1197
1198                 /* we have no control over the backend, meaning that we can
1199                  * only possibly change sample rate and buffer size.
1200                  */
1201
1202
1203                 if (get_rate() != backend->sample_rate()) {
1204                         change_bufsize = true;
1205                 }
1206
1207                 if (get_buffer_size() != backend->buffer_size()) {
1208                         change_bufsize = true;
1209                 }
1210         }
1211
1212         if (!_have_control) {
1213
1214                 /* We do not have control over the backend, so the best we can
1215                  * do is try to change the sample rate and/or bufsize and get
1216                  * out of here.
1217                  */
1218
1219                 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1220                         return 1;
1221                 }
1222
1223                 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1224                         return 1;
1225                 }
1226                 
1227                 if (change_rate) {
1228                         backend->set_sample_rate (get_rate());
1229                 }
1230                 
1231                 if (change_bufsize) {
1232                         backend->set_buffer_size (get_buffer_size());
1233                 }
1234
1235                 post_push ();
1236
1237                 return 0;
1238         } 
1239
1240         /* determine if we need to stop the backend before changing parameters */
1241
1242         if (change_driver || change_device || change_channels || change_latency ||
1243             (change_rate && !backend->can_change_sample_rate_when_running()) ||
1244             (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1245                 restart_required = true;
1246         } else {
1247                 restart_required = false;
1248         }
1249
1250         if (was_running) {
1251
1252                 if (!change_driver && !change_device && !change_channels && !change_latency) {
1253                         /* no changes in any parameters that absolutely require a
1254                          * restart, so check those that might be changeable without a
1255                          * restart
1256                          */
1257                         
1258                         if (change_rate && !backend->can_change_sample_rate_when_running()) {
1259                                 /* can't do this while running ... */
1260                                 restart_required = true;
1261                         }
1262
1263                         if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1264                                 /* can't do this while running ... */
1265                                 restart_required = true;
1266                         }
1267                 }
1268         }
1269
1270         if (was_running) {
1271                 if (restart_required) {
1272                         if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1273                                 return -1;
1274                         }
1275                 }
1276         }
1277                 
1278
1279         if (change_driver && backend->set_driver (get_driver())) {
1280                 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1281                 return -1;
1282         }
1283         if (change_device && backend->set_device_name (get_device_name())) {
1284                 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1285                 return -1;
1286         }
1287         if (change_rate && backend->set_sample_rate (get_rate())) {
1288                 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1289                 return -1;
1290         }
1291         if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1292                 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1293                 return -1;
1294         }
1295
1296         if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1297                 if (backend->set_input_channels (get_input_channels())) {
1298                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1299                         return -1;
1300                 }
1301                 if (backend->set_output_channels (get_output_channels())) {
1302                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1303                         return -1;
1304                 }
1305         }
1306         if (change_latency) {
1307                 if (backend->set_systemic_input_latency (get_input_latency())) {
1308                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1309                         return -1;
1310                 }
1311                 if (backend->set_systemic_output_latency (get_output_latency())) {
1312                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1313                         return -1;
1314                 }
1315         }
1316                         
1317         if (start || (was_running && restart_required)) {
1318                 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1319                         return -1;
1320                 }
1321         }
1322         
1323         post_push ();
1324
1325         return 0;
1326 }
1327
1328 void
1329 EngineControl::post_push ()
1330 {
1331         /* get a pointer to the current state object, creating one if
1332          * necessary
1333          */
1334         
1335         if (_have_control) {
1336                 State* state = get_saved_state_for_currently_displayed_backend_and_device ();
1337                 
1338                 if (!state) {
1339                         state = save_state ();
1340                         assert (state);
1341                 }
1342                 
1343                 /* all off */
1344                 
1345                 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1346                         (*i).active = false;
1347                 }
1348                 
1349                 /* mark this one active (to be used next time the dialog is
1350                  * shown)
1351                  */
1352                 
1353                 state->active = true;
1354                 
1355                 manage_control_app_sensitivity ();
1356         }
1357
1358         /* schedule a redisplay of MIDI ports */
1359         
1360         Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1361 }
1362
1363
1364 float
1365 EngineControl::get_rate () const
1366 {
1367         float r = atof (sample_rate_combo.get_active_text ());
1368         /* the string may have been translated with an abbreviation for
1369          * thousands, so use a crude heuristic to fix this.
1370          */
1371         if (r < 1000.0) {
1372                 r *= 1000.0;
1373         }
1374         return r;
1375 }
1376         
1377
1378 uint32_t
1379 EngineControl::get_buffer_size () const
1380 {
1381         string txt = buffer_size_combo.get_active_text ();
1382         uint32_t samples;
1383
1384         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1385                 throw exception ();
1386         }
1387
1388         return samples;
1389 }
1390
1391 uint32_t
1392 EngineControl::get_input_channels() const
1393 {
1394         return (uint32_t) input_channels_adjustment.get_value();
1395 }
1396
1397 uint32_t
1398 EngineControl::get_output_channels() const
1399 {
1400         return (uint32_t) output_channels_adjustment.get_value();
1401 }
1402
1403 uint32_t
1404 EngineControl::get_input_latency() const
1405 {
1406         return (uint32_t) input_latency_adjustment.get_value();
1407 }
1408
1409 uint32_t
1410 EngineControl::get_output_latency() const
1411 {
1412         return (uint32_t) output_latency_adjustment.get_value();
1413 }
1414
1415 string
1416 EngineControl::get_backend () const
1417 {
1418         return backend_combo.get_active_text ();
1419 }
1420
1421 string
1422 EngineControl::get_driver () const
1423 {
1424         return driver_combo.get_active_text ();
1425 }
1426
1427 string
1428 EngineControl::get_device_name () const
1429 {
1430         return device_combo.get_active_text ();
1431 }
1432
1433 void
1434 EngineControl::control_app_button_clicked ()
1435 {
1436         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1437         
1438         if (!backend) {
1439                 return;
1440         }
1441         
1442         backend->launch_control_app ();
1443 }
1444
1445 void
1446 EngineControl::manage_control_app_sensitivity ()
1447 {
1448         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1449         
1450         if (!backend) {
1451                 return;
1452         }
1453         
1454         string appname = backend->control_app_name();
1455
1456         if (appname.empty()) {
1457                 control_app_button.set_sensitive (false);
1458         } else {
1459                 control_app_button.set_sensitive (true);
1460         }
1461 }
1462
1463 void
1464 EngineControl::set_desired_sample_rate (uint32_t sr)
1465 {
1466         _desired_sample_rate = sr;
1467         device_changed ();
1468 }
1469
1470 void
1471 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1472 {
1473         if (page_num == 0) {
1474                 cancel_button->set_sensitive (true);
1475                 ok_button->set_sensitive (true);
1476                 apply_button->set_sensitive (true);
1477         } else {
1478                 cancel_button->set_sensitive (false);
1479                 ok_button->set_sensitive (false);
1480                 apply_button->set_sensitive (false);
1481         }
1482
1483         if (page_num == 1) {
1484                 /* MIDI tab */
1485                 refresh_midi_display ();
1486         }
1487
1488         if (page_num == 2) {
1489                 /* latency tab */
1490
1491                 if (!ARDOUR::AudioEngine::instance()->running()) {
1492                         
1493                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1494                         
1495                         /* save any existing latency values */
1496                         
1497                         uint32_t il = (uint32_t) input_latency.get_value ();
1498                         uint32_t ol = (uint32_t) input_latency.get_value ();
1499
1500                         /* reset to zero so that our new test instance of JACK
1501                            will be clean of any existing latency measures.
1502                         */
1503                         
1504                         input_latency.set_value (0);
1505                         output_latency.set_value (0);
1506                         
1507                         /* reset control */
1508
1509                         input_latency.set_value (il);
1510                         output_latency.set_value (ol);
1511
1512                 } 
1513
1514                 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1515                         disable_latency_tab ();
1516                 }
1517
1518                 enable_latency_tab ();
1519
1520         } else {
1521                 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1522         }
1523 }
1524
1525 /* latency measurement */
1526
1527 bool
1528 EngineControl::check_latency_measurement ()
1529 {
1530         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1531
1532         if (mtdm->resolve () < 0) {
1533                 lm_results.set_markup (string_compose ("<span foreground=\"red\">%1</span>", _("No signal detected ")));
1534                 return true;
1535         }
1536
1537         if (mtdm->err () > 0.3) {
1538                 mtdm->invert ();
1539                 mtdm->resolve ();
1540         }
1541
1542         char buf[128];
1543         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1544
1545         if (sample_rate == 0) {
1546                 lm_results.set_text (_("Disconnected from audio engine"));
1547                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1548                 return false;
1549         }
1550
1551         uint32_t frames_total = mtdm->del();
1552         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1553
1554         snprintf (buf, sizeof (buf), "%u samples %10.3lf ms", extra, extra * 1000.0f/sample_rate);
1555
1556         bool solid = true;
1557
1558         if (mtdm->err () > 0.2) {
1559                 strcat (buf, " ??");
1560                 solid = false;
1561         }
1562
1563         if (mtdm->inv ()) {
1564                 strcat (buf, " (Inv)");
1565                 solid = false;
1566         }
1567
1568         if (solid) {
1569                 lm_measure_button.set_active (false);
1570                 lm_use_button.set_sensitive (true);
1571                 strcat (buf, " (set)");
1572                 have_lm_results = true;
1573         }
1574         
1575         lm_results.set_text (buf);
1576
1577         return true;
1578 }
1579
1580 void
1581 EngineControl::start_latency_detection ()
1582 {
1583         ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1584         ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1585         ARDOUR::AudioEngine::instance()->start_latency_detection ();
1586         lm_results.set_text (_("Detecting ..."));
1587         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
1588         lm_start_stop_label.set_text (_("Cancel measurement"));
1589         have_lm_results = false;
1590         lm_input_channel_combo.set_sensitive (false);
1591         lm_output_channel_combo.set_sensitive (false);
1592 }
1593
1594 void
1595 EngineControl::end_latency_detection ()
1596 {
1597         ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1598         latency_timeout.disconnect ();
1599         lm_start_stop_label.set_text (_("Measure latency"));
1600         if (!have_lm_results) {
1601                 lm_results.set_markup ("<i>No measurement results yet</i>");
1602         }
1603         lm_input_channel_combo.set_sensitive (true);
1604         lm_output_channel_combo.set_sensitive (true);
1605 }
1606
1607 void
1608 EngineControl::latency_button_toggled ()
1609 {
1610         if (lm_measure_button.get_active ()) {
1611                 start_latency_detection ();
1612         } else {
1613                 end_latency_detection ();
1614         }
1615 }
1616
1617 void
1618 EngineControl::use_latency_button_clicked ()
1619 {
1620         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1621
1622         if (!mtdm) {
1623                 return;
1624         }
1625
1626         uint32_t frames_total = mtdm->del();
1627         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1628         uint32_t one_way = extra/2;
1629
1630         input_latency_adjustment.set_value (one_way);
1631         output_latency_adjustment.set_value (one_way);
1632 }
1633
1634 bool
1635 EngineControl::on_delete_event (GdkEventAny* ev)
1636 {
1637         if (notebook.get_current_page() == 2) {
1638                 /* currently on latency tab - be sure to clean up */
1639                 end_latency_detection ();
1640         }
1641         return ArdourDialog::on_delete_event (ev);
1642 }
1643
1644 void
1645 EngineControl::engine_running ()
1646 {
1647         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1648         assert (backend);
1649
1650         buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
1651         sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
1652
1653         buffer_size_combo.set_sensitive (true);
1654         sample_rate_combo.set_sensitive (true);
1655
1656         connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
1657
1658         started_at_least_once = true;
1659 }
1660
1661 void
1662 EngineControl::engine_stopped ()
1663 {
1664         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1665         assert (backend);
1666
1667         buffer_size_combo.set_sensitive (false);
1668         connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
1669
1670         sample_rate_combo.set_sensitive (true);
1671         buffer_size_combo.set_sensitive (true);
1672 }
1673         
1674 void
1675 EngineControl::connect_disconnect_click() 
1676 {
1677         if (ARDOUR::AudioEngine::instance()->running()) {
1678                 ARDOUR_UI::instance()->disconnect_from_engine ();
1679         } else {
1680                 ARDOUR_UI::instance()->reconnect_to_engine ();
1681         }
1682 }