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