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