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