79bda1bcc55af29aa4d5c4a8c59ac751be429d3d
[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 "i18n.h"
52
53 using namespace std;
54 using namespace Gtk;
55 using namespace Gtkmm2ext;
56 using namespace PBD;
57 using namespace Glib;
58
59 EngineControl::EngineControl ()
60         : ArdourDialog (_("Audio/MIDI Setup"))
61         , basic_packer (9, 3)
62         , input_latency_adjustment (0, 0, 99999, 1)
63         , input_latency (input_latency_adjustment)
64         , output_latency_adjustment (0, 0, 99999, 1)
65         , output_latency (output_latency_adjustment)
66         , input_channels_adjustment (0, 0, 256, 1)
67         , input_channels (input_channels_adjustment)
68         , output_channels_adjustment (0, 0, 256, 1)
69         , output_channels (output_channels_adjustment)
70         , ports_adjustment (128, 8, 1024, 1, 16)
71         , ports_spinner (ports_adjustment)
72         , control_app_button (_("Device Control Panel"))
73         , lm_start_stop_label (_("Measure latency"))
74         , lm_use_button (_("Use results"))
75         , lm_table (5, 2)
76         , have_lm_results (false)
77         , midi_refresh_button (_("Refresh list"))
78         , aj_button (_("Start MIDI ALSA/JACK bridge"))
79         , ignore_changes (0)
80         , _desired_sample_rate (0)
81         , no_push (true)
82         , started_at_least_once (false)
83 {
84         using namespace Notebook_Helpers;
85         vector<string> strings;
86         Label* label;
87         AttachOptions xopt = AttachOptions (FILL|EXPAND);
88         int row;
89
90         set_name (X_("AudioMIDISetup"));
91
92         /* the backend combo is the one thing that is ALWAYS visible 
93          */
94
95         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
96         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
97                 strings.push_back ((*b)->name);
98         }
99
100         set_popdown_strings (backend_combo, strings);
101         backend_combo.set_active_text (strings.front());
102         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
103
104         /* setup basic packing characteristics for the table used on the main
105          * tab of the notebook
106          */
107
108         basic_packer.set_spacings (6);
109         basic_packer.set_border_width (12);
110         basic_packer.set_homogeneous (true);
111
112         /* pack it in */
113
114         basic_hbox.pack_start (basic_packer, false, false);
115
116         /* latency tab */
117
118         /* latency measurement tab */
119         
120         lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
121         
122         row = 0;
123         lm_table.set_row_spacings (12);
124
125         lm_table.attach (lm_title, 0, 2, row, row+1, xopt, (AttachOptions) 0);
126         row++;
127
128         Gtk::Label* preamble;
129
130         preamble = manage (new Label);
131         preamble->set_width_chars (60);
132         preamble->set_line_wrap (true);
133         preamble->set_markup (_("<span weight=\"bold\">Turn down the volume on your hardware to a very low level.</span>"));
134
135         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
136         row++;
137
138         preamble = manage (new Label);
139         preamble->set_width_chars (60);
140         preamble->set_line_wrap (true);
141         preamble->set_markup (_("Select two channels below and connect them using a cable or (less ideally) a speaker and microphone."));
142
143         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
144         row++;
145
146         label = manage (new Label (_("Output channel")));
147         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
148
149         Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
150         misc_align->add (lm_output_channel_combo);
151         lm_table.attach (*misc_align, 1, 2, row, row+1, xopt, (AttachOptions) 0);
152         ++row;
153
154         label = manage (new Label (_("Input channel")));
155         lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
156
157         misc_align = manage (new Alignment (0.0, 0.5));
158         misc_align->add (lm_input_channel_combo);
159         lm_table.attach (*misc_align, 1, 2, row, row+1, FILL, (AttachOptions) 0);
160         ++row;
161
162         xopt = AttachOptions(0);
163
164         lm_measure_button.add (lm_start_stop_label);
165         
166         lm_measure_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::latency_button_toggled));
167         lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
168         lm_use_button.set_sensitive (false);
169
170         preamble = manage (new Label);
171         preamble->set_width_chars (60);
172         preamble->set_line_wrap (true);
173         preamble->set_markup (_("Once the channels are connected, click the \"Measure latency\" button."));
174         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
175         row++;
176
177         lm_table.attach (lm_measure_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
178         ++row;
179         lm_table.attach (lm_results, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
180         ++row;
181
182
183         preamble = manage (new Label);
184         preamble->set_width_chars (60);
185         preamble->set_line_wrap (true);
186         preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
187         lm_table.attach (*preamble, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
188         row++;
189
190         lm_table.attach (lm_use_button, 0, 2, row, row+1, xopt, (AttachOptions) 0);
191         ++row;
192
193         lm_results.set_markup ("<i>No measurement results yet</i>");
194
195         lm_vbox.set_border_width (12);
196         lm_vbox.pack_start (lm_table, false, false);
197
198         /* pack it all up */
199
200         notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
201         notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
202         notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
203         notebook.set_border_width (12);
204
205         notebook.set_tab_pos (POS_RIGHT);
206         notebook.show_all ();
207
208         notebook.set_name ("SettingsNotebook");
209
210         /* packup the notebook */
211
212         get_vbox()->set_border_width (12);
213         get_vbox()->pack_start (notebook);
214
215         /* need a special function to print "all available channels" when the
216          * channel counts hit zero.
217          */
218
219         input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
220         output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
221
222         control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
223         manage_control_app_sensitivity ();
224
225         cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
226         ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
227         apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
228
229         /* Pick up any existing audio setup configuration, if appropriate */
230
231         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
232         
233         ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
234         ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
235         ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
236
237         cerr << "AMS about to change backend\n";
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::rate_as_string (float r)
805 {
806         char buf[32];
807         if (fmod (r, 1000.0f)) {
808                 snprintf (buf, sizeof (buf), "%.1f kHz", r/1000.0);
809         } else {
810                 snprintf (buf, sizeof (buf), "%.0f kHz", r/1000.0);
811         }
812         return buf;
813 }
814
815 string
816 EngineControl::bufsize_as_string (uint32_t sz)
817 {
818         /* Translators: "samples" is always plural here, so no
819            need for plural+singular forms.
820         */
821         char buf[32];
822         snprintf (buf, sizeof (buf), _("%u samples"), sz);
823         return buf;
824 }
825
826 void 
827 EngineControl::sample_rate_changed ()
828 {
829         if (ignore_changes) {
830                 return;
831         }
832
833         /* reset the strings for buffer size to show the correct msec value
834            (reflecting the new sample rate).
835         */
836
837         show_buffer_duration ();
838         save_state ();
839
840 }
841
842 void 
843 EngineControl::buffer_size_changed ()
844 {
845         if (ignore_changes) {
846                 return;
847         }
848
849         show_buffer_duration ();
850         save_state ();
851 }
852
853 void
854 EngineControl::show_buffer_duration ()
855 {
856
857         /* buffer sizes  - convert from just samples to samples + msecs for
858          * the displayed string
859          */
860
861         string bs_text = buffer_size_combo.get_active_text ();
862         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
863         uint32_t rate = get_rate();
864
865         /* Translators: "msecs" is ALWAYS plural here, so we do not
866            need singular form as well.
867         */
868         /* Developers: note the hard-coding of a double buffered model
869            in the (2 * samples) computation of latency. we always start
870            the audiobackend in this configuration.
871         */
872         char buf[32];
873         snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
874         buffer_size_duration_label.set_text (buf);
875 }
876
877 void
878 EngineControl::parameter_changed ()
879 {
880         if (!ignore_changes) {
881                 save_state ();
882         }
883 }
884
885 EngineControl::State*
886 EngineControl::get_matching_state (const string& backend,
887                                    const string& driver,
888                                    const string& device)
889 {
890         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
891                 if ((*i).backend == backend &&
892                     (*i).driver == driver &&
893                     (*i).device == device) {
894                         return &(*i);
895                 }
896         }
897         return 0;
898 }
899
900 EngineControl::State*
901 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
902 {
903         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
904
905         if (backend) {
906                 return get_matching_state (backend_combo.get_active_text(),
907                                            (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
908                                            device_combo.get_active_text());
909         }
910
911
912         return get_matching_state (backend_combo.get_active_text(),
913                                    string(),
914                                    device_combo.get_active_text());
915 }
916
917 EngineControl::State*
918 EngineControl::save_state ()
919 {
920         if (!_have_control) {
921                 return 0;
922         }
923
924         bool existing = true;
925         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
926
927         if (!state) {
928                 existing = false;
929                 state = new State;
930         }
931         
932         store_state (*state);
933
934         if (!existing) {
935                 states.push_back (*state);
936         }
937
938         return state;
939 }
940
941 void
942 EngineControl::store_state (State& state)
943 {
944         state.backend = get_backend ();
945         state.driver = get_driver ();
946         state.device = get_device_name ();
947         state.sample_rate = get_rate ();
948         state.buffer_size = get_buffer_size ();
949         state.input_latency = get_input_latency ();
950         state.output_latency = get_output_latency ();
951         state.input_channels = get_input_channels ();
952         state.output_channels = get_output_channels ();
953 }
954
955 void
956 EngineControl::maybe_display_saved_state ()
957 {
958         if (!_have_control) {
959                 return;
960         }
961
962         State* state = get_saved_state_for_currently_displayed_backend_and_device ();
963
964         if (state) {
965                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
966
967                 if (!_desired_sample_rate) {
968                         sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
969                 }
970                 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
971                 /* call this explicitly because we're ignoring changes to
972                    the controls at this point.
973                 */
974                 show_buffer_duration ();
975                 input_latency.set_value (state->input_latency);
976                 output_latency.set_value (state->output_latency);
977         }
978 }
979         
980 XMLNode&
981 EngineControl::get_state ()
982 {
983         XMLNode* root = new XMLNode ("AudioMIDISetup");
984         std::string path;
985
986         if (!states.empty()) {
987                 XMLNode* state_nodes = new XMLNode ("EngineStates");
988                 
989                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
990                         
991                         XMLNode* node = new XMLNode ("State");
992                         
993                         node->add_property ("backend", (*i).backend);
994                         node->add_property ("driver", (*i).driver);
995                         node->add_property ("device", (*i).device);
996                         node->add_property ("sample-rate", (*i).sample_rate);
997                         node->add_property ("buffer-size", (*i).buffer_size);
998                         node->add_property ("input-latency", (*i).input_latency);
999                         node->add_property ("output-latency", (*i).output_latency);
1000                         node->add_property ("input-channels", (*i).input_channels);
1001                         node->add_property ("output-channels", (*i).output_channels);
1002                         node->add_property ("active", (*i).active ? "yes" : "no");
1003                         
1004                         state_nodes->add_child_nocopy (*node);
1005                 }
1006                 
1007                 root->add_child_nocopy (*state_nodes);
1008         }
1009
1010         return *root;
1011 }
1012
1013 void
1014 EngineControl::set_state (const XMLNode& root)
1015 {
1016         XMLNodeList          clist, cclist;
1017         XMLNodeConstIterator citer, cciter;
1018         XMLNode* child;
1019         XMLNode* grandchild;
1020         XMLProperty* prop = NULL;
1021
1022         if (root.name() != "AudioMIDISetup") {
1023                 return;
1024         }
1025
1026         clist = root.children();
1027
1028         states.clear ();
1029
1030         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1031
1032                 child = *citer;
1033                 
1034                 if (child->name() != "EngineStates") {
1035                         continue;
1036                 }
1037
1038                 cclist = child->children();
1039
1040                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1041                         State state;
1042                         
1043                         grandchild = *cciter;
1044
1045                         if (grandchild->name() != "State") {
1046                                 continue;
1047                         }
1048                         
1049                         if ((prop = grandchild->property ("backend")) == 0) {
1050                                 continue;
1051                         }
1052                         state.backend = prop->value ();
1053                         
1054                         if ((prop = grandchild->property ("driver")) == 0) {
1055                                 continue;
1056                         }
1057                         state.driver = prop->value ();
1058                         
1059                         if ((prop = grandchild->property ("device")) == 0) {
1060                                 continue;
1061                         }
1062                         state.device = prop->value ();
1063                         
1064                         if ((prop = grandchild->property ("sample-rate")) == 0) {
1065                                 continue;
1066                         }
1067                         state.sample_rate = atof (prop->value ());
1068                         
1069                         if ((prop = grandchild->property ("buffer-size")) == 0) {
1070                                 continue;
1071                         }
1072                         state.buffer_size = atoi (prop->value ());
1073                         
1074                         if ((prop = grandchild->property ("input-latency")) == 0) {
1075                                 continue;
1076                         }
1077                         state.input_latency = atoi (prop->value ());
1078                         
1079                         if ((prop = grandchild->property ("output-latency")) == 0) {
1080                                 continue;
1081                         }
1082                         state.output_latency = atoi (prop->value ());
1083                         
1084                         if ((prop = grandchild->property ("input-channels")) == 0) {
1085                                 continue;
1086                         }
1087                         state.input_channels = atoi (prop->value ());
1088                         
1089                         if ((prop = grandchild->property ("output-channels")) == 0) {
1090                                 continue;
1091                         }
1092                         state.output_channels = atoi (prop->value ());
1093
1094                         if ((prop = grandchild->property ("active")) == 0) {
1095                                 continue;
1096                         }
1097                         state.active = string_is_affirmative (prop->value ());
1098                         
1099                         states.push_back (state);
1100                 }
1101         }
1102
1103         /* now see if there was an active state and switch the setup to it */
1104         
1105         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1106
1107                 if ((*i).active) {
1108                         ignore_changes++;
1109                         backend_combo.set_active_text ((*i).backend);
1110                         driver_combo.set_active_text ((*i).driver);
1111                         device_combo.set_active_text ((*i).device);
1112                         sample_rate_combo.set_active_text (rate_as_string ((*i).sample_rate));
1113                         buffer_size_combo.set_active_text (bufsize_as_string ((*i).buffer_size));
1114                         input_latency.set_value ((*i).input_latency);
1115                         output_latency.set_value ((*i).output_latency);
1116                         ignore_changes--;
1117                         break;
1118                 }
1119         }
1120 }
1121
1122
1123 int
1124 EngineControl::push_state_to_backend (bool start)
1125 {
1126         if (no_push) {
1127                 return 0;
1128         }
1129
1130         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1131
1132         if (!backend) {
1133                 return 0;
1134         }
1135         
1136         /* figure out what is going to change */
1137
1138         bool restart_required = false;
1139         bool was_running = ARDOUR::AudioEngine::instance()->running();
1140         bool change_driver = false;
1141         bool change_device = false;
1142         bool change_rate = false;
1143         bool change_bufsize = false;
1144         bool change_latency = false;
1145         bool change_channels = false;
1146
1147         uint32_t ochan = get_output_channels ();
1148         uint32_t ichan = get_input_channels ();
1149
1150         if (_have_control) {
1151
1152                 if (started_at_least_once) {
1153                         
1154                         /* we can control the backend */
1155                         
1156                         if (backend->requires_driver_selection()) {
1157                                 if (get_driver() != backend->driver_name()) {
1158                                         change_driver = true;
1159                                 }
1160                         }
1161                         
1162                         if (get_device_name() != backend->device_name()) {
1163                                 change_device = true;
1164                         }
1165                         
1166                         if (get_rate() != backend->sample_rate()) {
1167                                 change_rate = true;
1168                         }
1169                         
1170                         if (get_buffer_size() != backend->buffer_size()) {
1171                                 change_bufsize = true;
1172                         }
1173                         
1174                         /* zero-requested channels means "all available" */
1175
1176                         if (ichan == 0) {
1177                                 ichan = backend->input_channels();
1178                         }
1179                         
1180                         if (ochan == 0) {
1181                                 ochan = backend->output_channels();
1182                         }
1183                         
1184                         if (ichan != backend->input_channels()) {
1185                                 change_channels = true;
1186                         }
1187                         
1188                         if (ochan != backend->output_channels()) {
1189                                 change_channels = true;
1190                         }
1191
1192                         if (get_input_latency() != backend->systemic_input_latency() ||
1193                             get_output_latency() != backend->systemic_output_latency()) {
1194                                 change_latency = true;
1195                         }
1196                 } else {
1197                         /* backend never started, so we have to force a group
1198                            of settings.
1199                         */
1200                         change_driver = true;
1201                         change_device = true;
1202                         change_rate = true;
1203                         change_bufsize = true;
1204                         change_channels = true;
1205                         change_latency = true;
1206                 }
1207
1208         } else {
1209
1210                 /* we have no control over the backend, meaning that we can
1211                  * only possibly change sample rate and buffer size.
1212                  */
1213
1214
1215                 if (get_rate() != backend->sample_rate()) {
1216                         change_bufsize = true;
1217                 }
1218
1219                 if (get_buffer_size() != backend->buffer_size()) {
1220                         change_bufsize = true;
1221                 }
1222         }
1223
1224         if (!_have_control) {
1225
1226                 /* We do not have control over the backend, so the best we can
1227                  * do is try to change the sample rate and/or bufsize and get
1228                  * out of here.
1229                  */
1230
1231                 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1232                         return 1;
1233                 }
1234
1235                 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1236                         return 1;
1237                 }
1238                 
1239                 if (change_rate) {
1240                         backend->set_sample_rate (get_rate());
1241                 }
1242                 
1243                 if (change_bufsize) {
1244                         backend->set_buffer_size (get_buffer_size());
1245                 }
1246
1247                 post_push ();
1248
1249                 return 0;
1250         } 
1251
1252         /* determine if we need to stop the backend before changing parameters */
1253
1254         if (change_driver || change_device || change_channels || change_latency ||
1255             (change_rate && !backend->can_change_sample_rate_when_running()) ||
1256             (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1257                 restart_required = true;
1258         } else {
1259                 restart_required = false;
1260         }
1261
1262         if (was_running) {
1263
1264                 if (!change_driver && !change_device && !change_channels && !change_latency) {
1265                         /* no changes in any parameters that absolutely require a
1266                          * restart, so check those that might be changeable without a
1267                          * restart
1268                          */
1269                         
1270                         if (change_rate && !backend->can_change_sample_rate_when_running()) {
1271                                 /* can't do this while running ... */
1272                                 restart_required = true;
1273                         }
1274
1275                         if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1276                                 /* can't do this while running ... */
1277                                 restart_required = true;
1278                         }
1279                 }
1280         }
1281
1282         if (was_running) {
1283                 if (restart_required) {
1284                         if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1285                                 return -1;
1286                         }
1287                 }
1288         }
1289                 
1290
1291         if (change_driver && backend->set_driver (get_driver())) {
1292                 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1293                 return -1;
1294         }
1295         if (change_device && backend->set_device_name (get_device_name())) {
1296                 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1297                 return -1;
1298         }
1299         if (change_rate && backend->set_sample_rate (get_rate())) {
1300                 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1301                 return -1;
1302         }
1303         if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1304                 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1305                 return -1;
1306         }
1307
1308         if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1309                 if (backend->set_input_channels (get_input_channels())) {
1310                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1311                         return -1;
1312                 }
1313                 if (backend->set_output_channels (get_output_channels())) {
1314                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1315                         return -1;
1316                 }
1317         }
1318         if (change_latency) {
1319                 if (backend->set_systemic_input_latency (get_input_latency())) {
1320                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1321                         return -1;
1322                 }
1323                 if (backend->set_systemic_output_latency (get_output_latency())) {
1324                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1325                         return -1;
1326                 }
1327         }
1328                         
1329         if (start || (was_running && restart_required)) {
1330                 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1331                         return -1;
1332                 }
1333         }
1334         
1335         post_push ();
1336
1337         return 0;
1338 }
1339
1340 void
1341 EngineControl::post_push ()
1342 {
1343         /* get a pointer to the current state object, creating one if
1344          * necessary
1345          */
1346         
1347         if (_have_control) {
1348                 State* state = get_saved_state_for_currently_displayed_backend_and_device ();
1349                 
1350                 if (!state) {
1351                         state = save_state ();
1352                         assert (state);
1353                 }
1354                 
1355                 /* all off */
1356                 
1357                 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1358                         (*i).active = false;
1359                 }
1360                 
1361                 /* mark this one active (to be used next time the dialog is
1362                  * shown)
1363                  */
1364                 
1365                 state->active = true;
1366                 
1367                 manage_control_app_sensitivity ();
1368         }
1369
1370         /* schedule a redisplay of MIDI ports */
1371         
1372         Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1373 }
1374
1375
1376 float
1377 EngineControl::get_rate () const
1378 {
1379         float r = atof (sample_rate_combo.get_active_text ());
1380         /* the string may have been translated with an abbreviation for
1381          * thousands, so use a crude heuristic to fix this.
1382          */
1383         if (r < 1000.0) {
1384                 r *= 1000.0;
1385         }
1386         return r;
1387 }
1388         
1389
1390 uint32_t
1391 EngineControl::get_buffer_size () const
1392 {
1393         string txt = buffer_size_combo.get_active_text ();
1394         uint32_t samples;
1395
1396         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1397                 throw exception ();
1398         }
1399
1400         return samples;
1401 }
1402
1403 uint32_t
1404 EngineControl::get_input_channels() const
1405 {
1406         return (uint32_t) input_channels_adjustment.get_value();
1407 }
1408
1409 uint32_t
1410 EngineControl::get_output_channels() const
1411 {
1412         return (uint32_t) output_channels_adjustment.get_value();
1413 }
1414
1415 uint32_t
1416 EngineControl::get_input_latency() const
1417 {
1418         return (uint32_t) input_latency_adjustment.get_value();
1419 }
1420
1421 uint32_t
1422 EngineControl::get_output_latency() const
1423 {
1424         return (uint32_t) output_latency_adjustment.get_value();
1425 }
1426
1427 string
1428 EngineControl::get_backend () const
1429 {
1430         return backend_combo.get_active_text ();
1431 }
1432
1433 string
1434 EngineControl::get_driver () const
1435 {
1436         return driver_combo.get_active_text ();
1437 }
1438
1439 string
1440 EngineControl::get_device_name () const
1441 {
1442         return device_combo.get_active_text ();
1443 }
1444
1445 void
1446 EngineControl::control_app_button_clicked ()
1447 {
1448         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1449         
1450         if (!backend) {
1451                 return;
1452         }
1453         
1454         backend->launch_control_app ();
1455 }
1456
1457 void
1458 EngineControl::manage_control_app_sensitivity ()
1459 {
1460         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1461         
1462         if (!backend) {
1463                 return;
1464         }
1465         
1466         string appname = backend->control_app_name();
1467
1468         if (appname.empty()) {
1469                 control_app_button.set_sensitive (false);
1470         } else {
1471                 control_app_button.set_sensitive (true);
1472         }
1473 }
1474
1475 void
1476 EngineControl::set_desired_sample_rate (uint32_t sr)
1477 {
1478         _desired_sample_rate = sr;
1479         device_changed ();
1480 }
1481
1482 void
1483 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1484 {
1485         if (page_num == 0) {
1486                 cancel_button->set_sensitive (true);
1487                 ok_button->set_sensitive (true);
1488                 apply_button->set_sensitive (true);
1489         } else {
1490                 cancel_button->set_sensitive (false);
1491                 ok_button->set_sensitive (false);
1492                 apply_button->set_sensitive (false);
1493         }
1494
1495         if (page_num == 1) {
1496                 /* MIDI tab */
1497                 refresh_midi_display ();
1498         }
1499
1500         if (page_num == 2) {
1501                 /* latency tab */
1502
1503                 if (!ARDOUR::AudioEngine::instance()->running()) {
1504                         
1505                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1506                         
1507                         /* save any existing latency values */
1508                         
1509                         uint32_t il = (uint32_t) input_latency.get_value ();
1510                         uint32_t ol = (uint32_t) input_latency.get_value ();
1511
1512                         /* reset to zero so that our new test instance of JACK
1513                            will be clean of any existing latency measures.
1514                         */
1515                         
1516                         input_latency.set_value (0);
1517                         output_latency.set_value (0);
1518                         
1519                         /* reset control */
1520
1521                         input_latency.set_value (il);
1522                         output_latency.set_value (ol);
1523
1524                 } 
1525
1526                 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1527                         disable_latency_tab ();
1528                 }
1529
1530                 enable_latency_tab ();
1531
1532         } else {
1533                 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1534         }
1535 }
1536
1537 /* latency measurement */
1538
1539 bool
1540 EngineControl::check_latency_measurement ()
1541 {
1542         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1543
1544         if (mtdm->resolve () < 0) {
1545                 lm_results.set_markup (string_compose ("<span foreground=\"red\">%1</span>", _("No signal detected ")));
1546                 return true;
1547         }
1548
1549         if (mtdm->err () > 0.3) {
1550                 mtdm->invert ();
1551                 mtdm->resolve ();
1552         }
1553
1554         char buf[128];
1555         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1556
1557         if (sample_rate == 0) {
1558                 lm_results.set_text (_("Disconnected from audio engine"));
1559                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1560                 return false;
1561         }
1562
1563         uint32_t frames_total = mtdm->del();
1564         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1565
1566         snprintf (buf, sizeof (buf), "%u samples %10.3lf ms", extra, extra * 1000.0f/sample_rate);
1567
1568         bool solid = true;
1569
1570         if (mtdm->err () > 0.2) {
1571                 strcat (buf, " ??");
1572                 solid = false;
1573         }
1574
1575         if (mtdm->inv ()) {
1576                 strcat (buf, " (Inv)");
1577                 solid = false;
1578         }
1579
1580         if (solid) {
1581                 lm_measure_button.set_active (false);
1582                 lm_use_button.set_sensitive (true);
1583                 strcat (buf, " (set)");
1584                 have_lm_results = true;
1585         }
1586         
1587         lm_results.set_text (buf);
1588
1589         return true;
1590 }
1591
1592 void
1593 EngineControl::start_latency_detection ()
1594 {
1595         ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1596         ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1597         ARDOUR::AudioEngine::instance()->start_latency_detection ();
1598         lm_results.set_text (_("Detecting ..."));
1599         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 250);
1600         lm_start_stop_label.set_text (_("Cancel measurement"));
1601         have_lm_results = false;
1602         lm_input_channel_combo.set_sensitive (false);
1603         lm_output_channel_combo.set_sensitive (false);
1604 }
1605
1606 void
1607 EngineControl::end_latency_detection ()
1608 {
1609         ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1610         latency_timeout.disconnect ();
1611         lm_start_stop_label.set_text (_("Measure latency"));
1612         if (!have_lm_results) {
1613                 lm_results.set_markup ("<i>No measurement results yet</i>");
1614         }
1615         lm_input_channel_combo.set_sensitive (true);
1616         lm_output_channel_combo.set_sensitive (true);
1617 }
1618
1619 void
1620 EngineControl::latency_button_toggled ()
1621 {
1622         if (lm_measure_button.get_active ()) {
1623                 start_latency_detection ();
1624         } else {
1625                 end_latency_detection ();
1626         }
1627 }
1628
1629 void
1630 EngineControl::use_latency_button_clicked ()
1631 {
1632         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1633
1634         if (!mtdm) {
1635                 return;
1636         }
1637
1638         uint32_t frames_total = mtdm->del();
1639         uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1640         uint32_t one_way = extra/2;
1641
1642         input_latency_adjustment.set_value (one_way);
1643         output_latency_adjustment.set_value (one_way);
1644 }
1645
1646 bool
1647 EngineControl::on_delete_event (GdkEventAny* ev)
1648 {
1649         if (notebook.get_current_page() == 2) {
1650                 /* currently on latency tab - be sure to clean up */
1651                 end_latency_detection ();
1652         }
1653         return ArdourDialog::on_delete_event (ev);
1654 }
1655
1656 void
1657 EngineControl::engine_running ()
1658 {
1659         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1660         assert (backend);
1661
1662         buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
1663         sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
1664
1665         buffer_size_combo.set_sensitive (true);
1666         sample_rate_combo.set_sensitive (true);
1667
1668         connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
1669
1670         started_at_least_once = true;
1671 }
1672
1673 void
1674 EngineControl::engine_stopped ()
1675 {
1676         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1677         assert (backend);
1678
1679         buffer_size_combo.set_sensitive (false);
1680         connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
1681
1682         sample_rate_combo.set_sensitive (true);
1683         buffer_size_combo.set_sensitive (true);
1684 }
1685         
1686 void
1687 EngineControl::connect_disconnect_click() 
1688 {
1689         if (ARDOUR::AudioEngine::instance()->running()) {
1690                 ARDOUR_UI::instance()->disconnect_from_engine ();
1691         } else {
1692                 ARDOUR_UI::instance()->reconnect_to_engine ();
1693         }
1694 }